1
0
mirror of https://github.com/redis/go-redis.git synced 2025-07-31 05:04:23 +03:00

Set correct cluster slot for scan commands, similarly to Java's Jedis client (#2623)

- At present, the `scan` command is dispatched to a random slot.
- As far as I can tell, the scanX family of commands are not cluster aware (e.g. don't redirect the client to the correct slot).
- You can see [here](869dc0bb66/src/main/java/redis/clients/jedis/ShardedCommandObjects.java (L101)), the Jedis client calling `processKey` on the match argument, and this is what this PR also does.

We've had this patch running in production, and it seems to work well for us.

For further thought:
- Continuing looking at other Redis clients (e.g. Jedis), they outright [reject as invalid](869dc0bb66/src/main/java/redis/clients/jedis/ShardedCommandObjects.java (L98)) any scan command that does not include a hash-tag. Presumably this has the advantage of users not being surprised when their scan produces no results when a random server is picked.
- Perhaps it would be sensible for go-redis to do the same also?

Co-authored-by: Nedyalko Dyakov <1547186+ndyakov@users.noreply.github.com>
This commit is contained in:
Pete Woods
2025-06-24 11:43:03 +01:00
committed by GitHub
parent 0383d08a35
commit 4ac591c7c4
6 changed files with 66 additions and 1 deletions

View File

@ -56,6 +56,18 @@ func Key(key string) string {
return key
}
func Present(key string) bool {
if key == "" {
return false
}
if s := strings.IndexByte(key, '{'); s > -1 {
if e := strings.IndexByte(key[s+1:], '}'); e > 0 {
return true
}
}
return false
}
func RandomSlot() int {
return rand.Intn(slotNumber)
}

View File

@ -69,3 +69,28 @@ var _ = Describe("HashSlot", func() {
}
})
})
var _ = Describe("Present", func() {
It("should calculate hash slots", func() {
tests := []struct {
key string
present bool
}{
{"123456789", false},
{"{}foo", false},
{"foo{}", false},
{"foo{}{bar}", false},
{"", false},
{string([]byte{83, 153, 134, 118, 229, 214, 244, 75, 140, 37, 215, 215}), false},
{"foo{bar}", true},
{"{foo}bar", true},
{"{user1000}.following", true},
{"foo{{bar}}zap", true},
{"foo{bar}{zap}", true},
}
for _, test := range tests {
Expect(Present(test.key)).To(Equal(test.present), "for %s", test.key)
}
})
})