class Redis::Cluster

Overview

Use in place of a Redis::Client when talking to Redis clusters. This class will discover all nodes in a Redis cluster when given a URI for any of them, route commands to appropriate shards based on the keys they operate on, and route commands which do not change state to shard replicas to spread the load across the cluster.

As nodes are added or removed, replicas are promoted, or hash slots migrate between shards, the cluster client will adapt to the new topology automatically. Commands that receive a MOVED redirection are retried against the new node, and topology is re-discovered in the background so subsequent commands route correctly without requiring another redirection. Commands that receive an ASK redirection (slot mid-migration) are retried against the importing node with an ASKING prefix.

It's important that, when using commands which operate on multiple keys (for example: MGET, DEL, RPOPLPUSH, etc) that all specified keys reside on the same shard in the cluster. Usually, this means designing your key names with curly braces around parts of them to ensure they hash to the same key slot. For example:

redis.del "{comment}:1", "{comment}:2"
value = redis.rpoplpush "{queue}:default", "{queue}:default:pending"

If you want to use a Redis module that provides custom commands, you can register them as read-only with Redis::Cluster.register_read_only_commands and they will automatically be routed to replicas. See redis/cluster/json.cr for example usage.

Included Modules

Defined in:

cluster.cr
errors.cr

Constructors

Class Method Summary

Instance Method Summary

Instance methods inherited from module Redis::Commands::Immediate

cms cms, get!(key : String) : String get!, info info, topk topk

Instance methods inherited from module Redis::Commands

bf bf, cms cms, dbsize dbsize, decr(key : String) decr, decrby(key : String, amount : Int | String) decrby, del(keys : Enumerable(String))
del(*keys : String)
del
, dump(key : String) dump, eval(script : String, keys : Enumerable(String) = EmptyEnumerable.new, args : Enumerable(String) = EmptyEnumerable.new) eval, eval_ro(script : String, keys : Enumerable(String) = EmptyEnumerable.new, args : Enumerable(String) = EmptyEnumerable.new) eval_ro, evalsha(sha : String, keys : Enumerable(String) = EmptyEnumerable.new, args : Enumerable(String) = EmptyEnumerable.new) evalsha, evalsha_ro(sha : String, keys : Enumerable(String) = EmptyEnumerable.new, args : Enumerable(String) = EmptyEnumerable.new) evalsha_ro, exists(keys : Enumerable(String))
exists(*keys : String)
exists
, expire(key : String, ttl : Time::Span)
expire(key : String, ttl : Int)
expire
, expireat(key : String, at : Time) expireat, flushall flushall, flushdb flushdb, ft ft, get(key : String) get, getdel(key : String) getdel, incr(key : String) incr, incrby(key : String, amount : Int | String) incrby, incrbyfloat(key : String, amount : Float | String) incrbyfloat, info(section : String) info, json json, keys(pattern = "*") keys, mget(keys : Enumerable(String))
mget(*keys : String)
mget
, mset(data : ::Hash(String, String)) mset, pexpire(key : String, ttl : Time::Span)
pexpire(key : String, ttl : Int)
pexpire
, pexpireat(key : String, at : Time) pexpireat, ping(message : String | Nil = nil) ping, pttl(key : String) pttl, publish(channel : String, message : String) publish, run(command) run, scan(cursor : String = "0", match : String | Nil = nil, count : String | Int | Nil = nil, type : String | Nil = nil) scan, script_exists(shas : Enumerable(String))
script_exists(*shas : String)
script_exists
, script_flush(mode : ScriptFlushMode)
script_flush
script_flush
, script_kill script_kill, script_load(script : String) script_load, set(key, value, *, ex : Time, nx = false, xx = false, keepttl = false, get = false)
set(key, value, *, ex : Time::Span, nx = false, xx = false, keepttl = false, get = false)
set(key : String, value : String | Bytes, *, ex : String | Int | Nil = nil, px : String | Int | Nil = nil, nx = false, xx = false, keepttl = false, get = false)
set
, strlen(key : String) strlen, tdigest tdigest, ts ts, ttl(key : String) ttl, type(key : String) type, unlink(keys : Enumerable(String))
unlink(*keys : String)
unlink
, wait(numreplicas replica_count : Int | String, timeout : Time::Span)
wait(numreplicas replica_count : Int | String, timeout : Int | String)
wait

