redis 4.2.1 → 4.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: 1e49d4c950b40f5d702b9b49bfc16b6af12dba334f5866dfd44f1ff69af55ecc
4
- data.tar.gz: bf025908a9697cb0308aa3cbce20ecd292a5d9d801c53ad89f739ca5e5beb74f
3
+ metadata.gz: 5d173abb7a6c08e1feb87c9fd5ba33a2a0d907c6d045f6959d55f3234e56ceeb
4
+ data.tar.gz: 7463d58522a3db5262eeea4b95834d82ede95cf102d2375e051cf4ada1b235c3
5
5
  SHA512:
6
- metadata.gz: b27b0178a9120d2843017f5b153dfd48f668ad6b56058bc1058cc318034f1715491cbc9557d71459588b4d3c17970f2572efffd1e79523069ebc4bc208b1c193
7
- data.tar.gz: e6ec5a2f2d49bebdef37531d0292f1076bde4ec81adc190eae122d972bb8b9d46354c78c1fc2d1340d392d5805b42a63409bf6fe4cd020c1176a31ed249bc450
6
+ metadata.gz: 0b34ab14e41e1bd63a99319f3ee5e1e7ea0c1e89581e525dfd82b77968a834525807b025f02d8ff5df4e7a994f19cc7ee4098103c70abd78ab6c22ec435a055c
7
+ data.tar.gz: 84da776467bebb7fd59333084787e484203e55b81be5031c78b44741f2b79342cc1f6f48c1f9663b15f360a3af590783ee2f403da07a3e04486e35ba70e49e01
data/CHANGELOG.md CHANGED
@@ -1,5 +1,29 @@
1
1
  # Unreleased
2
2
 
3
+ # 4.3.0
4
+
5
+ * Add the TYPE argument to scan and scan_each. See #985.
6
+ * Support AUTH command for ACL. See #967.
7
+
8
+ # 4.2.5
9
+
10
+ * Optimize the ruby connector write buffering. See #964.
11
+
12
+ # 4.2.4
13
+
14
+ * Fix bytesize calculations in the ruby connector, and work on a copy of the buffer. Fix #961, #962.
15
+
16
+ # 4.2.3
17
+
18
+ * Use io/wait instead of IO.select in the ruby connector. See #960.
19
+ * Use exception free non blocking IOs in the ruby connector. See #926.
20
+ * Prevent corruption of the client when an interrupt happen during inside a pipeline block. See #945.
21
+
22
+ # 4.2.2
23
+
24
+ * Fix `WATCH` support for `Redis::Distributed`. See #941.
25
+ * Fix handling of empty stream responses. See #905, #929.
26
+
3
27
  # 4.2.1
4
28
 
5
29
  * Fix `exists?` returning an actual boolean when called with multiple keys. See #918.
@@ -17,6 +41,7 @@
17
41
  * Optimized initialization of Redis::Cluster. See #912.
18
42
  * Accept sentinel options even with string key. See #599.
19
43
  * Verify TLS connections by default. See #900.
44
+ * Make `Redis#hset` variadic. It now returns an integer, not a boolean. See #910.
20
45
 
21
46
  # 4.1.4
22
47
 
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # redis-rb [![Build Status][travis-image]][travis-link] [![Inline docs][inchpages-image]][inchpages-link] ![](https://github.com/redis/redis-rb/workflows/Test/badge.svg?branch=master)
1
+ # redis-rb [![Build Status][gh-actions-image]][gh-actions-link] [![Inline docs][inchpages-image]][inchpages-link]
2
2
 
3
3
  A Ruby client that tries to match [Redis][redis-home]' API one-to-one, while still
4
4
  providing an idiomatic interface.
@@ -54,6 +54,12 @@ To connect to a password protected Redis instance, use:
54
54
  redis = Redis.new(password: "mysecret")
