func (c *ClusterClient) process(cmd Cmder) { var ask bool slot := hashtag.Slot(cmd.clusterKey()) addr := c.slotMasterAddr(slot) client, err := c.getClient(addr) if err != nil { cmd.setErr(err) return } for attempt := 0; attempt <= c.opt.getMaxRedirects(); attempt++ { if attempt > 0 { cmd.reset() } if ask { pipe := client.Pipeline() pipe.Process(NewCmd("ASKING")) pipe.Process(cmd) _, _ = pipe.Exec() pipe.Close() ask = false } else { client.Process(cmd) } // If there is no (real) error, we are done! err := cmd.Err() if err == nil || err == Nil || err == TxFailedErr { return } // On network errors try random node. if isNetworkError(err) { client, err = c.randomClient() if err != nil { return } continue } var moved bool var addr string moved, ask, addr = isMovedError(err) if moved || ask { if moved && c.slotMasterAddr(slot) != addr { c.lazyReloadSlots() } client, err = c.getClient(addr) if err != nil { return } continue } break } }
// Watch creates new transaction and marks the keys to be watched // for conditional execution of a transaction. func (c *ClusterClient) Watch(keys ...string) (*Multi, error) { addr := c.slotMasterAddr(hashtag.Slot(keys[0])) client, err := c.getClient(addr) if err != nil { return nil, err } return client.Watch(keys...) }
func (c *ClusterClient) getClientForCmd(cmd Cmder) (*Client, int, error) { slot := hashtag.Slot(cmd.clusterKey()) addr := c.slotMasterAddr(slot) client, err := c.getClient(addr) if err != nil { return nil, 0, err } return client, slot, nil }
func (pipe *ClusterPipeline) Exec() (cmds []Cmder, retErr error) { if pipe.closed { return nil, pool.ErrClosed } if len(pipe.cmds) == 0 { return []Cmder{}, nil } cmds = pipe.cmds pipe.cmds = make([]Cmder, 0, 10) cmdsMap := make(map[string][]Cmder) for _, cmd := range cmds { slot := hashtag.Slot(cmd.clusterKey()) addr := pipe.cluster.slotMasterAddr(slot) cmdsMap[addr] = append(cmdsMap[addr], cmd) } for attempt := 0; attempt <= pipe.cluster.opt.getMaxRedirects(); attempt++ { failedCmds := make(map[string][]Cmder) for addr, cmds := range cmdsMap { client, err := pipe.cluster.getClient(addr) if err != nil { setCmdsErr(cmds, err) retErr = err continue } cn, err := client.conn() if err != nil { setCmdsErr(cmds, err) retErr = err continue } failedCmds, err = pipe.execClusterCmds(cn, cmds, failedCmds) if err != nil { retErr = err } client.putConn(cn, err, false) } cmdsMap = failedCmds } return cmds, retErr }
key string slot int }{ {"123456789", 12739}, {"{}foo", 9500}, {"foo{}", 5542}, {"foo{}{bar}", 8363}, {"", 10503}, {"", 5176}, {string([]byte{83, 153, 134, 118, 229, 214, 244, 75, 140, 37, 215, 215}), 5463}, } // Empty keys receive random slot. rand.Seed(100) for _, test := range tests { Expect(hashtag.Slot(test.key)).To(Equal(test.slot), "for %s", test.key) } }) It("should extract keys from tags", func() { tests := []struct { one, two string }{ {"foo{bar}", "bar"}, {"{foo}bar", "foo"}, {"{user1000}.following", "{user1000}.followers"}, {"foo{{bar}}zap", "{bar"}, {"foo{bar}{zap}", "bar"}, } for _, test := range tests {