redis-clustering 5.2.0 → 5.4.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0390ca44d9f71661f4cfb1a7f8d1a396a84e204e992cf8a5d0755af4232cb7ac'
4
- data.tar.gz: 923cffa3fc0a5a2aaecb4d669ea685e25047c7dc70f932f3e807e6e381fc3ecb
3
+ metadata.gz: f7060eff856ef3d21ddeff60bfd2a0b6d741d04fd9b233f5b36eee50812b88bc
4
+ data.tar.gz: 07d96f3179db08a80f054542474ef0d69a3db1cfce92b80b729a9f2b04bf9e1f
5
5
  SHA512:
6
- metadata.gz: b4c5f9903044360cd0317a2ad75d0c41cbd8627c34da86c9cca3471ef78e0256778c15f997f0073386b8dbfa38ca906ab03b31efb3b7c190b7412a0d6bdd1987
7
- data.tar.gz: 4e331655c8142429b1fbee72eec3c1fd26b42f5fa4e33156a0f3415151bd7bc3b9da85e604d464b4db33c2425d6a2b910718a6b6108ede5e7bc30635371ea33f
6
+ metadata.gz: 05b728ef68b71a9dc1f370cb2c03cc5fb7bc01c9a3278b160cebf0f6de3a6b42ad5cb3c9c9a30e9a49f9f4cffad16ec926825796941e2d0d99b09fad964a0773
7
+ data.tar.gz: 6b16c0354a7a8bfbef6124186e98d28e183e00e614471543e51acfca01585a44a35f1157b75b815c6bb156ca4be975c39c1eb4ce9d0aa248db4834040023d77c
data/README.md CHANGED
@@ -75,3 +75,34 @@ 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.
102
+
103
+ ## MGET, MSET and DEL
104
+ This gem allows you to use MGET, MSET and DEL specifying multiple keys without a hash tag.
105
+ Cross-slot errors are prevented by an internal dedicated implementation.
106
+ The underlying library makes the behavior possible.
107
+ (ref. [redis-cluster-client](https://github.com/redis-rb/redis-cluster-client))
108
+ That said, we recommend to use a hash tag for these commands to the better performance.
@@ -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?
@@ -98,6 +99,34 @@ class Redis
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,8 +96,33 @@ 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]
99
124
  def watch(*keys, &block)
100
- synchronize { |c| c.call_v([:watch] + keys, &block) }
125
+ synchronize { |c| c.watch(*keys, &block) }
101
126
  end
102
127
 
103
128
  private
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.2.0
4
+ version: 5.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ezra Zygmuntowicz
@@ -13,10 +13,9 @@ authors:
13
13
  - Michel Martens
14
14
  - Damian Janowski
15
15
  - Pieter Noordhuis
16
- autorequire:
17
16
  bindir: bin
18
17
  cert_chain: []
19
- date: 2024-04-15 00:00:00.000000000 Z
18
+ date: 2025-02-20 00:00:00.000000000 Z
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
22
21
  name: redis
@@ -24,28 +23,28 @@ dependencies:
24
23
  requirements:
25
24
  - - '='
26
25
  - !ruby/object:Gem::Version
27
- version: 5.2.0
26
+ version: 5.4.0
28
27
  type: :runtime
29
28
  prerelease: false
30
29
  version_requirements: !ruby/object:Gem::Requirement
31
30
  requirements:
32
31
  - - '='
33
32
  - !ruby/object:Gem::Version
34
- version: 5.2.0
33
+ version: 5.4.0
35
34
  - !ruby/object:Gem::Dependency
36
35
  name: redis-cluster-client
37
36
  requirement: !ruby/object:Gem::Requirement
38
37
  requirements:
39
38
  - - ">="
40
39
  - !ruby/object:Gem::Version
41
- version: 0.7.11
40
+ version: 0.10.0
42
41
  type: :runtime
43
42
  prerelease: false
44
43
  version_requirements: !ruby/object:Gem::Requirement
45
44
  requirements:
46
45
  - - ">="
47
46
  - !ruby/object:Gem::Version
48
- version: 0.7.11
47
+ version: 0.10.0
49
48
  description: |2
50
49
  A Ruby client that tries to match Redis' Cluster API one-to-one, while still
51
50
  providing an idiomatic interface.
@@ -61,6 +60,7 @@ files:
61
60
  - lib/redis-clustering.rb
62
61
  - lib/redis/cluster.rb
63
62
  - lib/redis/cluster/client.rb
63
+ - lib/redis/cluster/transaction_adapter.rb
64
64
  - lib/redis/cluster/version.rb
65
65
  homepage: https://github.com/redis/redis-rb/blob/master/cluster
66
66
  licenses:
@@ -68,10 +68,9 @@ licenses:
68
68
  metadata:
69
69
  bug_tracker_uri: https://github.com/redis/redis-rb/issues
70
70
  changelog_uri: https://github.com/redis/redis-rb/blob/master/cluster/CHANGELOG.md
71
- documentation_uri: https://www.rubydoc.info/gems/redis/5.2.0
71
+ documentation_uri: https://www.rubydoc.info/gems/redis/5.4.0
72
72
  homepage_uri: https://github.com/redis/redis-rb/blob/master/cluster
73
- source_code_uri: https://github.com/redis/redis-rb/tree/v5.2.0/cluster
74
- post_install_message:
73
+ source_code_uri: https://github.com/redis/redis-rb/tree/v5.4.0/cluster
75
74
  rdoc_options: []
76
75
  require_paths:
77
76
  - lib
@@ -86,8 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
86
85
  - !ruby/object:Gem::Version
87
86
  version: '0'
88
87
  requirements: []
89
- rubygems_version: 3.5.3
90
- signing_key:
88
+ rubygems_version: 3.6.2
91
89
  specification_version: 4
92
90
  summary: A Ruby client library for Redis Cluster
93
91
  test_files: []