Skip to content

Latest commit

 

History

History
102 lines (74 loc) · 4.21 KB

README.md

File metadata and controls

102 lines (74 loc) · 4.21 KB

Redis::Cluster

Getting started

Install with:

$ gem install redis-clustering

You can connect to Redis by instantiating the Redis::Cluster class:

require "redis-clustering"

redis = Redis::Cluster.new(nodes: (7000..7005).map { |port| "redis://127.0.0.1:#{port}" })

NB: Both redis_cluster and redis-cluster are unrelated and abandoned gems.

# Nodes can be passed to the client as an array of connection URLs.
nodes = (7000..7005).map { |port| "redis://127.0.0.1:#{port}" }
redis = Redis::Cluster.new(nodes: nodes)

# You can also specify the options as a Hash. The options are the same as for a single server connection.
(7000..7005).map { |port| { host: '127.0.0.1', port: port } }

You can also specify only a subset of the nodes, and the client will discover the missing ones using the CLUSTER NODES command.

Redis::Cluster.new(nodes: %w[redis://127.0.0.1:7000])

If you want the connection to be able to read from any replica, you must pass the replica: true. Note that this connection won't be usable to write keys.

Redis::Cluster.new(nodes: nodes, replica: true)

Also, you can specify the :replica_affinity option if you want to prevent accessing cross availability zones.

Redis::Cluster.new(nodes: nodes, replica: true, replica_affinity: :latency)

The calling code is responsible for avoiding cross slot commands.

redis = Redis::Cluster.new(nodes: %w[redis://127.0.0.1:7000])

redis.mget('key1', 'key2')
#=> Redis::CommandError (CROSSSLOT Keys in request don't hash to the same slot)

redis.mget('{key}1', '{key}2')
#=> [nil, nil]
  • The client automatically reconnects after a failover occurred, but the caller is responsible for handling errors while it is happening.
  • The client support permanent node failures, and will reroute requests to promoted slaves.
  • The client supports MOVED and ASK redirections transparently.

Cluster mode with SSL/TLS

Since Redis can return FQDN of nodes in reply to client since 7.* with CLUSTER commands, we can use cluster feature with SSL/TLS connection like this:

Redis::Cluster.new(nodes: %w[rediss://foo.example.com:6379])

On the other hand, in Redis versions prior to 6.*, you can specify options like the following if cluster mode is enabled and client has to connect to nodes via single endpoint with SSL/TLS.

Redis::Cluster.new(nodes: %w[rediss://foo-endpoint.example.com:6379], fixed_hostname: 'foo-endpoint.example.com')

In case of the above architecture, if you don't pass the fixed_hostname option to the client and servers return IP addresses of nodes, the client may fail to verify certificates.

Transaction with an optimistic locking

Since Redis cluster is a distributed system, several behaviors are different from a standalone server. Client libraries can make them compatible up to a point, but a part of features needs some restrictions. Especially, some cautions are needed to use the transaction feature with an optimistic locking.

redis.watch("{my}key") do |client|        # The client is an instance of the internal adapter
  if redis.get("{my}key") == "some value" # We can't use the client passed by the block argument
    client.multi do |tx|                  # The tx is the same instance of the internal adapter
      tx.set("{my}key", "other value")
      tx.incr("{my}counter")
    end
  else
    client.unwatch
  end
end

In a cluster mode client, you need to pass a block if you call the watch method and you need to specify an argument to the block. Also, you should use the block argument as a receiver to call the transaction feature methods in the block. The commands called by methods of the receiver are added to the internal pipeline for the transaction and they are sent to the server lastly. On the other hand, if you want to call other methods for commands, you can use the global instance of the client instead of the block argument. It affects out of the transaction pipeline and the replies are returned soon. Although the above restrictions are needed, this implementations is compatible with a standalone client.