Instance methods inherited from module Redis::Commands::Vector

vadd(key : String, vector : Enumerable(Float32), element : String, *, reduce : Int | String | Nil = nil, setattr attributes = nil) vadd, vcard(key : String) vcard, vdim(key : String) vdim, vemb(key : String, element : String) vemb, vgetattr(key : String, element : String, as type : T.class) : T | Nil forall T
vgetattr(key : String, element : String)
vgetattr
, vinfo(key : String) vinfo, vrange(key : String, start : String, end stop : String, count : Int | String | Nil = nil) vrange, vsim(key : String, element : String, *, epsilon : Float | String | Nil = nil, withscores : Bool = false, count : Int | String | Nil = nil)
vsim(key : String, vector : Array(Float32), *, epsilon : Float | String | Nil = nil, withscores : Bool = false, count : Int | String | Nil = nil)
vsim

Instance methods inherited from module Redis::Commands::HyperLogLog

pfadd(key : String, values : Enumerable(String))
pfadd(key : String, *values : String)
pfadd
, pfcount(keys : Enumerable(String))
pfcount(*keys : String)
pfcount
, pfmerge(destination_key target : String, source_keys sources : Enumerable(String))
pfmerge(destination_key target : String, *source_keys : String)
pfmerge

Instance methods inherited from module Redis::Commands::Geo

geoadd(key : String, entries : Enumerable(Redis::Geo::Member), nx = nil, xx = nil, ch = nil)
geoadd(key : String, *entries : String, nx = nil, xx = nil, ch = nil)
geoadd
, geodist(key : String, member1 : String, member2 : String, unit : Redis::Geo::Unit | Nil = nil) geodist, geopos(key : String, members : Enumerable(String))
geopos(key : String, *members : String)
geopos
, geosearch(key : String, *, fromlonlat lonlat : Tuple(String, String), byradius radius : Redis::Geo::Radius, sort : Redis::Geo::Sort | Nil = nil, count : Int | String | Nil = nil, withcoord : Bool = false, withdist : Bool = false)
geosearch(key : String, *, fromlonlat lonlat : Tuple(String, String), bybox box : Redis::Geo::Box, sort : Redis::Geo::Sort | Nil = nil, count : Int | String | Nil = nil, withcoord : Bool = false, withdist : Bool = false)
geosearch(key : String, *, frommember member : String, byradius radius : Redis::Geo::Radius, sort : Redis::Geo::Sort | Nil = nil, count : Int | String | Nil = nil, withcoord : Bool = false, withdist : Bool = false)
geosearch(key : String, *, frommember member : String, bybox box : Redis::Geo::Box, sort : Redis::Geo::Sort | Nil = nil, count : Int | String | Nil = nil, withcoord : Bool = false, withdist : Bool = false)
geosearch

Instance methods inherited from module Redis::Commands::Stream