55
55
  ```
56
56
 
57
+ To connect a Redis instance using [ACL](https://redis.io/topics/acl), use:
58
+
59
+ ```ruby
60
+ redis = Redis.new(username: 'myname', password: 'mysecret')
61
+ ```
62
+
57
63
  The Redis class exports methods that are named identical to the commands
58
64
  they execute. The arguments these methods accept are often identical to
59
65
  the arguments specified on the [Redis website][redis-commands]. For
@@ -265,6 +271,7 @@ All timeout values are specified in seconds.
265
271
  When using pub/sub, you can subscribe to a channel using a timeout as well:
266
272
 
267
273
  ```ruby
274
+ redis = Redis.new(reconnect_attempts: 0)
268
275
  redis.subscribe_with_timeout(5, "news") do |on|
269
276
  on.message do |channel, message|
270
277
  # ...
@@ -439,7 +446,7 @@ redis = Redis.new(:driver => :synchrony)
439
446
  ## Testing
440
447
 
441
448
  This library is tested against recent Ruby and Redis versions.
442
- Check [Travis][travis-link] for the exact versions supported.
449
+ Check [Github Actions][gh-actions-link] for the exact versions supported.
443
450
 
444
451
  ## See Also
445
452
 
@@ -458,12 +465,11 @@ client and evangelized Redis in Rubyland. Thank you, Ezra.
458
465
  requests.
459
466
 
460
467
 
461
- [inchpages-image]: https://inch-ci.org/github/redis/redis-rb.svg
462
- [inchpages-link]: https://inch-ci.org/github/redis/redis-rb
463
- [redis-commands]: https://redis.io/commands
464
- [redis-home]: https://redis.io
465
- [redis-url]: http://www.iana.org/assignments/uri-schemes/prov/redis
466
- [travis-home]: https://travis-ci.org/
467
- [travis-image]: https://secure.travis-ci.org/redis/redis-rb.svg?branch=master
468
- [travis-link]: https://travis-ci.org/redis/redis-rb
469
- [rubydoc]: http://www.rubydoc.info/gems/redis
468
+ [inchpages-image]: https://inch-ci.org/github/redis/redis-rb.svg
469
+ [inchpages-link]: https://inch-ci.org/github/redis/redis-rb
470
+ [redis-commands]: https://redis.io/commands
471
+ [redis-home]: https://redis.io
472
+ [redis-url]: http://www.iana.org/assignments/uri-schemes/prov/redis
473
+ [gh-actions-image]: https://github.com/redis/redis-rb/workflows/Test/badge.svg
474
+ [gh-actions-link]: https://github.com/redis/redis-rb/actions
475
+ [rubydoc]: http://www.rubydoc.info/gems/redis
data/lib/redis.rb CHANGED
@@ -39,6 +39,7 @@ class Redis
39
39
  # @option options [String] :path path to server socket (overrides host and port)
40
40
  # @option options [Float] :timeout (5.0) timeout in seconds
41
41
  # @option options [Float] :connect_timeout (same as timeout) timeout for initial connect in seconds
42
+ # @option options [String] :username Username to authenticate against server
42
43
  # @option options [String] :password Password to authenticate against server
43
44
  # @option options [Integer] :db (0) Database to select after initial connect
44
45
  # @option options [Symbol] :driver Driver to use, currently supported: `:ruby`, `:hiredis`, `:synchrony`
@@ -143,12 +144,13 @@ class Redis
143
144
 
144
145
  # Authenticate to the server.
145
146
  #
146
- # @param [String] password must match the password specified in the
147
- # `requirepass` directive in the configuration file
147
+ # @param [Array<String>] args includes both username and password
148
+ # or only password
148
149
  # @return [String] `OK`
149
- def auth(password)
150
+ # @see https://redis.io/commands/auth AUTH command
151
+ def auth(*args)
150
152
  synchronize do |client|
151
- client.call([:auth, password])
153
+ client.call([:auth, *args])
152
154
  end
153
155
  end
154
156
 
@@ -2438,14 +2440,13 @@ class Redis
2438
2440
  end
2439
2441
 
2440
2442
  def pipelined
2441
- synchronize do |_client|
2443
+ synchronize do |prior_client|
2442
2444
  begin
