mirror of
https://github.com/redis/go-redis.git
synced 2026-10-24 07:59:20 +03:00
* fix(pubsub): avoid panics on Channel conflict and Ring empty subscribe * fix(pubsub): avoid data race on Channel conflict warn Conflict-path logging used getContext(), which reads c.cmd while the other Channel Receive loop may write it — fails CI under -race. Closes nothing; Refs #3761 * fix(pubsub): prioritize closed state over stickyErr on conn retrieval * fix(pubsub): override stickyErr on Close for channel exit Match SingleConnPool.Close by setting stickyErr to ErrClosed so failed Ring PubSub Channel receivers always observe the closed exit signal. Add .gitattributes to keep Go sources on LF. * fix(pubsub): address review feedback on sticky error channel handling and docs - Remove dead stickyErr write in Close() - Short-circuit Channel() and ChannelWithSubscriptions() when stickyErr is set to return an already-closed channel and log a warning - Add warning log in failedPubSub for visibility - Update doc comments for Subscribe/PSubscribe/SSubscribe and Channel methods - Remove repo-wide .gitattributes Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com> --------- Signed-off-by: Vedant Madane <6527493+VedantMadane@users.noreply.github.com>
68 lines
2.0 KiB
Go
68 lines
2.0 KiB
Go
package redis_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
func TestRingSubscribeEmptyChannelsNoPanic(t *testing.T) {
|
|
ring := redis.NewRing(&redis.RingOptions{
|
|
Addrs: map[string]string{"shard1": "localhost:6379"},
|
|
})
|
|
defer ring.Close()
|
|
|
|
pubsub := ring.Subscribe(context.Background())
|
|
if pubsub == nil {
|
|
t.Fatal("expected non-nil PubSub")
|
|
}
|
|
// Receive should surface sticky error, not panic.
|
|
_, err := pubsub.Receive(context.Background())
|
|
if err == nil {
|
|
t.Fatal("expected sticky error from empty Subscribe")
|
|
}
|
|
if err := pubsub.Close(); err != nil {
|
|
t.Fatalf("Close: %v", err)
|
|
}
|
|
// After Close, sticky construction error is overridden by ErrClosed so
|
|
// Channel/initMsgChan can terminate (pool.ErrClosed is the exit signal).
|
|
_, err = pubsub.Receive(context.Background())
|
|
if err != redis.ErrClosed {
|
|
t.Fatalf("after Close want redis.ErrClosed, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestPubSubChannelMutualExclusionNoPanic(t *testing.T) {
|
|
// Construct via a client that may not be reachable — Subscribe without
|
|
// channels just builds a PubSub handle.
|
|
t.Run("ChannelAfterChannelWithSubscriptions", func(t *testing.T) {
|
|
client := redis.NewClient(&redis.Options{Addr: "127.0.0.1:1"})
|
|
defer client.Close()
|
|
pubsub := client.Subscribe(context.Background())
|
|
defer pubsub.Close()
|
|
|
|
_ = pubsub.ChannelWithSubscriptions()
|
|
ch := pubsub.Channel()
|
|
// Conflicting call returns an already-closed channel (no panic).
|
|
msg, ok := <-ch
|
|
if ok || msg != nil {
|
|
t.Fatalf("expected closed channel from conflicting Channel() call, got msg=%v ok=%v", msg, ok)
|
|
}
|
|
})
|
|
|
|
t.Run("ChannelWithSubscriptionsAfterChannel", func(t *testing.T) {
|
|
client := redis.NewClient(&redis.Options{Addr: "127.0.0.1:1"})
|
|
defer client.Close()
|
|
pubsub := client.Subscribe(context.Background())
|
|
defer pubsub.Close()
|
|
|
|
_ = pubsub.Channel()
|
|
ch := pubsub.ChannelWithSubscriptions()
|
|
msg, ok := <-ch
|
|
if ok || msg != nil {
|
|
t.Fatalf("expected closed channel from conflicting ChannelWithSubscriptions() call, got msg=%v ok=%v", msg, ok)
|
|
}
|
|
})
|
|
}
|