Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

800 righe
19 KiB

  1. package redis
  2. import (
  3. "context"
  4. "fmt"
  5. "strings"
  6. "sync"
  7. "time"
  8. "github.com/redis/go-redis/v9/internal"
  9. "github.com/redis/go-redis/v9/internal/pool"
  10. "github.com/redis/go-redis/v9/internal/proto"
  11. "github.com/redis/go-redis/v9/push"
  12. )
  13. // PubSub implements Pub/Sub commands as described in
  14. // http://redis.io/topics/pubsub. Message receiving is NOT safe
  15. // for concurrent use by multiple goroutines.
  16. //
  17. // PubSub automatically reconnects to Redis Server and resubscribes
  18. // to the channels in case of network errors.
  19. type PubSub struct {
  20. opt *Options
  21. newConn func(ctx context.Context, addr string, channels []string) (*pool.Conn, error)
  22. closeConn func(*pool.Conn) error
  23. mu sync.Mutex
  24. cn *pool.Conn
  25. channels map[string]struct{}
  26. patterns map[string]struct{}
  27. schannels map[string]struct{}
  28. closed bool
  29. exit chan struct{}
  30. cmd *Cmd
  31. chOnce sync.Once
  32. msgCh *channel
  33. allCh *channel
  34. // Push notification processor for handling generic push notifications
  35. pushProcessor push.NotificationProcessor
  36. // Cleanup callback for maintenanceNotifications upgrade tracking
  37. onClose func()
  38. }
  39. func (c *PubSub) init() {
  40. c.exit = make(chan struct{})
  41. }
  42. func (c *PubSub) String() string {
  43. c.mu.Lock()
  44. defer c.mu.Unlock()
  45. channels := mapKeys(c.channels)
  46. channels = append(channels, mapKeys(c.patterns)...)
  47. channels = append(channels, mapKeys(c.schannels)...)
  48. return fmt.Sprintf("PubSub(%s)", strings.Join(channels, ", "))
  49. }
  50. func (c *PubSub) connWithLock(ctx context.Context) (*pool.Conn, error) {
  51. c.mu.Lock()
  52. cn, err := c.conn(ctx, nil)
  53. c.mu.Unlock()
  54. return cn, err
  55. }
  56. func (c *PubSub) conn(ctx context.Context, newChannels []string) (*pool.Conn, error) {
  57. if c.closed {
  58. return nil, pool.ErrClosed
  59. }
  60. if c.cn != nil {
  61. return c.cn, nil
  62. }
  63. if c.opt.Addr == "" {
  64. // TODO(maintenanceNotifications):
  65. // this is probably cluster client
  66. // c.newConn will ignore the addr argument
  67. // will be changed when we have maintenanceNotifications upgrades for cluster clients
  68. c.opt.Addr = internal.RedisNull
  69. }
  70. channels := mapKeys(c.channels)
  71. channels = append(channels, newChannels...)
  72. cn, err := c.newConn(ctx, c.opt.Addr, channels)
  73. if err != nil {
  74. return nil, err
  75. }
  76. if err := c.resubscribe(ctx, cn); err != nil {
  77. _ = c.closeConn(cn)
  78. return nil, err
  79. }
  80. c.cn = cn
  81. return cn, nil
  82. }
  83. func (c *PubSub) writeCmd(ctx context.Context, cn *pool.Conn, cmd Cmder) error {
  84. return cn.WithWriter(ctx, c.opt.WriteTimeout, func(wr *proto.Writer) error {
  85. return writeCmd(wr, cmd)
  86. })
  87. }
  88. func (c *PubSub) resubscribe(ctx context.Context, cn *pool.Conn) error {
  89. var firstErr error
  90. if len(c.channels) > 0 {
  91. firstErr = c._subscribe(ctx, cn, "subscribe", mapKeys(c.channels))
  92. }
  93. if len(c.patterns) > 0 {
  94. err := c._subscribe(ctx, cn, "psubscribe", mapKeys(c.patterns))
  95. if err != nil && firstErr == nil {
  96. firstErr = err
  97. }
  98. }
  99. if len(c.schannels) > 0 {
  100. err := c._subscribe(ctx, cn, "ssubscribe", mapKeys(c.schannels))
  101. if err != nil && firstErr == nil {
  102. firstErr = err
  103. }
  104. }
  105. return firstErr
  106. }
  107. func mapKeys(m map[string]struct{}) []string {
  108. s := make([]string, len(m))
  109. i := 0
  110. for k := range m {
  111. s[i] = k
  112. i++
  113. }
  114. return s
  115. }
  116. func (c *PubSub) _subscribe(
  117. ctx context.Context, cn *pool.Conn, redisCmd string, channels []string,
  118. ) error {
  119. args := make([]interface{}, 0, 1+len(channels))
  120. args = append(args, redisCmd)
  121. for _, channel := range channels {
  122. args = append(args, channel)
  123. }
  124. cmd := NewSliceCmd(ctx, args...)
  125. return c.writeCmd(ctx, cn, cmd)
  126. }
  127. func (c *PubSub) releaseConnWithLock(
  128. ctx context.Context,
  129. cn *pool.Conn,
  130. err error,
  131. allowTimeout bool,
  132. ) {
  133. c.mu.Lock()
  134. c.releaseConn(ctx, cn, err, allowTimeout)
  135. c.mu.Unlock()
  136. }
  137. func (c *PubSub) releaseConn(ctx context.Context, cn *pool.Conn, err error, allowTimeout bool) {
  138. if c.cn != cn {
  139. return
  140. }
  141. if !cn.IsUsable() || cn.ShouldHandoff() {
  142. c.reconnect(ctx, fmt.Errorf("pubsub: connection is not usable"))
  143. }
  144. if isBadConn(err, allowTimeout, c.opt.Addr) {
  145. c.reconnect(ctx, err)
  146. }
  147. }
  148. func (c *PubSub) reconnect(ctx context.Context, reason error) {
  149. if c.cn != nil && c.cn.ShouldHandoff() {
  150. newEndpoint := c.cn.GetHandoffEndpoint()
  151. // If new endpoint is NULL, use the original address
  152. if newEndpoint == internal.RedisNull {
  153. newEndpoint = c.opt.Addr
  154. }
  155. if newEndpoint != "" {
  156. // Update the address in the options
  157. oldAddr := c.cn.RemoteAddr().String()
  158. c.opt.Addr = newEndpoint
  159. internal.Logger.Printf(ctx, "pubsub: reconnecting to new endpoint %s (was %s)", newEndpoint, oldAddr)
  160. }
  161. }
  162. _ = c.closeTheCn(reason)
  163. _, _ = c.conn(ctx, nil)
  164. }
  165. func (c *PubSub) closeTheCn(reason error) error {
  166. if c.cn == nil {
  167. return nil
  168. }
  169. err := c.closeConn(c.cn)
  170. c.cn = nil
  171. return err
  172. }
  173. func (c *PubSub) Close() error {
  174. c.mu.Lock()
  175. defer c.mu.Unlock()
  176. if c.closed {
  177. return pool.ErrClosed
  178. }
  179. c.closed = true
  180. close(c.exit)
  181. // Call cleanup callback if set
  182. if c.onClose != nil {
  183. c.onClose()
  184. }
  185. return c.closeTheCn(pool.ErrClosed)
  186. }
  187. // Subscribe the client to the specified channels. It returns
  188. // empty subscription if there are no channels.
  189. func (c *PubSub) Subscribe(ctx context.Context, channels ...string) error {
  190. c.mu.Lock()
  191. defer c.mu.Unlock()
  192. err := c.subscribe(ctx, "subscribe", channels...)
  193. if c.channels == nil {
  194. c.channels = make(map[string]struct{})
  195. }
  196. for _, s := range channels {
  197. c.channels[s] = struct{}{}
  198. }
  199. return err
  200. }
  201. // PSubscribe the client to the given patterns. It returns
  202. // empty subscription if there are no patterns.
  203. func (c *PubSub) PSubscribe(ctx context.Context, patterns ...string) error {
  204. c.mu.Lock()
  205. defer c.mu.Unlock()
  206. err := c.subscribe(ctx, "psubscribe", patterns...)
  207. if c.patterns == nil {
  208. c.patterns = make(map[string]struct{})
  209. }
  210. for _, s := range patterns {
  211. c.patterns[s] = struct{}{}
  212. }
  213. return err
  214. }
  215. // SSubscribe Subscribes the client to the specified shard channels.
  216. func (c *PubSub) SSubscribe(ctx context.Context, channels ...string) error {
  217. c.mu.Lock()
  218. defer c.mu.Unlock()
  219. err := c.subscribe(ctx, "ssubscribe", channels...)
  220. if c.schannels == nil {
  221. c.schannels = make(map[string]struct{})
  222. }
  223. for _, s := range channels {
  224. c.schannels[s] = struct{}{}
  225. }
  226. return err
  227. }
  228. // Unsubscribe the client from the given channels, or from all of
  229. // them if none is given.
  230. func (c *PubSub) Unsubscribe(ctx context.Context, channels ...string) error {
  231. c.mu.Lock()
  232. defer c.mu.Unlock()
  233. if len(channels) > 0 {
  234. for _, channel := range channels {
  235. delete(c.channels, channel)
  236. }
  237. } else {
  238. // Unsubscribe from all channels.
  239. for channel := range c.channels {
  240. delete(c.channels, channel)
  241. }
  242. }
  243. err := c.subscribe(ctx, "unsubscribe", channels...)
  244. return err
  245. }
  246. // PUnsubscribe the client from the given patterns, or from all of
  247. // them if none is given.
  248. func (c *PubSub) PUnsubscribe(ctx context.Context, patterns ...string) error {
  249. c.mu.Lock()
  250. defer c.mu.Unlock()
  251. if len(patterns) > 0 {
  252. for _, pattern := range patterns {
  253. delete(c.patterns, pattern)
  254. }
  255. } else {
  256. // Unsubscribe from all patterns.
  257. for pattern := range c.patterns {
  258. delete(c.patterns, pattern)
  259. }
  260. }
  261. err := c.subscribe(ctx, "punsubscribe", patterns...)
  262. return err
  263. }
  264. // SUnsubscribe unsubscribes the client from the given shard channels,
  265. // or from all of them if none is given.
  266. func (c *PubSub) SUnsubscribe(ctx context.Context, channels ...string) error {
  267. c.mu.Lock()
  268. defer c.mu.Unlock()
  269. if len(channels) > 0 {
  270. for _, channel := range channels {
  271. delete(c.schannels, channel)
  272. }
  273. } else {
  274. // Unsubscribe from all channels.
  275. for channel := range c.schannels {
  276. delete(c.schannels, channel)
  277. }
  278. }
  279. err := c.subscribe(ctx, "sunsubscribe", channels...)
  280. return err
  281. }
  282. func (c *PubSub) subscribe(ctx context.Context, redisCmd string, channels ...string) error {
  283. cn, err := c.conn(ctx, channels)
  284. if err != nil {
  285. return err
  286. }
  287. err = c._subscribe(ctx, cn, redisCmd, channels)
  288. c.releaseConn(ctx, cn, err, false)
  289. return err
  290. }
  291. func (c *PubSub) Ping(ctx context.Context, payload ...string) error {
  292. args := []interface{}{"ping"}
  293. if len(payload) == 1 {
  294. args = append(args, payload[0])
  295. }
  296. cmd := NewCmd(ctx, args...)
  297. c.mu.Lock()
  298. defer c.mu.Unlock()
  299. cn, err := c.conn(ctx, nil)
  300. if err != nil {
  301. return err
  302. }
  303. err = c.writeCmd(ctx, cn, cmd)
  304. c.releaseConn(ctx, cn, err, false)
  305. return err
  306. }
  307. // Subscription received after a successful subscription to channel.
  308. type Subscription struct {
  309. // Can be "subscribe", "unsubscribe", "psubscribe" or "punsubscribe".
  310. Kind string
  311. // Channel name we have subscribed to.
  312. Channel string
  313. // Number of channels we are currently subscribed to.
  314. Count int
  315. }
  316. func (m *Subscription) String() string {
  317. return fmt.Sprintf("%s: %s", m.Kind, m.Channel)
  318. }
  319. // Message received as result of a PUBLISH command issued by another client.
  320. type Message struct {
  321. Channel string
  322. Pattern string
  323. Payload string
  324. PayloadSlice []string
  325. }
  326. func (m *Message) String() string {
  327. return fmt.Sprintf("Message<%s: %s>", m.Channel, m.Payload)
  328. }
  329. // Pong received as result of a PING command issued by another client.
  330. type Pong struct {
  331. Payload string
  332. }
  333. func (p *Pong) String() string {
  334. if p.Payload != "" {
  335. return fmt.Sprintf("Pong<%s>", p.Payload)
  336. }
  337. return "Pong"
  338. }
  339. func (c *PubSub) newMessage(reply interface{}) (interface{}, error) {
  340. switch reply := reply.(type) {
  341. case string:
  342. return &Pong{
  343. Payload: reply,
  344. }, nil
  345. case []interface{}:
  346. switch kind := reply[0].(string); kind {
  347. case "subscribe", "unsubscribe", "psubscribe", "punsubscribe", "ssubscribe", "sunsubscribe":
  348. // Can be nil in case of "unsubscribe".
  349. channel, _ := reply[1].(string)
  350. return &Subscription{
  351. Kind: kind,
  352. Channel: channel,
  353. Count: int(reply[2].(int64)),
  354. }, nil
  355. case "message", "smessage":
  356. switch payload := reply[2].(type) {
  357. case string:
  358. return &Message{
  359. Channel: reply[1].(string),
  360. Payload: payload,
  361. }, nil
  362. case []interface{}:
  363. ss := make([]string, len(payload))
  364. for i, s := range payload {
  365. ss[i] = s.(string)
  366. }
  367. return &Message{
  368. Channel: reply[1].(string),
  369. PayloadSlice: ss,
  370. }, nil
  371. default:
  372. return nil, fmt.Errorf("redis: unsupported pubsub message payload: %T", payload)
  373. }
  374. case "pmessage":
  375. return &Message{
  376. Pattern: reply[1].(string),
  377. Channel: reply[2].(string),
  378. Payload: reply[3].(string),
  379. }, nil
  380. case "pong":
  381. return &Pong{
  382. Payload: reply[1].(string),
  383. }, nil
  384. default:
  385. return nil, fmt.Errorf("redis: unsupported pubsub message: %q", kind)
  386. }
  387. default:
  388. return nil, fmt.Errorf("redis: unsupported pubsub message: %#v", reply)
  389. }
  390. }
  391. // ReceiveTimeout acts like Receive but returns an error if message
  392. // is not received in time. This is low-level API and in most cases
  393. // Channel should be used instead.
  394. func (c *PubSub) ReceiveTimeout(ctx context.Context, timeout time.Duration) (interface{}, error) {
  395. if c.cmd == nil {
  396. c.cmd = NewCmd(ctx)
  397. }
  398. // Don't hold the lock to allow subscriptions and pings.
  399. cn, err := c.connWithLock(ctx)
  400. if err != nil {
  401. return nil, err
  402. }
  403. err = cn.WithReader(ctx, timeout, func(rd *proto.Reader) error {
  404. // To be sure there are no buffered push notifications, we process them before reading the reply
  405. if err := c.processPendingPushNotificationWithReader(ctx, cn, rd); err != nil {
  406. // Log the error but don't fail the command execution
  407. // Push notification processing errors shouldn't break normal Redis operations
  408. internal.Logger.Printf(ctx, "push: conn[%d] error processing pending notifications before reading reply: %v", cn.GetID(), err)
  409. }
  410. return c.cmd.readReply(rd)
  411. })
  412. c.releaseConnWithLock(ctx, cn, err, timeout > 0)
  413. if err != nil {
  414. return nil, err
  415. }
  416. return c.newMessage(c.cmd.Val())
  417. }
  418. // Receive returns a message as a Subscription, Message, Pong or error.
  419. // See PubSub example for details. This is low-level API and in most cases
  420. // Channel should be used instead.
  421. // Receive returns a message as a Subscription, Message, Pong, or an error.
  422. // See PubSub example for details. This is a low-level API and in most cases
  423. // Channel should be used instead.
  424. // This method blocks until a message is received or an error occurs.
  425. // It may return early with an error if the context is canceled, the connection fails,
  426. // or other internal errors occur.
  427. func (c *PubSub) Receive(ctx context.Context) (interface{}, error) {
  428. return c.ReceiveTimeout(ctx, 0)
  429. }
  430. // ReceiveMessage returns a Message or error ignoring Subscription and Pong
  431. // messages. This is low-level API and in most cases Channel should be used
  432. // instead.
  433. func (c *PubSub) ReceiveMessage(ctx context.Context) (*Message, error) {
  434. for {
  435. msg, err := c.Receive(ctx)
  436. if err != nil {
  437. return nil, err
  438. }
  439. switch msg := msg.(type) {
  440. case *Subscription:
  441. // Ignore.
  442. case *Pong:
  443. // Ignore.
  444. case *Message:
  445. return msg, nil
  446. default:
  447. err := fmt.Errorf("redis: unknown message: %T", msg)
  448. return nil, err
  449. }
  450. }
  451. }
  452. func (c *PubSub) getContext() context.Context {
  453. if c.cmd != nil {
  454. return c.cmd.ctx
  455. }
  456. return context.Background()
  457. }
  458. //------------------------------------------------------------------------------
  459. // Channel returns a Go channel for concurrently receiving messages.
  460. // The channel is closed together with the PubSub. If the Go channel
  461. // is blocked full for 1 minute the message is dropped.
  462. // Receive* APIs can not be used after channel is created.
  463. //
  464. // go-redis periodically sends ping messages to test connection health
  465. // and re-subscribes if ping can not received for 1 minute.
  466. func (c *PubSub) Channel(opts ...ChannelOption) <-chan *Message {
  467. c.chOnce.Do(func() {
  468. c.msgCh = newChannel(c, opts...)
  469. c.msgCh.initMsgChan()
  470. })
  471. if c.msgCh == nil {
  472. err := fmt.Errorf("redis: Channel can't be called after ChannelWithSubscriptions")
  473. panic(err)
  474. }
  475. return c.msgCh.msgCh
  476. }
  477. // ChannelSize is like Channel, but creates a Go channel
  478. // with specified buffer size.
  479. //
  480. // Deprecated: use Channel(WithChannelSize(size)), remove in v9.
  481. func (c *PubSub) ChannelSize(size int) <-chan *Message {
  482. return c.Channel(WithChannelSize(size))
  483. }
  484. // ChannelWithSubscriptions is like Channel, but message type can be either
  485. // *Subscription or *Message. Subscription messages can be used to detect
  486. // reconnections.
  487. //
  488. // ChannelWithSubscriptions can not be used together with Channel or ChannelSize.
  489. func (c *PubSub) ChannelWithSubscriptions(opts ...ChannelOption) <-chan interface{} {
  490. c.chOnce.Do(func() {
  491. c.allCh = newChannel(c, opts...)
  492. c.allCh.initAllChan()
  493. })
  494. if c.allCh == nil {
  495. err := fmt.Errorf("redis: ChannelWithSubscriptions can't be called after Channel")
  496. panic(err)
  497. }
  498. return c.allCh.allCh
  499. }
  500. func (c *PubSub) processPendingPushNotificationWithReader(ctx context.Context, cn *pool.Conn, rd *proto.Reader) error {
  501. // Only process push notifications for RESP3 connections with a processor
  502. if c.opt.Protocol != 3 || c.pushProcessor == nil {
  503. return nil
  504. }
  505. // Create handler context with client, connection pool, and connection information
  506. handlerCtx := c.pushNotificationHandlerContext(cn)
  507. return c.pushProcessor.ProcessPendingNotifications(ctx, handlerCtx, rd)
  508. }
  509. func (c *PubSub) pushNotificationHandlerContext(cn *pool.Conn) push.NotificationHandlerContext {
  510. // PubSub doesn't have a client or connection pool, so we pass nil for those
  511. // PubSub connections are blocking
  512. return push.NotificationHandlerContext{
  513. PubSub: c,
  514. Conn: cn,
  515. IsBlocking: true,
  516. }
  517. }
  518. type ChannelOption func(c *channel)
  519. // WithChannelSize specifies the Go chan size that is used to buffer incoming messages.
  520. //
  521. // The default is 100 messages.
  522. func WithChannelSize(size int) ChannelOption {
  523. return func(c *channel) {
  524. c.chanSize = size
  525. }
  526. }
  527. // WithChannelHealthCheckInterval specifies the health check interval.
  528. // PubSub will ping Redis Server if it does not receive any messages within the interval.
  529. // To disable health check, use zero interval.
  530. //
  531. // The default is 3 seconds.
  532. func WithChannelHealthCheckInterval(d time.Duration) ChannelOption {
  533. return func(c *channel) {
  534. c.checkInterval = d
  535. }
  536. }
  537. // WithChannelSendTimeout specifies the channel send timeout after which
  538. // the message is dropped.
  539. //
  540. // The default is 60 seconds.
  541. func WithChannelSendTimeout(d time.Duration) ChannelOption {
  542. return func(c *channel) {
  543. c.chanSendTimeout = d
  544. }
  545. }
  546. type channel struct {
  547. pubSub *PubSub
  548. msgCh chan *Message
  549. allCh chan interface{}
  550. ping chan struct{}
  551. chanSize int
  552. chanSendTimeout time.Duration
  553. checkInterval time.Duration
  554. }
  555. func newChannel(pubSub *PubSub, opts ...ChannelOption) *channel {
  556. c := &channel{
  557. pubSub: pubSub,
  558. chanSize: 100,
  559. chanSendTimeout: time.Minute,
  560. checkInterval: 3 * time.Second,
  561. }
  562. for _, opt := range opts {
  563. opt(c)
  564. }
  565. if c.checkInterval > 0 {
  566. c.initHealthCheck()
  567. }
  568. return c
  569. }
  570. func (c *channel) initHealthCheck() {
  571. ctx := context.TODO()
  572. c.ping = make(chan struct{}, 1)
  573. go func() {
  574. timer := time.NewTimer(time.Minute)
  575. timer.Stop()
  576. for {
  577. timer.Reset(c.checkInterval)
  578. select {
  579. case <-c.ping:
  580. if !timer.Stop() {
  581. <-timer.C
  582. }
  583. case <-timer.C:
  584. if pingErr := c.pubSub.Ping(ctx); pingErr != nil {
  585. c.pubSub.mu.Lock()
  586. c.pubSub.reconnect(ctx, pingErr)
  587. c.pubSub.mu.Unlock()
  588. }
  589. case <-c.pubSub.exit:
  590. return
  591. }
  592. }
  593. }()
  594. }
  595. // initMsgChan must be in sync with initAllChan.
  596. func (c *channel) initMsgChan() {
  597. ctx := context.TODO()
  598. c.msgCh = make(chan *Message, c.chanSize)
  599. go func() {
  600. timer := time.NewTimer(time.Minute)
  601. timer.Stop()
  602. var errCount int
  603. for {
  604. msg, err := c.pubSub.Receive(ctx)
  605. if err != nil {
  606. if err == pool.ErrClosed {
  607. close(c.msgCh)
  608. return
  609. }
  610. if errCount > 0 {
  611. time.Sleep(100 * time.Millisecond)
  612. }
  613. errCount++
  614. continue
  615. }
  616. errCount = 0
  617. // Any message is as good as a ping.
  618. select {
  619. case c.ping <- struct{}{}:
  620. default:
  621. }
  622. switch msg := msg.(type) {
  623. case *Subscription:
  624. // Ignore.
  625. case *Pong:
  626. // Ignore.
  627. case *Message:
  628. timer.Reset(c.chanSendTimeout)
  629. select {
  630. case c.msgCh <- msg:
  631. if !timer.Stop() {
  632. <-timer.C
  633. }
  634. case <-timer.C:
  635. internal.Logger.Printf(
  636. ctx, "redis: %s channel is full for %s (message is dropped)",
  637. c, c.chanSendTimeout)
  638. }
  639. default:
  640. internal.Logger.Printf(ctx, "redis: unknown message type: %T", msg)
  641. }
  642. }
  643. }()
  644. }
  645. // initAllChan must be in sync with initMsgChan.
  646. func (c *channel) initAllChan() {
  647. ctx := context.TODO()
  648. c.allCh = make(chan interface{}, c.chanSize)
  649. go func() {
  650. timer := time.NewTimer(time.Minute)
  651. timer.Stop()
  652. var errCount int
  653. for {
  654. msg, err := c.pubSub.Receive(ctx)
  655. if err != nil {
  656. if err == pool.ErrClosed {
  657. close(c.allCh)
  658. return
  659. }
  660. if errCount > 0 {
  661. time.Sleep(100 * time.Millisecond)
  662. }
  663. errCount++
  664. continue
  665. }
  666. errCount = 0
  667. // Any message is as good as a ping.
  668. select {
  669. case c.ping <- struct{}{}:
  670. default:
  671. }
  672. switch msg := msg.(type) {
  673. case *Pong:
  674. // Ignore.
  675. case *Subscription, *Message:
  676. timer.Reset(c.chanSendTimeout)
  677. select {
  678. case c.allCh <- msg:
  679. if !timer.Stop() {
  680. <-timer.C
  681. }
  682. case <-timer.C:
  683. internal.Logger.Printf(
  684. ctx, "redis: %s channel is full for %s (message is dropped)",
  685. c, c.chanSendTimeout)
  686. }
  687. default:
  688. internal.Logger.Printf(ctx, "redis: unknown message type: %T", msg)
  689. }
  690. }
  691. }()
  692. }