2443
- pipeline = Pipeline.new(@client)
2444
- original, @client = @client, pipeline
2445
+ @client = Pipeline.new(prior_client)
2445
2446
  yield(self)
2446
- original.call_pipeline(@client)
2447
+ prior_client.call_pipeline(@client)
2447
2448
  ensure
2448
- @client = original
2449
+ @client = prior_client
2449
2450
  end
2450
2451
  end
2451
2452
  end
@@ -2481,17 +2482,16 @@ class Redis
2481
2482
  # @see #watch
2482
2483
  # @see #unwatch
2483
2484
  def multi
2484
- synchronize do |client|
2485
+ synchronize do |prior_client|
2485
2486
  if !block_given?
2486
- client.call([:multi])
2487
+ prior_client.call([:multi])
2487
2488
  else
2488
2489
  begin
2489
- pipeline = Pipeline::Multi.new(@client)
2490
- original, @client = @client, pipeline
2490
+ @client = Pipeline::Multi.new(prior_client)
2491
2491
  yield(self)
2492
- original.call_pipeline(pipeline)
2492
+ prior_client.call_pipeline(@client)
2493
2493
  ensure
2494
- @client = original
2494
+ @client = prior_client
2495
2495
  end
2496
2496
  end
2497
2497
  end
@@ -2638,12 +2638,13 @@ class Redis
2638
2638
  _eval(:evalsha, args)
2639
2639
  end
2640
2640
 
2641
- def _scan(command, cursor, args, match: nil, count: nil, &block)
2641
+ def _scan(command, cursor, args, match: nil, count: nil, type: nil, &block)
2642
2642
  # SSCAN/ZSCAN/HSCAN already prepend the key to +args+.
2643
2643
 
2644
2644
  args << cursor
2645
2645
  args << "MATCH" << match if match
2646
2646
  args << "COUNT" << count if count
2647
+ args << "TYPE" << type if type
2647
2648
 
2648
2649
  synchronize do |client|
2649
2650
  client.call([command] + args, &block)
@@ -2658,11 +2659,15 @@ class Redis
2658
2659
  # @example Retrieve a batch of keys matching a pattern
2659
2660
  # redis.scan(4, :match => "key:1?")
2660
2661
  # # => ["92", ["key:13", "key:18"]]
2662
+ # @example Retrieve a batch of keys of a certain type
2663
+ # redis.scan(92, :type => "zset")
2664
+ # # => ["173", ["sortedset:14", "sortedset:78"]]
2661
2665
  #
2662
2666
  # @param [String, Integer] cursor the cursor of the iteration
2663
2667
  # @param [Hash] options
2664
2668
  # - `:match => String`: only return keys matching the pattern
2665
2669
  # - `:count => Integer`: return count keys at most per iteration
2670
+ # - `:type => String`: return keys only of the given type
2666
2671
  #
2667
2672
  # @return [String, Array<String>] the next cursor and all found keys
2668
2673
  def scan(cursor, **options)
@@ -2678,10 +2683,15 @@ class Redis
2678
2683
  # redis.scan_each(:match => "key:1?") {|key| puts key}
2679
2684
  # # => key:13
2680
2685
  # # => key:18
2686
+ # @example Execute block for each key of a type
2687
+ # redis.scan_each(:type => "hash") {|key| puts redis.type(key)}
2688
+ # # => "hash"
2689
+ # # => "hash"
2681
2690
  #
2682
2691
  # @param [Hash] options
2683
2692
  # - `:match => String`: only return keys matching the pattern
2684
2693
  # - `:count => Integer`: return count keys at most per iteration
2694
+ # - `:type => String`: return keys only of the given type
2685
2695
  #
2686
2696
  # @return [Enumerator] an enumerator for all found keys
2687
2697
  def scan_each(**options, &block)
@@ -3423,8 +3433,11 @@ class Redis
3423
3433
  end
3424
3434
  }
3425
3435
 
