redis-clustering 5.1.0 → 5.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48b10241c3e0f41500b8ce224013546591a034dfbc6a0fbbcbb9bef136eeca9e
4
- data.tar.gz: 7acea63bfa0f3eab6f4c570ddb9a164f6901090964cf5988b54a913e4becc794
3
+ metadata.gz: 9af906fe28a4173e248c913e907c2adf777e090f1c381a1eaec9f8c58f0c7b1b
4
+ data.tar.gz: 215aa3069d1af1e8d2279da77f89716743601c7ca1fadee5dc5b065f7c5fd4ea
5
5
  SHA512:
6
- metadata.gz: b7631f15ef1ce8b13d1af35f98e17f83a27b9dbd80ae8fd95c592010ed0d94dfcb3112bc1aed2e69044d9572e2cc4abe525bb88ae7bc44e72914cb66bb597a10
7
- data.tar.gz: ce47057bada6b051033e3ca8f9338c62388b0efa530cabf8aca5210243c21deb336a7de459f7c8ce0aa638a0645eb9d1b5ec82edc6075bc73a5670bd67c2107f
6
+ metadata.gz: 10afda63eeecbfb356e5c5dfda8cea0268a7d46721e6a5935f8d6fb67bc3f53402b2be4636d44a0c5af924158b145fb93899c84dba9c61edabd59dede84a0fe5
7
+ data.tar.gz: '0901be8172249c31b9b47fe11cd111fcb4b713684d1b34abf1bd179e25335c0bea5a10ea55aef09eb7781ea33fab40f347610c6d0e72005fa1ae54800b970828'
data/README.md CHANGED
@@ -75,3 +75,27 @@ Redis::Cluster.new(nodes: %w[rediss://foo-endpoint.example.com:6379], fixed_host
75
75
  ```
76
76
 
77
77
  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.
78
+
79
+ ## Transaction with an optimistic locking
80
+ Since Redis cluster is a distributed system, several behaviors are different from a standalone server.
81
+ Client libraries can make them compatible up to a point, but a part of features needs some restrictions.
82
+ Especially, some cautions are needed to use the transaction feature with an optimistic locking.
83
+
84
+ ```ruby
85
+ # The client is an instance of the internal adapter for the optimistic locking
86
+ redis.watch("{my}key") do |client|
87
+ if client.get("{my}key") == "some value"
88
+ # The tx is an instance of the internal adapter for the transaction
89
+ client.multi do |tx|
90
+ tx.set("{my}key", "other value")
91
+ tx.incr("{my}counter")
92
+ end
93
+ else
94
+ client.unwatch
95
+ end
96
+ end
97
+ ```
98
+
99
+ 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.
100
+ Also, you should use the block argument as a receiver to call commands in the block.
101
+ Although the above restrictions are needed, this implementations is compatible with a standalone client.
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'redis-cluster-client'
4
+ require 'redis/cluster/transaction_adapter'
4
5
 
5
6
  class Redis
6
7
  class Cluster
@@ -53,11 +54,11 @@ class Redis
53
54
  ruby2_keywords :initialize if respond_to?(:ruby2_keywords, true)
54
55
 
55
56
  def id
56
- @router.node_keys.join(' ')
57
+ server_url.join(' ')
57
58
  end
58
59
 
59
60
  def server_url
60
- @router.node_keys
61
+ @router.nil? ? @config.startup_nodes.keys : router.node_keys
61
62
  end
62
63
 
63
64
  def connected?
@@ -90,14 +91,42 @@ class Redis
90
91
  handle_errors { super(timeout, command, &block) }
91
92
  end
92
93
 
93
- def pipelined(&block)
94
- handle_errors { super(&block) }
94
+ def pipelined(exception: true, &block)
95
+ handle_errors { super(exception: exception, &block) }
95
96
  end
96
97
 
97
98
  def multi(watch: nil, &block)
98
99
  handle_errors { super(watch: watch, &block) }
99
100
  end
100
101
 
102
+ def watch(*keys, &block)
103
+ unless block_given?
104
+ raise(
105
+ Redis::Cluster::TransactionConsistencyError,
106
+ 'A block is required if you use the cluster client.'
107
+ )
108
+ end
109
+
110
+ unless block.arity == 1
111
+ raise(
112
+ Redis::Cluster::TransactionConsistencyError,
113
+ 'Given block needs an argument if you use the cluster client.'
114
+ )
115
+ end
116
+
117
+ handle_errors do
118
+ RedisClient::Cluster::OptimisticLocking.new(router).watch(keys) do |c, slot, asking|
119
+ transaction = Redis::Cluster::TransactionAdapter.new(
120
+ self, router, @command_builder, node: c, slot: slot, asking: asking
121
+ )
122
+
123
+ result = yield transaction
124
+ c.call('UNWATCH') unless transaction.lock_released?
125
+ result
126
+ end
127
+ end
128
+ end
129
+
101
130
  private
102
131
 
103
132
  def handle_errors
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis_client/cluster/transaction'
4
+
5
+ class Redis
6
+ class Cluster
7
+ class TransactionAdapter
8
+ class Internal < RedisClient::Cluster::Transaction
9
+ def initialize(client, router, command_builder, node: nil, slot: nil, asking: false)
10
+ @client = client
11
+ super(router, command_builder, node: node, slot: slot, asking: asking)
12
+ end
13
+
14
+ def multi
15
+ raise(Redis::Cluster::TransactionConsistencyError, "Can't nest multi transaction")
16
+ end
17
+
18
+ def exec
19
+ # no need to do anything
20
+ end
21
+
22
+ def discard
23
+ # no need to do anything
24
+ end
25
+
26
+ def watch(*_)
27
+ raise(Redis::Cluster::TransactionConsistencyError, "Can't use watch in a transaction")
28
+ end
29
+
30
+ def unwatch
31
+ # no need to do anything
32
+ end
33
+
34
+ private
35
+
36
+ def method_missing(name, *args, **kwargs, &block)
37
+ return call(name, *args, **kwargs, &block) if @client.respond_to?(name)
38
+
39
+ super
40
+ end
41
+
42
+ def respond_to_missing?(name, include_private = false)
43
+ return true if @client.respond_to?(name)
44
+
45
+ super
46
+ end
47
+ end
48
+
49
+ def initialize(client, router, command_builder, node: nil, slot: nil, asking: false)
50
+ @client = client
51
+ @router = router
52
+ @command_builder = command_builder
53
+ @node = node
54
+ @slot = slot
55
+ @asking = asking
56
+ @lock_released = false
57
+ end
58
+
59
+ def lock_released?
60
+ @lock_released
61
+ end
62
+
63
+ def multi
64
+ @lock_released = true
65
+ transaction = Redis::Cluster::TransactionAdapter::Internal.new(
66
+ @client, @router, @command_builder, node: @node, slot: @slot, asking: @asking
67
+ )
68
+ yield transaction
69
+ transaction.execute
70
+ end
71
+
72
+ def exec
73
+ # no need to do anything
74
+ end
75
+
76
+ def discard
77
+ # no need to do anything
78
+ end
79
+
80
+ def watch(*_)
81
+ raise(Redis::Cluster::TransactionConsistencyError, "Can't nest watch command if you use the cluster client")
82
+ end
83
+
84
+ def unwatch
85
+ @lock_released = true
86
+ @node.call('UNWATCH')
87
+ end
88
+
89
+ private
90
+
91
+ def method_missing(name, *args, **kwargs, &block)
92
+ return @client.public_send(name, *args, **kwargs, &block) if @client.respond_to?(name)
93
+
94
+ super
95
+ end
96
+
97
+ def respond_to_missing?(name, include_private = false)
98
+ return true if @client.respond_to?(name)
99
+
100
+ super
101
+ end
102
+ end
103
+ end
104
+ end
data/lib/redis/cluster.rb CHANGED
@@ -96,6 +96,35 @@ class Redis
96
96
  send_command([:cluster, subcommand] + args, &block)
97
97
  end
98
98
 
99
+ # Watch the given keys to determine execution of the MULTI/EXEC block.
100
+ #
101
+ # Using a block is required for a cluster client. It's different from a standalone client.
102
+ # And you should use the block argument as a receiver if you call commands.
103
+ #
104
+ # An `#unwatch` is automatically issued if an exception is raised within the
105
+ # block that is a subclass of StandardError and is not a ConnectionError.
106
+ #
107
+ # @param keys [String, Array<String>] one or more keys to watch
108
+ # @return [Object] returns the return value of the block
109
+ #
110
+ # @example A typical use case.
111
+ # # The client is an instance of the internal adapter for the optimistic locking
112
+ # redis.watch("{my}key") do |client|
113
+ # if client.get("{my}key") == "some value"
114
+ # # The tx is an instance of the internal adapter for the transaction
115
+ # client.multi do |tx|
116
+ # tx.set("{my}key", "other value")
117
+ # tx.incr("{my}counter")
118
+ # end
119
+ # else
120
+ # client.unwatch
121
+ # end
122
+ # end
123
+ # #=> ["OK", 6]
124
+ def watch(*keys, &block)
125
+ synchronize { |c| c.watch(*keys, &block) }
126
+ end
127
+
99
128
  private
100
129
 
101
130
  def initialize_client(options)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis-clustering
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.1.0
4
+ version: 5.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ezra Zygmuntowicz
@@ -16,7 +16,7 @@ authors:
16
16
  autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
- date: 2024-02-09 00:00:00.000000000 Z
19
+ date: 2024-08-21 00:00:00.000000000 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  name: redis
@@ -24,28 +24,28 @@ dependencies:
24
24
  requirements:
25
25
  - - '='
26
26
  - !ruby/object:Gem::Version
27
- version: 5.1.0
27
+ version: 5.3.0
28
28
  type: :runtime
29
29
  prerelease: false
30
30
  version_requirements: !ruby/object:Gem::Requirement
31
31
  requirements:
32
32
  - - '='
33
33
  - !ruby/object:Gem::Version
34
- version: 5.1.0
34
+ version: 5.3.0
35
35
  - !ruby/object:Gem::Dependency
36
36
  name: redis-cluster-client
37
37
  requirement: !ruby/object:Gem::Requirement
38
38
  requirements:
39
39
  - - ">="
40
40
  - !ruby/object:Gem::Version
41
- version: 0.7.0
41
+ version: 0.10.0
42
42
  type: :runtime
43
43
  prerelease: false
44
44
  version_requirements: !ruby/object:Gem::Requirement
45
45
  requirements:
46
46
  - - ">="
47
47
  - !ruby/object:Gem::Version
48
- version: 0.7.0
48
+ version: 0.10.0
49
49
  description: |2
50
50
  A Ruby client that tries to match Redis' Cluster API one-to-one, while still
51
51
  providing an idiomatic interface.
@@ -61,6 +61,7 @@ files:
61
61
  - lib/redis-clustering.rb
62
62
  - lib/redis/cluster.rb
63
63
  - lib/redis/cluster/client.rb
64
+ - lib/redis/cluster/transaction_adapter.rb
64
65
  - lib/redis/cluster/version.rb
65
66
  homepage: https://github.com/redis/redis-rb/blob/master/cluster
66
67
  licenses:
@@ -68,9 +69,9 @@ licenses:
68
69
  metadata:
69
70
  bug_tracker_uri: https://github.com/redis/redis-rb/issues
70
71
  changelog_uri: https://github.com/redis/redis-rb/blob/master/cluster/CHANGELOG.md
71
- documentation_uri: https://www.rubydoc.info/gems/redis/5.1.0
72
+ documentation_uri: https://www.rubydoc.info/gems/redis/5.3.0
72
73
  homepage_uri: https://github.com/redis/redis-rb/blob/master/cluster
73
- source_code_uri: https://github.com/redis/redis-rb/tree/v5.1.0/cluster
74
+ source_code_uri: https://github.com/redis/redis-rb/tree/v5.3.0/cluster
74
75
  post_install_message:
75
76
  rdoc_options: []
76
77
  require_paths:
@@ -86,7 +87,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
87
  - !ruby/object:Gem::Version
87
88
  version: '0'
88
89
  requirements: []
89
- rubygems_version: 3.3.7
90
+ rubygems_version: 3.5.11
90
91
  signing_key:
91
92
  specification_version: 4
92
93
  summary: A Ruby client library for Redis Cluster