From 3de5605ab2401c963647b63935049b1631121709 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Thu, 28 Dec 2017 16:41:05 +0200 Subject: [PATCH] Pick random node when command does not have keys --- cluster.go | 14 ++++++++++---- cluster_test.go | 26 ++++++++++++++++++++++++++ command.go | 4 ++-- internal/hashtag/hashtag.go | 8 ++++++-- ring.go | 6 +++++- 5 files changed, 49 insertions(+), 9 deletions(-) diff --git a/cluster.go b/cluster.go index c81fc1d..afaf3f3 100644 --- a/cluster.go +++ b/cluster.go @@ -533,16 +533,22 @@ func (c *ClusterClient) cmdInfo(name string) *CommandInfo { return info } +func cmdSlot(cmd Cmder, pos int) int { + if pos == 0 { + return hashtag.RandomSlot() + } + firstKey := cmd.stringArg(pos) + return hashtag.Slot(firstKey) +} + func (c *ClusterClient) cmdSlot(cmd Cmder) int { cmdInfo := c.cmdInfo(cmd.Name()) - firstKey := cmd.stringArg(cmdFirstKeyPos(cmd, cmdInfo)) - return hashtag.Slot(firstKey) + return cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo)) } func (c *ClusterClient) cmdSlotAndNode(state *clusterState, cmd Cmder) (int, *clusterNode, error) { cmdInfo := c.cmdInfo(cmd.Name()) - firstKey := cmd.stringArg(cmdFirstKeyPos(cmd, cmdInfo)) - slot := hashtag.Slot(firstKey) + slot := cmdSlot(cmd, cmdFirstKeyPos(cmd, cmdInfo)) if cmdInfo != nil && cmdInfo.ReadOnly && c.opt.ReadOnly { if c.opt.RouteByLatency { diff --git a/cluster_test.go b/cluster_test.go index 6f3677b..43f3261 100644 --- a/cluster_test.go +++ b/cluster_test.go @@ -536,6 +536,32 @@ var _ = Describe("ClusterClient", func() { Expect(nodesList).Should(HaveLen(1)) }) + It("should RANDOMKEY", func() { + const nkeys = 100 + + for i := 0; i < nkeys; i++ { + err := client.Set(fmt.Sprintf("key%d", i), "value", 0).Err() + Expect(err).NotTo(HaveOccurred()) + } + + var keys []string + addKey := func(key string) { + for _, k := range keys { + if k == key { + return + } + } + keys = append(keys, key) + } + + for i := 0; i < nkeys*10; i++ { + key := client.RandomKey().Val() + addKey(key) + } + + Expect(len(keys)).To(BeNumerically("~", nkeys, nkeys/10)) + }) + assertClusterClient() }) diff --git a/command.go b/command.go index 601a288..598ed98 100644 --- a/command.go +++ b/command.go @@ -82,13 +82,13 @@ func cmdFirstKeyPos(cmd Cmder, info *CommandInfo) int { if cmd.stringArg(2) != "0" { return 3 } else { - return -1 + return 0 } case "publish": return 1 } if info == nil { - return -1 + return 0 } return int(info.FirstKeyPos) } diff --git a/internal/hashtag/hashtag.go b/internal/hashtag/hashtag.go index 2866488..8c7ebbf 100644 --- a/internal/hashtag/hashtag.go +++ b/internal/hashtag/hashtag.go @@ -55,13 +55,17 @@ func Key(key string) string { return key } +func RandomSlot() int { + return rand.Intn(SlotNumber) +} + // hashSlot returns a consistent slot number between 0 and 16383 // for any given string key. func Slot(key string) int { - key = Key(key) if key == "" { - return rand.Intn(SlotNumber) + return RandomSlot() } + key = Key(key) return int(crc16sum(key)) % SlotNumber } diff --git a/ring.go b/ring.go index a30c321..0697b3e 100644 --- a/ring.go +++ b/ring.go @@ -343,7 +343,11 @@ func (c *Ring) shardByName(name string) (*ringShard, error) { func (c *Ring) cmdShard(cmd Cmder) (*ringShard, error) { cmdInfo := c.cmdInfo(cmd.Name()) - firstKey := cmd.stringArg(cmdFirstKeyPos(cmd, cmdInfo)) + pos := cmdFirstKeyPos(cmd, cmdInfo) + if pos == 0 { + return c.randomShard() + } + firstKey := cmd.stringArg(pos) return c.shardByKey(firstKey) }