3436
+ EMPTY_STREAM_RESPONSE = [nil].freeze
3437
+ private_constant :EMPTY_STREAM_RESPONSE
3438
+
3426
3439
  HashifyStreamEntries = lambda { |reply|
3427
- reply.map do |entry_id, values|
3440
+ reply.compact.map do |entry_id, values|
3428
3441
  [entry_id, values.each_slice(2).to_h]
3429
3442
  end
3430
3443
  }
data/lib/redis/client.rb CHANGED
@@ -6,13 +6,18 @@ require "cgi"
6
6
 
7
7
  class Redis
8
8
  class Client
9
+ # Defaults are also used for converting string keys to symbols.
9
10
  DEFAULTS = {
10
11
  url: -> { ENV["REDIS_URL"] },
11
12
  scheme: "redis",
12
13
  host: "127.0.0.1",
13
14
  port: 6379,
14
15
  path: nil,
16
+ read_timeout: nil,
17
+ write_timeout: nil,
18
+ connect_timeout: nil,
15
19
  timeout: 5.0,
20
+ username: nil,
16
21
  password: nil,
17
22
  db: 0,
18
23
  driver: nil,
@@ -22,6 +27,7 @@ class Redis
22
27
  reconnect_delay: 0,
23
28
  reconnect_delay_max: 0.5,
24
29
  inherit_socket: false,
30
+ logger: nil,
25
31
  sentinels: nil,
26
32
  role: nil
27
33
  }.freeze
@@ -56,6 +62,10 @@ class Redis
56
62
  @options[:read_timeout]
57
63
  end
58
64
 
65
+ def username
66
+ @options[:username]
67
+ end
68
+
59
69
  def password
60
70
  @options[:password]
61
71
  end
@@ -105,7 +115,7 @@ class Redis
105
115
  # Don't try to reconnect when the connection is fresh
106
116
  with_reconnect(false) do
107
117
  establish_connection
108
- call [:auth, password] if password
118
+ call [:auth, username, password].compact if username || password
109
119
  call [:select, db] if db != 0
110
120
  call [:client, :setname, @options[:id]] if @options[:id]
111
121
  @connector.check(self)
@@ -126,7 +136,7 @@ class Redis
126
136
  reply = process([command]) { read }
127
137
  raise reply if reply.is_a?(CommandError)
128
138
 
129
- if block_given?
139
+ if block_given? && reply != 'QUEUED'
130
140
  yield reply
131
141
  else
132
142
  reply
@@ -429,7 +439,8 @@ class Redis
429
439
  defaults[:scheme] = uri.scheme
430
440
  defaults[:host] = uri.host if uri.host
431
441
  defaults[:port] = uri.port if uri.port
432
- defaults[:password] = CGI.unescape(uri.password) if uri.password
442
+ defaults[:username] = CGI.unescape(uri.user) if uri.user && !uri.user.empty?
443
+ defaults[:password] = CGI.unescape(uri.password) if uri.password && !uri.password.empty?
433
444
  defaults[:db] = uri.path[1..-1].to_i if uri.path
434
445
  defaults[:role] = :master
435
446
  else
@@ -505,7 +516,7 @@ class Redis
505
516
  require_relative "connection/#{driver}"
506
517
  rescue LoadError, NameError
507
518
  begin
508
- require "connection/#{driver}"
519
+ require "redis/connection/#{driver}"
509
520
  rescue LoadError, NameError => error
510
521
  raise "Cannot load driver #{driver.inspect}: #{error.message}"
511
522
  end
@@ -574,6 +585,7 @@ class Redis
574
585
  client = Client.new(@options.merge({
575
586
  host: sentinel[:host] || sentinel["host"],
576
587
  port: sentinel[:port] || sentinel["port"],
588
+ username: sentinel[:username] || sentinel["username"],
577
589
  password: sentinel[:password] || sentinel["password"],
578
590
  reconnect_attempts: 0
579
591
  }))
data/lib/redis/cluster.rb CHANGED
@@ -128,7 +128,7 @@ class Redis
128
128
  def send_command(command, &block)
129
129
  cmd = command.first.to_s.downcase
130
130
  case cmd
