redis-clustering 5.0.0.beta3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a1f30a75985b5fc7e5ea4f5dfa1b8daaf85e2f0e387c669977c9602035e940cf
4
+ data.tar.gz: 316047ae9330f0fee89d4870a6bbf56a79158bd8050d5f382956bae971618398
5
+ SHA512:
6
+ metadata.gz: 9de07f2ae00e29220f13a09aad0f2741c87e4f41597385775ec4f2dcc049fc92b7a1c559b8230d5519c8f91f41d880575017c4e0f4519fb04d5ca3f16c005906
7
+ data.tar.gz: e02b002e2c7b61c6acba9fc586fbd9b3f47c1cdf6e81f8e4f40d3d366dd740994040e4d9ad642c97c3a1527ae25947d90feb61f49d9fce2424af979171504002
data/CHANGELOG.md ADDED
File without changes
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Ezra Zygmuntowicz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,71 @@
1
+ # Redis::Cluster
2
+
3
+ ## Getting started
4
+
5
+ Install with:
6
+
7
+ ```
8
+ $ gem install redis-clustering
9
+ ```
10
+
11
+ You can connect to Redis by instantiating the `Redis::Cluster` class:
12
+
13
+ ```ruby
14
+ require "redis-clustering"
15
+
16
+ redis = Redis::Cluter.new(nodes: (7000..7005).map { |port| "redis://127.0.0.1:#{port}" })
17
+ ```
18
+
19
+ NB: Both `redis_cluster` and `redis-cluster` are unrelated and abandoned gems.
20
+
21
+ ```ruby
22
+ # Nodes can be passed to the client as an array of connection URLs.
23
+ nodes = (7000..7005).map { |port| "redis://127.0.0.1:#{port}" }
24
+ redis = Redis::Cluster.new(nodes: nodes)
25
+
26
+ # You can also specify the options as a Hash. The options are the same as for a single server connection.
27
+ (7000..7005).map { |port| { host: '127.0.0.1', port: port } }
28
+ ```
29
+
30
+ You can also specify only a subset of the nodes, and the client will discover the missing ones using the [CLUSTER NODES](https://redis.io/commands/cluster-nodes) command.
31
+
32
+ ```ruby
33
+ Redis::Cluster.new(nodes: %w[redis://127.0.0.1:7000])
34
+ ```
35
+
36
+ If you want [the connection to be able to read from any replica](https://redis.io/commands/readonly), you must pass the `replica: true`. Note that this connection won't be usable to write keys.
37
+
38
+ ```ruby
39
+ Redis::Cluster.new(nodes: nodes, replica: true)
40
+ ```
41
+
42
+ The calling code is responsible for [avoiding cross slot commands](https://redis.io/topics/cluster-spec#keys-distribution-model).
43
+
44
+ ```ruby
45
+ redis = Redis::Cluster.new(nodes: %w[redis://127.0.0.1:7000])
46
+
47
+ redis.mget('key1', 'key2')
48
+ #=> Redis::CommandError (CROSSSLOT Keys in request don't hash to the same slot)
49
+
50
+ redis.mget('{key}1', '{key}2')
51
+ #=> [nil, nil]
52
+ ```
53
+
54
+ * The client automatically reconnects after a failover occurred, but the caller is responsible for handling errors while it is happening.
55
+ * The client support permanent node failures, and will reroute requests to promoted slaves.
56
+ * The client supports `MOVED` and `ASK` redirections transparently.
57
+
58
+ ## Cluster mode with SSL/TLS
59
+ 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:
60
+
61
+ ```ruby
62
+ Redis.new(cluster: %w[rediss://foo.example.com:6379])
63
+ ```
64
+
65
+ 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.
66
+
67
+ ```ruby
68
+ Redis.new(cluster: %w[rediss://foo-endpoint.example.com:6379], fixed_hostname: 'foo-endpoint.example.com')
69
+ ```
70
+
71
+ 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.
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis-cluster-client'
4
+
5
+ class Redis
6
+ class Cluster
7
+ class Client < RedisClient::Cluster
8
+ ERROR_MAPPING = ::Redis::Client::ERROR_MAPPING.merge(
9
+ RedisClient::Cluster::InitialSetupError => Redis::Cluster::InitialSetupError,
10
+ RedisClient::Cluster::OrchestrationCommandNotSupported => Redis::Cluster::OrchestrationCommandNotSupported,
11
+ RedisClient::Cluster::AmbiguousNodeError => Redis::Cluster::AmbiguousNodeError,
12
+ RedisClient::Cluster::ErrorCollection => Redis::Cluster::CommandErrorCollection
13
+ ).freeze
14
+
15
+ class << self
16
+ def config(**kwargs)
17
+ super(protocol: 2, **kwargs)
18
+ end
19
+
20
+ def sentinel(**kwargs)
21
+ super(protocol: 2, **kwargs)
22
+ end
23
+ end
24
+
25
+ def initialize(*)
26
+ handle_errors { super }
27
+ @inherit_socket = false
28
+ @pid = Process.pid
29
+ end
30
+ ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
31
+
32
+ def id
33
+ @router.node.node_keys.join(' ')
34
+ end
35
+
36
+ def server_url
37
+ @router.node.node_keys
38
+ end
39
+
40
+ def connected?
41
+ true
42
+ end
43
+
44
+ def disable_reconnection
45
+ yield # TODO: do we need this, is it doable?
46
+ end
47
+
48
+ def timeout
49
+ config.read_timeout
50
+ end
51
+
52
+ def db
53
+ 0
54
+ end
55
+
56
+ undef_method :call
57
+ undef_method :call_once
58
+ undef_method :call_once_v
59
+ undef_method :blocking_call
60
+
61
+ def call_v(command, &block)
62
+ handle_errors { super(command, &block) }
63
+ end
64
+
65
+ def blocking_call_v(timeout, command, &block)
66
+ timeout += self.timeout if timeout && timeout > 0
67
+ handle_errors { super(timeout, command, &block) }
68
+ end
69
+
70
+ def pipelined(&block)
71
+ handle_errors { super(&block) }
72
+ end
73
+
74
+ def multi(&block)
75
+ handle_errors { super(&block) }
76
+ end
77
+
78
+ private
79
+
80
+ def handle_errors
81
+ yield
82
+ rescue RedisClient::Cluster::ErrorCollection => error
83
+ error.errors.each do |_node, node_error|
84
+ if node_error.is_a?(RedisClient::AuthenticationError)
85
+ raise ERROR_MAPPING.fetch(node_error.class), node_error.message, node_error.backtrace
86
+ end
87
+ end
88
+ raise ERROR_MAPPING.fetch(error.class), error.message, error.backtrace
89
+ rescue ::RedisClient::Error => error
90
+ raise ERROR_MAPPING.fetch(error.class), error.message, error.backtrace
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "redis/version"
4
+
5
+ class Redis
6
+ class Cluster
7
+ VERSION = Redis::VERSION
8
+ end
9
+ end
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "redis"
4
+
5
+ class Redis
6
+ class Cluster < ::Redis
7
+ # Raised when client connected to redis as cluster mode
8
+ # and failed to fetch cluster state information by commands.
9
+ class InitialSetupError < BaseError
10
+ end
11
+
12
+ # Raised when client connected to redis as cluster mode
13
+ # and some cluster subcommands were called.
14
+ class OrchestrationCommandNotSupported < BaseError
15
+ def initialize(command, subcommand = '')
16
+ str = [command, subcommand].map(&:to_s).reject(&:empty?).join(' ').upcase
17
+ msg = "#{str} command should be used with care "\
18
+ 'only by applications orchestrating Redis Cluster, like redis-trib, '\
19
+ 'and the command if used out of the right context can leave the cluster '\
20
+ 'in a wrong state or cause data loss.'
21
+ super(msg)
22
+ end
23
+ end
24
+
25
+ # Raised when error occurs on any node of cluster.
26
+ class CommandErrorCollection < BaseError
27
+ attr_reader :errors
28
+
29
+ # @param errors [Hash{String => Redis::CommandError}]
30
+ # @param error_message [String]
31
+ def initialize(errors, error_message = 'Command errors were replied on any node')
32
+ @errors = errors
33
+ super(error_message)
34
+ end
35
+ end
36
+
37
+ # Raised when cluster client can't select node.
38
+ class AmbiguousNodeError < BaseError
39
+ end
40
+
41
+ def connection
42
+ raise NotImplementedError, "Redis::Cluster doesn't implement #connection"
43
+ end
44
+
45
+ # Create a new client instance
46
+ #
47
+ # @param [Hash] options
48
+ # @option options [Float] :timeout (5.0) timeout in seconds
49
+ # @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
50
+ # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`
51
+ # @option options [Integer, Array<Integer, Float>] :reconnect_attempts Number of attempts trying to connect,
52
+ # or a list of sleep duration between attempts.
53
+ # @option options [Boolean] :inherit_socket (false) Whether to use socket in forked process or not
54
+ # @option options [Array<String, Hash{Symbol => String, Integer}>] :nodes List of cluster nodes to contact
55
+ # @option options [Boolean] :replica Whether to use readonly replica nodes in Redis Cluster or not
56
+ # @option options [String] :fixed_hostname Specify a FQDN if cluster mode enabled and
57
+ # client has to connect nodes via single endpoint with SSL/TLS
58
+ # @option options [Class] :connector Class of custom connector
59
+ #
60
+ # @return [Redis::Cluster] a new client instance
61
+ def initialize(*) # rubocop:disable Lint/UselessMethodDefinition
62
+ super
63
+ end
64
+ ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
65
+
66
+ # Sends `CLUSTER *` command to random node and returns its reply.
67
+ #
68
+ # @see https://redis.io/commands#cluster Reference of cluster command
69
+ #
70
+ # @param subcommand [String, Symbol] the subcommand of cluster command
71
+ # e.g. `:slots`, `:nodes`, `:slaves`, `:info`
72
+ #
73
+ # @return [Object] depends on the subcommand
74
+ def cluster(subcommand, *args)
75
+ subcommand = subcommand.to_s.downcase
76
+ block = case subcommand
77
+ when 'slots'
78
+ HashifyClusterSlots
79
+ when 'nodes'
80
+ HashifyClusterNodes
81
+ when 'slaves'
82
+ HashifyClusterSlaves
83
+ when 'info'
84
+ HashifyInfo
85
+ else
86
+ Noop
87
+ end
88
+
89
+ send_command([:cluster, subcommand] + args, &block)
90
+ end
91
+
92
+ private
93
+
94
+ def initialize_client(options)
95
+ cluster_config = RedisClient.cluster(**options, protocol: 2, client_implementation: ::Redis::Cluster::Client)
96
+ cluster_config.new_client
97
+ end
98
+ end
99
+ end
100
+
101
+ require "redis/cluster/client"
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "redis/cluster"
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis-clustering
3
+ version: !ruby/object:Gem::Version
4
+ version: 5.0.0.beta3
5
+ platform: ruby
6
+ authors:
7
+ - Ezra Zygmuntowicz
8
+ - Taylor Weibley
9
+ - Matthew Clark
10
+ - Brian McKinney
11
+ - Salvatore Sanfilippo
12
+ - Luca Guidi
13
+ - Michel Martens
14
+ - Damian Janowski
15
+ - Pieter Noordhuis
16
+ autorequire:
17
+ bindir: bin
18
+ cert_chain: []
19
+ date: 2022-08-18 00:00:00.000000000 Z
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: redis-cluster-client
23
+ requirement: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '0.2'
28
+ type: :runtime
29
+ prerelease: false
30
+ version_requirements: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '0.2'
35
+ description: |2
36
+ A Ruby client that tries to match Redis' Cluster API one-to-one, while still
37
+ providing an idiomatic interface.
38
+ email:
39
+ - redis-db@googlegroups.com
40
+ executables: []
41
+ extensions: []
42
+ extra_rdoc_files: []
43
+ files:
44
+ - CHANGELOG.md
45
+ - LICENSE
46
+ - README.md
47
+ - lib/redis-clustering.rb
48
+ - lib/redis/cluster.rb
49
+ - lib/redis/cluster/client.rb
50
+ - lib/redis/cluster/version.rb
51
+ homepage: https://github.com/redis/redis-rb/blob/master/cluster
52
+ licenses:
53
+ - MIT
54
+ metadata:
55
+ bug_tracker_uri: https://github.com/redis/redis-rb/issues
56
+ changelog_uri: https://github.com/redis/redis-rb/blob/master/cluster/CHANGELOG.md
57
+ documentation_uri: https://www.rubydoc.info/gems/redis/5.0.0.beta3
58
+ homepage_uri: https://github.com/redis/redis-rb/blob/master/cluster
59
+ source_code_uri: https://github.com/redis/redis-rb/tree/v5.0.0.beta3/cluster
60
+ post_install_message:
61
+ rdoc_options: []
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: 2.7.0
69
+ required_rubygems_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">"
72
+ - !ruby/object:Gem::Version
73
+ version: 1.3.1
74
+ requirements: []
75
+ rubygems_version: 3.1.2
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: A Ruby client library for Redis Cluster
79
+ test_files: []