Skip to content

Commit cb1968c

Browse files
DengY11DengY11ndyakov
authored
feat(ring): add GetShardClients and GetShardClientForKey methods to Ring for shard access (#3388)
* feat: expose shard information in redis.Ring - Add GetShards() method to retrieve a list of active shard clients. - Add GetShardByKey(key string) method to get the shard client for a specific key. - These methods enable users to manage Pub/Sub operations more effectively by accessing shard-specific clients. * rename GetShardClients and GetShardClientForKey --------- Co-authored-by: DengY11 <212294929@qq.com> Co-authored-by: Nedyalko Dyakov <1547186+ndyakov@users.noreply.github.com>
1 parent 86d418f commit cb1968c

File tree

2 files changed

+102
-0
lines changed

2 files changed

+102
-0
lines changed

ring.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -847,3 +847,26 @@ func (c *Ring) Close() error {
847847

848848
return c.sharding.Close()
849849
}
850+
851+
// GetShardClients returns a list of all shard clients in the ring.
852+
// This can be used to create dedicated connections (e.g., PubSub) for each shard.
853+
func (c *Ring) GetShardClients() []*Client {
854+
shards := c.sharding.List()
855+
clients := make([]*Client, 0, len(shards))
856+
for _, shard := range shards {
857+
if shard.IsUp() {
858+
clients = append(clients, shard.Client)
859+
}
860+
}
861+
return clients
862+
}
863+
864+
// GetShardClientForKey returns the shard client that would handle the given key.
865+
// This can be used to determine which shard a particular key/channel would be routed to.
866+
func (c *Ring) GetShardClientForKey(key string) (*Client, error) {
867+
shard, err := c.sharding.GetByKey(key)
868+
if err != nil {
869+
return nil, err
870+
}
871+
return shard.Client, nil
872+
}

ring_test.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,3 +782,82 @@ var _ = Describe("Ring Tx timeout", func() {
782782
testTimeout()
783783
})
784784
})
785+
786+
var _ = Describe("Ring GetShardClients and GetShardClientForKey", func() {
787+
var ring *redis.Ring
788+
789+
BeforeEach(func() {
790+
ring = redis.NewRing(&redis.RingOptions{
791+
Addrs: map[string]string{
792+
"shard1": ":6379",
793+
"shard2": ":6380",
794+
},
795+
})
796+
})
797+
798+
AfterEach(func() {
799+
Expect(ring.Close()).NotTo(HaveOccurred())
800+
})
801+
802+
It("GetShardClients returns active shard clients", func() {
803+
shards := ring.GetShardClients()
804+
// Note: This test will pass even if Redis servers are not running,
805+
// because GetShardClients only returns clients that are marked as "up",
806+
// and newly created shards start as "up" until the first health check fails.
807+
808+
if len(shards) == 0 {
809+
// Expected if Redis servers are not running
810+
Skip("No active shards found (Redis servers not running)")
811+
} else {
812+
Expect(len(shards)).To(BeNumerically(">", 0))
813+
for _, client := range shards {
814+
Expect(client).NotTo(BeNil())
815+
}
816+
}
817+
})
818+
819+
It("GetShardClientForKey returns correct shard for keys", func() {
820+
testKeys := []string{"key1", "key2", "user:123", "channel:test"}
821+
822+
for _, key := range testKeys {
823+
client, err := ring.GetShardClientForKey(key)
824+
Expect(err).NotTo(HaveOccurred())
825+
Expect(client).NotTo(BeNil())
826+
}
827+
})
828+
829+
It("GetShardClientForKey is consistent for same key", func() {
830+
key := "test:consistency"
831+
832+
// Call GetShardClientForKey multiple times with the same key
833+
// Should always return the same shard
834+
var firstClient *redis.Client
835+
for i := 0; i < 5; i++ {
836+
client, err := ring.GetShardClientForKey(key)
837+
Expect(err).NotTo(HaveOccurred())
838+
Expect(client).NotTo(BeNil())
839+
840+
if i == 0 {
841+
firstClient = client
842+
} else {
843+
Expect(client.String()).To(Equal(firstClient.String()))
844+
}
845+
}
846+
})
847+
848+
It("GetShardClientForKey distributes keys across shards", func() {
849+
testKeys := []string{"key1", "key2", "key3", "key4", "key5"}
850+
shardMap := make(map[string]int)
851+
852+
for _, key := range testKeys {
853+
client, err := ring.GetShardClientForKey(key)
854+
Expect(err).NotTo(HaveOccurred())
855+
shardMap[client.String()]++
856+
}
857+
858+
// Should have at least 1 shard (could be all keys go to same shard due to hashing)
859+
Expect(len(shardMap)).To(BeNumerically(">=", 1))
860+
// But with multiple keys, we expect some distribution
861+
Expect(len(shardMap)).To(BeNumerically("<=", 2)) // At most 2 shards (our setup)
862+
})
863+
})

0 commit comments

Comments
 (0)