From c809246d8b39d8bbe9d03b67fee954081a863f32 Mon Sep 17 00:00:00 2001 From: Vladimir Mihailenco Date: Sat, 12 Sep 2015 09:36:03 +0300 Subject: [PATCH] Clarify thread safety. Fixes #166. --- cluster.go | 9 ++++++--- cluster_pipeline.go | 5 +++-- multi.go | 3 ++- pipeline.go | 6 +++--- pubsub.go | 3 ++- redis.go | 7 +++++++ ring.go | 11 +++++++---- sentinel.go | 5 +++-- 8 files changed, 33 insertions(+), 16 deletions(-) diff --git a/cluster.go b/cluster.go index cbf00b20..463cae4f 100644 --- a/cluster.go +++ b/cluster.go @@ -9,6 +9,9 @@ import ( "time" ) +// ClusterClient is a Redis Cluster client representing a pool of zero +// or more underlying connections. It's safe for concurrent use by +// multiple goroutines. type ClusterClient struct { commandable @@ -26,7 +29,7 @@ type ClusterClient struct { reloading uint32 } -// NewClusterClient returns a new Redis Cluster client as described in +// NewClusterClient returns a Redis Cluster client as described in // http://redis.io/topics/cluster-spec. func NewClusterClient(opt *ClusterOptions) *ClusterClient { client := &ClusterClient{ @@ -43,8 +46,8 @@ func NewClusterClient(opt *ClusterOptions) *ClusterClient { // Close closes the cluster client, releasing any open resources. // -// It is rare to Close a Client, as the Client is meant to be -// long-lived and shared between many goroutines. +// It is rare to Close a ClusterClient, as the ClusterClient is meant +// to be long-lived and shared between many goroutines. func (c *ClusterClient) Close() error { defer c.clientsMx.Unlock() c.clientsMx.Lock() diff --git a/cluster_pipeline.go b/cluster_pipeline.go index 01f06e7d..619ad82f 100644 --- a/cluster_pipeline.go +++ b/cluster_pipeline.go @@ -10,7 +10,8 @@ type ClusterPipeline struct { } // Pipeline creates a new pipeline which is able to execute commands -// against multiple shards. +// against multiple shards. It's NOT safe for concurrent use by +// multiple goroutines. func (c *ClusterClient) Pipeline() *ClusterPipeline { pipe := &ClusterPipeline{ cluster: c, @@ -82,7 +83,7 @@ func (pipe *ClusterPipeline) Exec() (cmds []Cmder, retErr error) { return cmds, retErr } -// Close marks the pipeline as closed +// Close closes the pipeline, releasing any open resources. func (pipe *ClusterPipeline) Close() error { pipe.Discard() pipe.closed = true diff --git a/multi.go b/multi.go index 1cc419c0..7b55c7b6 100644 --- a/multi.go +++ b/multi.go @@ -9,7 +9,8 @@ import ( var errDiscard = errors.New("redis: Discard can be used only inside Exec") // Multi implements Redis transactions as described in -// http://redis.io/topics/transactions. +// http://redis.io/topics/transactions. It's NOT safe for concurrent +// use by multiple goroutines. type Multi struct { commandable diff --git a/pipeline.go b/pipeline.go index 6fb1db1a..02ecbacc 100644 --- a/pipeline.go +++ b/pipeline.go @@ -1,9 +1,8 @@ package redis // Pipeline implements pipelining as described in -// http://redis.io/topics/pipelining. -// -// Pipeline is not thread-safe. +// http://redis.io/topics/pipelining. It's NOT safe for concurrent use +// by multiple goroutines. type Pipeline struct { commandable @@ -36,6 +35,7 @@ func (pipe *Pipeline) process(cmd Cmder) { pipe.cmds = append(pipe.cmds, cmd) } +// Close closes the pipeline, releasing any open resources. func (pipe *Pipeline) Close() error { pipe.Discard() pipe.closed = true diff --git a/pubsub.go b/pubsub.go index b85e475c..fa804eb4 100644 --- a/pubsub.go +++ b/pubsub.go @@ -15,7 +15,8 @@ func (c *Client) Publish(channel, message string) *IntCmd { } // PubSub implements Pub/Sub commands as described in -// http://redis.io/topics/pubsub. +// http://redis.io/topics/pubsub. It's NOT safe for concurrent use by +// multiple goroutines. type PubSub struct { *baseClient diff --git a/redis.go b/redis.go index 1504e6cc..aea53a22 100644 --- a/redis.go +++ b/redis.go @@ -80,6 +80,9 @@ func (c *baseClient) process(cmd Cmder) { } // Close closes the client, releasing any open resources. +// +// It is rare to Close a Client, as the Client is meant to be +// long-lived and shared between many goroutines. func (c *baseClient) Close() error { return c.connPool.Close() } @@ -173,6 +176,9 @@ func (opt *Options) getIdleTimeout() time.Duration { //------------------------------------------------------------------------------ +// Client is a Redis client representing a pool of zero or more +// underlying connections. It's safe for concurrent use by multiple +// goroutines. type Client struct { *baseClient commandable @@ -186,6 +192,7 @@ func newClient(opt *Options, pool pool) *Client { } } +// NewClient returns a client to the Redis Server specified by Options. func NewClient(opt *Options) *Client { pool := newConnPool(opt) return newClient(opt, pool) diff --git a/ring.go b/ring.go index 4b20e7a9..8005af97 100644 --- a/ring.go +++ b/ring.go @@ -92,7 +92,8 @@ func (shard *ringShard) Vote(up bool) bool { } // Ring is a Redis client that uses constistent hashing to distribute -// keys across multiple Redis servers (shards). +// keys across multiple Redis servers (shards). It's safe for +// concurrent use by multiple goroutines. // // It monitors the state of each shard and removes dead shards from // the ring. When shard comes online it is added back to the ring. This @@ -215,8 +216,8 @@ func (ring *Ring) heartbeat() { // Close closes the ring client, releasing any open resources. // -// It is rare to Close a Client, as the Client is meant to be -// long-lived and shared between many goroutines. +// It is rare to Close a Ring, as the Ring is meant to be long-lived +// and shared between many goroutines. func (ring *Ring) Close() (retErr error) { defer ring.mx.Unlock() ring.mx.Lock() @@ -238,7 +239,8 @@ func (ring *Ring) Close() (retErr error) { } // RingPipeline creates a new pipeline which is able to execute commands -// against multiple shards. +// against multiple shards. It's NOT safe for concurrent use by +// multiple goroutines. type RingPipeline struct { commandable @@ -342,6 +344,7 @@ func (pipe *RingPipeline) Exec() (cmds []Cmder, retErr error) { return cmds, retErr } +// Close closes the pipeline, releasing any open resources. func (pipe *RingPipeline) Close() error { pipe.Discard() pipe.closed = true diff --git a/sentinel.go b/sentinel.go index 255416ec..04a821bd 100644 --- a/sentinel.go +++ b/sentinel.go @@ -54,8 +54,9 @@ func (opt *FailoverOptions) options() *Options { } } -// NewFailoverClient returns a Redis client with automatic failover -// capabilities using Redis Sentinel. +// NewFailoverClient returns a Redis client that uses Redis Sentinel +// for automatic failover. It's safe for concurrent use by multiple +// goroutines. func NewFailoverClient(failoverOpt *FailoverOptions) *Client { opt := failoverOpt.options() failover := &sentinelFailover{