Merge pull request #219 from go-redis/feature/cluster-watch

cluster: add Watch support.
This commit is contained in:
Vladimir Mihailenco 2015-12-17 15:52:04 +02:00
commit ba44d4d158
5 changed files with 103 additions and 25 deletions

View File

@ -44,6 +44,17 @@ func NewClusterClient(opt *ClusterOptions) *ClusterClient {
return client
}
// 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(hashSlot(keys[0]))
client, err := c.getClient(addr)
if err != nil {
return nil, err
}
return client.Watch(keys...)
}
// Close closes the cluster client, releasing any open resources.
//
// It is rare to Close a ClusterClient, as the ClusterClient is meant

View File

@ -4,9 +4,10 @@ package redis
type ClusterPipeline struct {
commandable
cmds []Cmder
cluster *ClusterClient
closed bool
cmds []Cmder
closed bool
}
// Pipeline creates a new pipeline which is able to execute commands

View File

@ -5,7 +5,9 @@ import (
"math/rand"
"net"
"reflect"
"strconv"
"strings"
"sync"
"testing"
"time"
@ -317,6 +319,49 @@ var _ = Describe("Cluster", func() {
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(ContainSubstring("MOVED"))
})
It("should Watch", func() {
var incr func(string) error
// Transactionally increments key using GET and SET commands.
incr = func(key string) error {
tx, err := client.Watch(key)
if err != nil {
return err
}
defer tx.Close()
n, err := tx.Get(key).Int64()
if err != nil && err != redis.Nil {
return err
}
_, err = tx.Exec(func() error {
tx.Set(key, strconv.FormatInt(n+1, 10), 0)
return nil
})
if err == redis.TxFailedErr {
return incr(key)
}
return err
}
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
err := incr("key")
Expect(err).NotTo(HaveOccurred())
}()
}
wg.Wait()
n, err := client.Get("key").Int64()
Expect(err).NotTo(HaveOccurred())
Expect(n).To(Equal(int64(100)))
})
})
})

View File

@ -23,8 +23,8 @@ type Multi struct {
closed bool
}
// Watch marks the keys to be watched for conditional execution
// of a transaction.
// Watch creates new transaction and marks the keys to be watched
// for conditional execution of a transaction.
func (c *Client) Watch(keys ...string) (*Multi, error) {
tx := c.Multi()
if err := tx.Watch(keys...).Err(); err != nil {

View File

@ -1,6 +1,9 @@
package redis_test
import (
"strconv"
"sync"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
@ -21,29 +24,47 @@ var _ = Describe("Multi", func() {
Expect(client.Close()).NotTo(HaveOccurred())
})
It("should exec", func() {
multi := client.Multi()
defer func() {
Expect(multi.Close()).NotTo(HaveOccurred())
}()
It("should Watch", func() {
var incr func(string) error
var (
set *redis.StatusCmd
get *redis.StringCmd
)
cmds, err := multi.Exec(func() error {
set = multi.Set("key", "hello", 0)
get = multi.Get("key")
return nil
})
// Transactionally increments key using GET and SET commands.
incr = func(key string) error {
tx, err := client.Watch(key)
if err != nil {
return err
}
defer tx.Close()
n, err := tx.Get(key).Int64()
if err != nil && err != redis.Nil {
return err
}
_, err = tx.Exec(func() error {
tx.Set(key, strconv.FormatInt(n+1, 10), 0)
return nil
})
if err == redis.TxFailedErr {
return incr(key)
}
return err
}
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
err := incr("key")
Expect(err).NotTo(HaveOccurred())
}()
}
wg.Wait()
n, err := client.Get("key").Int64()
Expect(err).NotTo(HaveOccurred())
Expect(cmds).To(HaveLen(2))
Expect(set.Err()).NotTo(HaveOccurred())
Expect(set.Val()).To(Equal("OK"))
Expect(get.Err()).NotTo(HaveOccurred())
Expect(get.Val()).To(Equal("hello"))
Expect(n).To(Equal(int64(100)))
})
It("should discard", func() {