xack(key : String, group : String, id : String)
xack(key : String, group : String, ids : Enumerable(String))
xack
, xackdel(key : String, group : String, delete_mode : DeleteMode | Nil, ids : Enumerable(String))
xackdel(key : String, group : String, ids : Enumerable(String))
xackdel
, xadd(key : String, id : String, fields : NamedTuple | ::Hash(String, String), *, idmpauto : String | Nil = nil, idmp : Tuple(String, String) | Nil = nil)
xadd(key : String, id : String, *, maxlen, fields : NamedTuple | ::Hash(String, String), idmpauto : String | Nil = nil, idmp : Tuple(String, String) | Nil = nil)
xadd(key : String, id : String, *, minid, fields : NamedTuple | ::Hash(String, String), idmpauto : String | Nil = nil, idmp : Tuple(String, String) | Nil = nil)
xadd(key : String, id : String, **fields : String)
xadd(key : String, id : String, *, maxlen, **fields : String)
xadd(key : String, id : String, *, minid, **fields : String)
xadd
, xautoclaim(key : String, group : String, consumer : String, min_idle_time : Time::Span, start : String, count : Int32 | String | Nil = nil) xautoclaim, xdel(key : String, ids : Enumerable(String))
xdel(key : String, *ids : String)
xdel
, xgroup(command : String, key : String, groupname : String)
xgroup(command : XGroup, key : String, groupname : String, *, id : String | Nil = nil, mkstream = false, consumer_name : String | Nil = nil)
xgroup(command : String, key : String, groupname : String, *args : String)
xgroup
, xgroup_create(key : String, groupname : String, *, id : String = "$", mkstream = false) xgroup_create, xgroup_create_consumer(key : String, groupname : String, consumer_name : String) xgroup_create_consumer, xgroup_del_consumer(key : String, group : String, consumer : String) xgroup_del_consumer, xgroup_destroy(key : String, group : String) xgroup_destroy, xinfo_consumers(key : String, group : String) xinfo_consumers, xinfo_groups(key : String) xinfo_groups, xinfo_stream(key : String) xinfo_stream, xinfo_stream_full(key : String, *, count : Int | String | Nil = nil) xinfo_stream_full, xlen(key : String) xlen, xnack(key : String, group : String, mode : NackMode, ids : Enumerable(String)) xnack, xpending(key : String, group : String, start : String, end finish : String, count : String | Int32, idle : String | Time::Span | Nil = nil)
xpending(key : String, group : String)
xpending
, xrange(key : String, start min : String, end max : String, count : String | Int32 | Nil = nil) xrange, xread(*, count : Int | String | Nil = nil, block : Time::Span | Int | String | Nil = nil, streams : NamedTuple)
xread(*, count : Int | String | Nil = nil, block : Time::Span | Int | String | Nil = nil, streams : ::Hash(String, String))
xread
, xreadgroup(group : String, consumer : String, count : String | Int32 | Nil = nil, block : Time::Span | String | Int32 | Nil = nil, claim : Time::Span | String | Int32 | Nil = nil, no_ack = false, streams : ::Hash(String, String) = {} of String => String)
xreadgroup(group : String, consumer : String, count : String | Int32 | Nil = nil, block : Time::Span | String | Int32 | Nil = nil, claim : Time::Span | String | Int32 | Nil = nil, no_ack = false, streams : NamedTuple = NamedTuple.new)
xreadgroup
, xrevrange(key : String, end max : String, start min : String, count : String | Int32 | Nil = nil) xrevrange, xtrim(key : String, *, maxlen : Tuple(String, String), limit : String | Int32 | Nil = nil, delete_mode : DeleteMode | Nil = nil)
xtrim(key : String, *, minid : Tuple(String, String), limit : String | Int32 | Nil = nil, delete_mode : DeleteMode | Nil = nil)
xtrim

Instance methods inherited from module Redis::Commands::SortedSet

