| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446 |
- package redis_test
- import (
- "io"
- "net"
- "sync"
- "time"
- "github.com/go-redis/redis/v7"
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
- )
- var _ = Describe("PubSub", func() {
- var client *redis.Client
- BeforeEach(func() {
- opt := redisOptions()
- opt.MinIdleConns = 0
- opt.MaxConnAge = 0
- client = redis.NewClient(opt)
- Expect(client.FlushDB().Err()).NotTo(HaveOccurred())
- })
- AfterEach(func() {
- Expect(client.Close()).NotTo(HaveOccurred())
- })
- It("implements Stringer", func() {
- pubsub := client.PSubscribe("mychannel*")
- defer pubsub.Close()
- Expect(pubsub.String()).To(Equal("PubSub(mychannel*)"))
- })
- It("should support pattern matching", func() {
- pubsub := client.PSubscribe("mychannel*")
- defer pubsub.Close()
- {
- msgi, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- subscr := msgi.(*redis.Subscription)
- Expect(subscr.Kind).To(Equal("psubscribe"))
- Expect(subscr.Channel).To(Equal("mychannel*"))
- Expect(subscr.Count).To(Equal(1))
- }
- {
- msgi, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err.(net.Error).Timeout()).To(Equal(true))
- Expect(msgi).To(BeNil())
- }
- n, err := client.Publish("mychannel1", "hello").Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(n).To(Equal(int64(1)))
- Expect(pubsub.PUnsubscribe("mychannel*")).NotTo(HaveOccurred())
- {
- msgi, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- subscr := msgi.(*redis.Message)
- Expect(subscr.Channel).To(Equal("mychannel1"))
- Expect(subscr.Pattern).To(Equal("mychannel*"))
- Expect(subscr.Payload).To(Equal("hello"))
- }
- {
- msgi, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- subscr := msgi.(*redis.Subscription)
- Expect(subscr.Kind).To(Equal("punsubscribe"))
- Expect(subscr.Channel).To(Equal("mychannel*"))
- Expect(subscr.Count).To(Equal(0))
- }
- stats := client.PoolStats()
- Expect(stats.Misses).To(Equal(uint32(1)))
- })
- It("should pub/sub channels", func() {
- channels, err := client.PubSubChannels("mychannel*").Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(channels).To(BeEmpty())
- pubsub := client.Subscribe("mychannel", "mychannel2")
- defer pubsub.Close()
- channels, err = client.PubSubChannels("mychannel*").Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(channels).To(ConsistOf([]string{"mychannel", "mychannel2"}))
- channels, err = client.PubSubChannels("").Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(channels).To(BeEmpty())
- channels, err = client.PubSubChannels("*").Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(len(channels)).To(BeNumerically(">=", 2))
- })
- It("should return the numbers of subscribers", func() {
- pubsub := client.Subscribe("mychannel", "mychannel2")
- defer pubsub.Close()
- channels, err := client.PubSubNumSub("mychannel", "mychannel2", "mychannel3").Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(channels).To(Equal(map[string]int64{
- "mychannel": 1,
- "mychannel2": 1,
- "mychannel3": 0,
- }))
- })
- It("should return the numbers of subscribers by pattern", func() {
- num, err := client.PubSubNumPat().Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(num).To(Equal(int64(0)))
- pubsub := client.PSubscribe("*")
- defer pubsub.Close()
- num, err = client.PubSubNumPat().Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(num).To(Equal(int64(1)))
- })
- It("should pub/sub", func() {
- pubsub := client.Subscribe("mychannel", "mychannel2")
- defer pubsub.Close()
- {
- msgi, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- subscr := msgi.(*redis.Subscription)
- Expect(subscr.Kind).To(Equal("subscribe"))
- Expect(subscr.Channel).To(Equal("mychannel"))
- Expect(subscr.Count).To(Equal(1))
- }
- {
- msgi, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- subscr := msgi.(*redis.Subscription)
- Expect(subscr.Kind).To(Equal("subscribe"))
- Expect(subscr.Channel).To(Equal("mychannel2"))
- Expect(subscr.Count).To(Equal(2))
- }
- {
- msgi, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err.(net.Error).Timeout()).To(Equal(true))
- Expect(msgi).NotTo(HaveOccurred())
- }
- n, err := client.Publish("mychannel", "hello").Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(n).To(Equal(int64(1)))
- n, err = client.Publish("mychannel2", "hello2").Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(n).To(Equal(int64(1)))
- Expect(pubsub.Unsubscribe("mychannel", "mychannel2")).NotTo(HaveOccurred())
- {
- msgi, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- msg := msgi.(*redis.Message)
- Expect(msg.Channel).To(Equal("mychannel"))
- Expect(msg.Payload).To(Equal("hello"))
- }
- {
- msgi, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- msg := msgi.(*redis.Message)
- Expect(msg.Channel).To(Equal("mychannel2"))
- Expect(msg.Payload).To(Equal("hello2"))
- }
- {
- msgi, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- subscr := msgi.(*redis.Subscription)
- Expect(subscr.Kind).To(Equal("unsubscribe"))
- Expect(subscr.Channel).To(Equal("mychannel"))
- Expect(subscr.Count).To(Equal(1))
- }
- {
- msgi, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- subscr := msgi.(*redis.Subscription)
- Expect(subscr.Kind).To(Equal("unsubscribe"))
- Expect(subscr.Channel).To(Equal("mychannel2"))
- Expect(subscr.Count).To(Equal(0))
- }
- stats := client.PoolStats()
- Expect(stats.Misses).To(Equal(uint32(1)))
- })
- It("should ping/pong", func() {
- pubsub := client.Subscribe("mychannel")
- defer pubsub.Close()
- _, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- err = pubsub.Ping("")
- Expect(err).NotTo(HaveOccurred())
- msgi, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- pong := msgi.(*redis.Pong)
- Expect(pong.Payload).To(Equal(""))
- })
- It("should ping/pong with payload", func() {
- pubsub := client.Subscribe("mychannel")
- defer pubsub.Close()
- _, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- err = pubsub.Ping("hello")
- Expect(err).NotTo(HaveOccurred())
- msgi, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- pong := msgi.(*redis.Pong)
- Expect(pong.Payload).To(Equal("hello"))
- })
- It("should multi-ReceiveMessage", func() {
- pubsub := client.Subscribe("mychannel")
- defer pubsub.Close()
- subscr, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- Expect(subscr).To(Equal(&redis.Subscription{
- Kind: "subscribe",
- Channel: "mychannel",
- Count: 1,
- }))
- err = client.Publish("mychannel", "hello").Err()
- Expect(err).NotTo(HaveOccurred())
- err = client.Publish("mychannel", "world").Err()
- Expect(err).NotTo(HaveOccurred())
- msg, err := pubsub.ReceiveMessage()
- Expect(err).NotTo(HaveOccurred())
- Expect(msg.Channel).To(Equal("mychannel"))
- Expect(msg.Payload).To(Equal("hello"))
- msg, err = pubsub.ReceiveMessage()
- Expect(err).NotTo(HaveOccurred())
- Expect(msg.Channel).To(Equal("mychannel"))
- Expect(msg.Payload).To(Equal("world"))
- })
- It("returns an error when subscribe fails", func() {
- pubsub := client.Subscribe()
- defer pubsub.Close()
- pubsub.SetNetConn(&badConn{
- readErr: io.EOF,
- writeErr: io.EOF,
- })
- err := pubsub.Subscribe("mychannel")
- Expect(err).To(MatchError("EOF"))
- err = pubsub.Subscribe("mychannel")
- Expect(err).NotTo(HaveOccurred())
- })
- expectReceiveMessageOnError := func(pubsub *redis.PubSub) {
- pubsub.SetNetConn(&badConn{
- readErr: io.EOF,
- writeErr: io.EOF,
- })
- step := make(chan struct{}, 3)
- go func() {
- defer GinkgoRecover()
- Eventually(step).Should(Receive())
- err := client.Publish("mychannel", "hello").Err()
- Expect(err).NotTo(HaveOccurred())
- step <- struct{}{}
- }()
- _, err := pubsub.ReceiveMessage()
- Expect(err).To(Equal(io.EOF))
- step <- struct{}{}
- msg, err := pubsub.ReceiveMessage()
- Expect(err).NotTo(HaveOccurred())
- Expect(msg.Channel).To(Equal("mychannel"))
- Expect(msg.Payload).To(Equal("hello"))
- Eventually(step).Should(Receive())
- }
- It("Subscribe should reconnect on ReceiveMessage error", func() {
- pubsub := client.Subscribe("mychannel")
- defer pubsub.Close()
- subscr, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- Expect(subscr).To(Equal(&redis.Subscription{
- Kind: "subscribe",
- Channel: "mychannel",
- Count: 1,
- }))
- expectReceiveMessageOnError(pubsub)
- })
- It("PSubscribe should reconnect on ReceiveMessage error", func() {
- pubsub := client.PSubscribe("mychannel")
- defer pubsub.Close()
- subscr, err := pubsub.ReceiveTimeout(time.Second)
- Expect(err).NotTo(HaveOccurred())
- Expect(subscr).To(Equal(&redis.Subscription{
- Kind: "psubscribe",
- Channel: "mychannel",
- Count: 1,
- }))
- expectReceiveMessageOnError(pubsub)
- })
- It("should return on Close", func() {
- pubsub := client.Subscribe("mychannel")
- defer pubsub.Close()
- var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- defer GinkgoRecover()
- wg.Done()
- defer wg.Done()
- _, err := pubsub.ReceiveMessage()
- Expect(err).To(HaveOccurred())
- Expect(err.Error()).To(SatisfyAny(
- Equal("redis: client is closed"),
- ContainSubstring("use of closed network connection"),
- ))
- }()
- wg.Wait()
- wg.Add(1)
- Expect(pubsub.Close()).NotTo(HaveOccurred())
- wg.Wait()
- })
- It("should ReceiveMessage without a subscription", func() {
- timeout := 100 * time.Millisecond
- pubsub := client.Subscribe()
- defer pubsub.Close()
- var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- defer GinkgoRecover()
- defer wg.Done()
- time.Sleep(timeout)
- err := pubsub.Subscribe("mychannel")
- Expect(err).NotTo(HaveOccurred())
- time.Sleep(timeout)
- err = client.Publish("mychannel", "hello").Err()
- Expect(err).NotTo(HaveOccurred())
- }()
- msg, err := pubsub.ReceiveMessage()
- Expect(err).NotTo(HaveOccurred())
- Expect(msg.Channel).To(Equal("mychannel"))
- Expect(msg.Payload).To(Equal("hello"))
- wg.Wait()
- })
- It("handles big message payload", func() {
- pubsub := client.Subscribe("mychannel")
- defer pubsub.Close()
- ch := pubsub.Channel()
- bigVal := bigVal()
- err := client.Publish("mychannel", bigVal).Err()
- Expect(err).NotTo(HaveOccurred())
- var msg *redis.Message
- Eventually(ch).Should(Receive(&msg))
- Expect(msg.Channel).To(Equal("mychannel"))
- Expect(msg.Payload).To(Equal(string(bigVal)))
- })
- It("supports concurrent Ping and Receive", func() {
- const N = 100
- pubsub := client.Subscribe("mychannel")
- defer pubsub.Close()
- done := make(chan struct{})
- go func() {
- defer GinkgoRecover()
- for i := 0; i < N; i++ {
- _, err := pubsub.ReceiveTimeout(5 * time.Second)
- Expect(err).NotTo(HaveOccurred())
- }
- close(done)
- }()
- for i := 0; i < N; i++ {
- err := pubsub.Ping()
- Expect(err).NotTo(HaveOccurred())
- }
- select {
- case <-done:
- case <-time.After(30 * time.Second):
- Fail("timeout")
- }
- })
- })
|