131
- when 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
131
+ when 'acl', 'auth', 'bgrewriteaof', 'bgsave', 'quit', 'save'
132
132
  @node.call_all(command, &block).first
133
133
  when 'flushall', 'flushdb'
134
134
  @node.call_master(command, &block).first
@@ -18,6 +18,7 @@ class Redis
18
18
  @node_opts = build_node_options(node_addrs)
19
19
  @replica = options.delete(:replica) == true
20
20
  add_common_node_option_if_needed(options, @node_opts, :scheme)
21
+ add_common_node_option_if_needed(options, @node_opts, :username)
21
22
  add_common_node_option_if_needed(options, @node_opts, :password)
22
23
  @options = options
23
24
  end
@@ -63,7 +64,9 @@ class Redis
63
64
  raise InvalidClientOptionError, "Invalid uri scheme #{addr}" unless VALID_SCHEMES.include?(uri.scheme)
64
65
 
65
66
  db = uri.path.split('/')[1]&.to_i
66
- { scheme: uri.scheme, password: uri.password, host: uri.host, port: uri.port, db: db }.reject { |_, v| v.nil? }
67
+
68
+ { scheme: uri.scheme, username: uri.user, password: uri.password, host: uri.host, port: uri.port, db: db }
69
+ .reject { |_, v| v.nil? || v == '' }
67
70
  rescue URI::InvalidURIError => err
68
71
  raise InvalidClientOptionError, err.message
69
72
  end
@@ -79,7 +82,7 @@ class Redis
79
82
 
80
83
  # Redis cluster node returns only host and port information.
81
84
  # So we should complement additional information such as:
82
- # scheme, password and so on.
85
+ # scheme, username, password and so on.
83
86
  def add_common_node_option_if_needed(options, node_opts, key)
84
87
  return options if options[key].nil? && node_opts.first[key].nil?
85
88
 
@@ -49,57 +49,50 @@ class Redis
49
49
  end
50
50
 
51
51
  def _read_from_socket(nbytes)
52
- begin
53
- read_nonblock(nbytes)
54
- rescue IO::WaitReadable
55
- if IO.select([self], nil, nil, @timeout)
56
- retry
57
- else
58
- raise Redis::TimeoutError
59
- end
60
- rescue IO::WaitWritable
61
- if IO.select(nil, [self], nil, @timeout)
62
- retry
63
- else
64
- raise Redis::TimeoutError
65
- end
66
- end
67
- rescue EOFError
68
- raise Errno::ECONNRESET
69
- end
70
-
71
- def _write_to_socket(data)
72
- begin
73
- write_nonblock(data)
74
- rescue IO::WaitWritable
75
- if IO.select(nil, [self], nil, @write_timeout)
76
- retry
77
- else
78
- raise Redis::TimeoutError
79
- end
80
- rescue IO::WaitReadable
81
- if IO.select([self], nil, nil, @write_timeout)
82
- retry
83
- else
84
- raise Redis::TimeoutError
52
+ loop do
53
+ case chunk = read_nonblock(nbytes, exception: false)
54
+ when :wait_readable
55
+ unless wait_readable(@timeout)
56
+ raise Redis::TimeoutError
57
+ end
58
+ when :wait_writable
59
+ unless wait_writable(@timeout)
60
+ raise Redis::TimeoutError
61
+ end
62
+ when nil
63
+ raise Errno::ECONNRESET
64
+ when String
65
+ return chunk
85
66
  end
86
67
  end
87
- rescue EOFError
88
- raise Errno::ECONNRESET
89
68
  end
90
69
 
91
- def write(data)
92
- return super(data) unless @write_timeout
70
+ def write(buffer)
71
+ return super(buffer) unless @write_timeout
93
72
 
94
- length = data.bytesize
95
- total_count = 0
73
+ bytes_to_write = buffer.bytesize
74
+ total_bytes_written = 0
96
75
  loop do