zadd(key : String, score : String | Int64 | Float64, value : String, *, nx = false, xx = false, gt = false, lt = false, ch = false, incr = false)
zadd(key : String, values : Enumerable(String), *, nx = false, xx = false, gt = false, lt = false, ch = false, incr = false)
zadd(key : String, *values : String, nx = false, xx = false, gt = false, lt = false, ch = false)
zadd
, zcard(key : String) zcard, zcount(key : String, min : String, max : String) zcount, zrange(key : String, start : String | Int64, stop : String | Int64, by order : ZRangeOrder | Nil = nil, rev : Bool | Nil = nil, limit : Tuple(String | Int, String | Int) | Nil = nil, with_scores : Bool = false) zrange, zrangebyscore(key : String, min : String | Float64, max : String | Float64, with_scores = false, limit : Enumerable(String) | Nil = nil) zrangebyscore, zrem(key : String, value : String)
zrem(key : String, values : Enumerable(String))
zrem(key : String, *values : String)
zrem
, zremrangebylex(key : String, min : String, max : String)
zremrangebylex(key : String, range : Range)
zremrangebylex
, zremrangebyrank(key : String, start : Int64 | String, stop : Int64 | String)
zremrangebyrank(key : String, range : Range)
zremrangebyrank
, zremrangebyscore(key : String, min : String | Float64, max : String | Float64)
zremrangebyscore(key : String, range : Range)
zremrangebyscore
, zrevrange(key : String, starting : String | Int64, ending : String | Int64, with_scores : Bool = false) zrevrange, zscan(key : String, cursor : String, match pattern : String | Nil = nil, count : String | Nil = nil) zscan, zscore(key : String, value : String) zscore

Instance methods inherited from module Redis::Commands::Set

sadd(key : String, values : Enumerable(String))
sadd(key : String, *values : String)
sadd
, scard(key : String) scard, sdiff(first : String, second : String) sdiff, sinter(keys : Enumerable(String))
sinter(first : String, *others : String)
sinter
, sismember(key : String, value : String) sismember, smembers(key : String) smembers, srem(key : String, members : Enumerable(String))
srem(key : String, *values : String)
srem
, sscan(key : String, cursor : String, match pattern : String | Nil = nil, count : String | Nil = nil) sscan

Instance methods inherited from module Redis::Commands::List

blpop(keys : Enumerable(String), timeout : Time::Span)
blpop(*keys : String, timeout : Time::Span)
blpop(*keys : String, timeout : Int | Float)
blpop(*keys : String, timeout : String)
blpop
, brpop(keys : Enumerable(String), timeout : Int)
brpop(*keys : String, timeout : Time::Span)
brpop(*keys : String, timeout : Number)
brpop(*keys : String, timeout : String)
brpop
, brpoplpush(source : String, destination : String, timeout : Time::Span)
brpoplpush(source : String, destination : String, timeout : Int | String)
brpoplpush
, llen(key : String) llen, lmove(from source : String, to destination : String, from_side source_side : Side, to_side destination_side : Side) lmove, lpop(key : String, count : String | Nil = nil) lpop, lpush(key : String, values : Enumerable(String))
lpush(key, *values : String)
lpush
, lrange(key : String, start : String | Int, finish : String | Int) lrange, lrem(key : String, count : Int, value : String) lrem, ltrim(key : String, start : String | Int, stop : String | Int)
ltrim(key : String, range : Range(String, String))
ltrim(key : String, range : Range(Int32, Int32))
ltrim
, rpop(key : String) rpop, rpoplpush(source : String, destination : String) rpoplpush, rpush(key : String, values : Enumerable(String))
rpush(key, *values : String)
rpush

Instance methods inherited from module Redis::Commands::Hash

hdel(key : String, fields : Enumerable(String))
hdel(key : String, *fields : String)
hdel
, hget(key : String, field : String) hget, hgetall(key : String) hgetall, hincrby(key : String, field : String, increment : Int | String) hincrby, hmget(key : String, fields : Enumerable(String))
hmget(key : String, *fields : String)
hmget
, hmset(key : String, data : ::Hash(String, String)) hmset, hscan(key : String, cursor : String, *, match pattern : String | Nil = nil, count : String | Int | Nil = nil) hscan, hset(key : String, fields : Enumerable(String))
hset(key : String, fields : ::Hash(String, String))
hset(key : String, *fields : String)
hset(key : String, **fields : String)
hset
, hsetnx(key : String, field : String, value : String) hsetnx