97
- count = _write_to_socket(data)
76
+ case bytes_written = write_nonblock(buffer, exception: false)
77
+ when :wait_readable
78
+ unless wait_readable(@write_timeout)
79
+ raise Redis::TimeoutError
80
+ end
81
+ when :wait_writable
82
+ unless wait_writable(@write_timeout)
83
+ raise Redis::TimeoutError
84
+ end
85
+ when nil
86
+ raise Errno::ECONNRESET
87
+ when Integer
88
+ total_bytes_written += bytes_written
98
89
 
99
- total_count += count
100
- return total_count if total_count >= length
90
+ if total_bytes_written >= bytes_to_write
91
+ return total_bytes_written
92
+ end
101
93
 
102
- data = data.byteslice(count..-1)
94
+ buffer = buffer.byteslice(bytes_written..-1)
95
+ end
103
96
  end
104
97
  end
105
98
  end
@@ -135,7 +128,7 @@ class Redis
135
128
  raise TimeoutError
136
129
  end
137
130
 
138
- # JRuby raises Errno::EAGAIN on #read_nonblock even when IO.select
131
+ # JRuby raises Errno::EAGAIN on #read_nonblock even when it
139
132
  # says it is readable (1.6.6, in both 1.8 and 1.9 mode).
140
133
  # Use the blocking #readpartial method instead.
141
134
 
@@ -160,7 +153,7 @@ class Redis
160
153
  begin
161
154
  sock.connect_nonblock(sockaddr)
162
155
  rescue Errno::EINPROGRESS
163
- raise TimeoutError if IO.select(nil, [sock], nil, timeout).nil?
156
+ raise TimeoutError unless sock.wait_writable(timeout)
164
157
 
165
158
  begin
166
159
  sock.connect_nonblock(sockaddr)
@@ -215,7 +208,7 @@ class Redis
215
208
  begin
216
209
  sock.connect_nonblock(sockaddr)
217
210
  rescue Errno::EINPROGRESS
218
- raise TimeoutError if IO.select(nil, [sock], nil, timeout).nil?
211
+ raise TimeoutError unless sock.wait_writable(timeout)
219
212
 
220
213
  begin
221
214
  sock.connect_nonblock(sockaddr)
@@ -233,6 +226,18 @@ class Redis
233
226
  class SSLSocket < ::OpenSSL::SSL::SSLSocket
234
227
  include SocketMixin
235
228
 
229
+ unless method_defined?(:wait_readable)
230
+ def wait_readable(timeout = nil)
231
+ to_io.wait_readable(timeout)
232
+ end
233
+ end
234
+
235
+ unless method_defined?(:wait_writable)
236
+ def wait_writable(timeout = nil)
237
+ to_io.wait_writable(timeout)
238
+ end
239
+ end
240
+
236
241
  def self.connect(host, port, timeout, ssl_params)
237
242
  # Note: this is using Redis::Connection::TCPSocket
238
243
  tcp_sock = TCPSocket.connect(host, port, timeout)
@@ -254,13 +259,13 @@ class Redis
254
259
  # Instead, you have to retry.
255
260
  ssl_sock.connect_nonblock
256
261
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable
257
- if IO.select([ssl_sock], nil, nil, timeout)
262
+ if ssl_sock.wait_readable(timeout)
258
263
  retry
259
264
  else
260
265
  raise TimeoutError
261
266
  end
262
267
  rescue IO::WaitWritable
263
- if IO.select(nil, [ssl_sock], nil, timeout)
268
+ if ssl_sock.wait_writable(timeout)
264
269
  retry
265
270
  else
266
271
  raise TimeoutError
@@ -24,10 +24,14 @@ class Redis
24
24
  @default_options = options.dup
25
25
  node_configs.each { |node_config| add_node(node_config) }
26
26
  @subscribed_node = nil
27
+ @watch_key = nil
27
28
  end
28
29
 
29
30
  def node_for(key)
30
- @ring.get_node(key_tag(key.to_s) || key.to_s)
31
+ key = key_tag(key.to_s) || key.to_s
32
+ raise CannotDistribute, :watch if @watch_key && @watch_key != key
33
+
34
+ @ring.get_node(key)
31
35
  end