Constructor Detail

def self.new(uri : URI = URI.parse(ENV["REDIS_CLUSTER_URL"]? || "redis:///"), topology_refresh_throttle : Time::Span = 1.second) #

Pass a URI (defaulting to the REDIS_CLUSTER_URL environment variable) to connect to the specified Redis cluster — the URI can point to any server in the cluster and Redis::Cluster will discover the rest.

topology_refresh_throttle controls the minimum interval between automatic background topology refreshes. When MOVED redirections come in faster than this interval, refreshes are coalesced.


[View source]

Class Method Detail

def self.register_read_only_command(command : String) #

Tell the cluster driver that the specified Redis command can be routed to read-only replicas.

Redis::Cluster.register_read_only_command "mymodule.get"

[View source]
def self.register_read_only_commands(commands : Enumerable(String)) #

Tell the cluster driver that all the specified Redis commands can be routed to read-only replicas.

Redis::Cluster.register_read_only_commands %w[
  mymodule.get
  mymodule.mget
]

[View source]

Instance Method Detail

def close #

Close all connections to this Redis cluster


[View source]
def flushdb #

Executes #flushdb on each shard in the cluster.


[View source]
def keys : Array(String) #

Get all key across all shards. This executes a #keys command on every shard in the cluster. Probably not a good idea in production since this will block every Redis shard or replica for the duration of the query, but we're supporting it because you may have a reasonable use case for it at some point and it's just not easy to do otherwise.


[View source]
def pipeline(key : String, &) #

Run a pipeline for the specified key

cluster.pipeline "{widgets}" do |pipe|
  widget_ids.each do |id|
    pipe.get "{widgets}:#{id}"
  end
end

WARNING All keys that this pipeline operates on MUST reside on the same shard. It's best to pass a pre-hashed key (one containing {}) to this method. See the example above.


[View source]
def psubscribe(*patterns : String, &) #

Subscribe to one or more pubsub channel patterns. Like #subscribe, messages are propagated across the cluster.


[View source]
def refresh_topology(force : Bool = true) : Nil #

Force a refresh of the cluster's topology. This is normally done automatically in response to MOVED redirections, but it can be useful to call this method explicitly when an external process tells you that the cluster has changed.


[View source]
def run(command full_command) #
Description copied from module Redis::Commands

Execute the given command and return the result from the server. Commands must be an Enumerable and its size method must be re-entrant.

run({"set", "foo", "bar"})

[View source]
def scan_each(*args, **kwargs, &) : Nil #

Execute Commands#scan_each on each shard, yielding any matching keys.


[View source]
def slot_for(key : String) #

Return the Redis hash slot for the given key. This is useful for seeing which shard your command will be routed to.


[View source]
def spublish(channel : String, message : String) #

Publish a message to a sharded pubsub channel. The message is delivered only to subscribers on the shard that owns the channel's hash slot — unlike publish, it is not propagated across the cluster.


[View source]
def ssubscribe(*channels : String, &) #

Subscribe to one or more sharded pubsub channels. All channels must hash to the same slot (use {} to force co-location). The block yields a Subscription and the underlying Connection, which holds the subscription for its duration.

cluster.ssubscribe "orders" do |subscription, connection|
  subscription.on_message do |channel, message|
    # ...
  end
end

[View source]
def subscribe(*channels : String, &) #

Subscribe to one or more pubsub channels. Regular (non-sharded) pub/sub messages are propagated across the cluster, so the subscriber will receive messages regardless of which node a publisher targets.


[View source]
def sunsubscribe(*channels : String) #

Unsubscribe from sharded pubsub channels on the shard that owns their hash slot. Typically called from inside an #ssubscribe block via the yielded Connection; calling this on the cluster directly routes a standalone SUNSUBSCRIBE to the shard and has no effect on a subscription held by a different connection.


[View source]