32
36
 
33
37
  def nodes
@@ -799,13 +803,26 @@ class Redis
799
803
  end
800
804
 
801
805
  # Watch the given keys to determine execution of the MULTI/EXEC block.
802
- def watch(*_keys)
803
- raise CannotDistribute, :watch
806
+ def watch(*keys, &block)
807
+ ensure_same_node(:watch, keys) do |node|
808
+ @watch_key = key_tag(keys.first) || keys.first.to_s
809
+
810
+ begin
811
+ node.watch(*keys, &block)
812
+ rescue StandardError
813
+ @watch_key = nil
814
+ raise
815
+ end
816
+ end
804
817
  end
805
818
 
806
819
  # Forget about all watched keys.
807
820
  def unwatch
808
- raise CannotDistribute, :unwatch
821
+ raise CannotDistribute, :unwatch unless @watch_key
822
+
823
+ result = node_for(@watch_key).unwatch
824
+ @watch_key = nil
825
+ result
809
826
  end
810
827
 
811
828
  def pipelined
@@ -813,18 +830,30 @@ class Redis
813
830
  end
814
831
 
815
832
  # Mark the start of a transaction block.
816
- def multi
817
- raise CannotDistribute, :multi
833
+ def multi(&block)
834
+ raise CannotDistribute, :multi unless @watch_key
835
+
836
+ result = node_for(@watch_key).multi(&block)
837
+ @watch_key = nil if block_given?
838
+ result
818
839
  end
819
840
 
820
841
  # Execute all commands issued after MULTI.
821
842
  def exec
822
- raise CannotDistribute, :exec
843
+ raise CannotDistribute, :exec unless @watch_key
844
+
845
+ result = node_for(@watch_key).exec
846
+ @watch_key = nil
847
+ result
823
848
  end
824
849
 
825
850
  # Discard all commands issued after MULTI.
826
851
  def discard
827
- raise CannotDistribute, :discard
852
+ raise CannotDistribute, :discard unless @watch_key
853
+
854
+ result = node_for(@watch_key).discard
855
+ @watch_key = nil
856
+ result
828
857
  end
829
858
 
830
859
  # Control remote script registry.
data/lib/redis/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Redis
4
- VERSION = '4.2.1'
4
+ VERSION = '4.3.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: redis
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.1
4
+ version: 4.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ezra Zygmuntowicz
@@ -13,10 +13,10 @@ authors:
13
13
  - Michel Martens
14
14
  - Damian Janowski
15
15
  - Pieter Noordhuis
16
- autorequire:
16
+ autorequire:
17
17
  bindir: bin
18
18
  cert_chain: []
19
- date: 2020-06-11 00:00:00.000000000 Z
19
+ date: 2021-06-11 00:00:00.000000000 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  name: em-synchrony
@@ -102,10 +102,10 @@ licenses:
102
102
  metadata:
103
103
  bug_tracker_uri: https://github.com/redis/redis-rb/issues
104
104
  changelog_uri: https://github.com/redis/redis-rb/blob/master/CHANGELOG.md
105
- documentation_uri: https://www.rubydoc.info/gems/redis/4.2.1
105
+ documentation_uri: https://www.rubydoc.info/gems/redis/4.3.0
106
106
  homepage_uri: https://github.com/redis/redis-rb
107
- source_code_uri: https://github.com/redis/redis-rb/tree/v4.2.1
108
- post_install_message:
107
+ source_code_uri: https://github.com/redis/redis-rb/tree/v4.3.0
108
+ post_install_message:
109
109
  rdoc_options: []
110
110
  require_paths:
111
111
  - lib
@@ -120,8 +120,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
120
  - !ruby/object:Gem::Version
121
121
  version: '0'
122
122
  requirements: []
123
- rubygems_version: 3.0.4
124
- signing_key:
123
+ rubygems_version: 3.1.2
124
+ signing_key:
125
125
  specification_version: 4
126
126
  summary: A Ruby client library for Redis
127
127
  test_files: []