redis 4.8.1 → 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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +82 -0
  3. data/README.md +125 -162
  4. data/lib/redis/client.rb +82 -616
  5. data/lib/redis/commands/bitmaps.rb +14 -4
  6. data/lib/redis/commands/cluster.rb +1 -18
  7. data/lib/redis/commands/connection.rb +5 -10
  8. data/lib/redis/commands/geo.rb +3 -3
  9. data/lib/redis/commands/hashes.rb +13 -6
  10. data/lib/redis/commands/hyper_log_log.rb +1 -1
  11. data/lib/redis/commands/keys.rb +27 -23
  12. data/lib/redis/commands/lists.rb +74 -25
  13. data/lib/redis/commands/pubsub.rb +34 -25
  14. data/lib/redis/commands/server.rb +15 -15
  15. data/lib/redis/commands/sets.rb +35 -40
  16. data/lib/redis/commands/sorted_sets.rb +128 -18
  17. data/lib/redis/commands/streams.rb +48 -21
  18. data/lib/redis/commands/strings.rb +18 -17
  19. data/lib/redis/commands/transactions.rb +7 -31
  20. data/lib/redis/commands.rb +11 -12
  21. data/lib/redis/distributed.rb +136 -72
  22. data/lib/redis/errors.rb +15 -50
  23. data/lib/redis/hash_ring.rb +26 -26
  24. data/lib/redis/pipeline.rb +47 -222
  25. data/lib/redis/subscribe.rb +50 -14
  26. data/lib/redis/version.rb +1 -1
  27. data/lib/redis.rb +77 -184
  28. metadata +10 -57
  29. data/lib/redis/cluster/command.rb +0 -79
  30. data/lib/redis/cluster/command_loader.rb +0 -33
  31. data/lib/redis/cluster/key_slot_converter.rb +0 -72
  32. data/lib/redis/cluster/node.rb +0 -120
  33. data/lib/redis/cluster/node_key.rb +0 -31
  34. data/lib/redis/cluster/node_loader.rb +0 -34
  35. data/lib/redis/cluster/option.rb +0 -100
  36. data/lib/redis/cluster/slot.rb +0 -86
  37. data/lib/redis/cluster/slot_loader.rb +0 -46
  38. data/lib/redis/cluster.rb +0 -315
  39. data/lib/redis/connection/command_helper.rb +0 -41
  40. data/lib/redis/connection/hiredis.rb +0 -68
  41. data/lib/redis/connection/registry.rb +0 -13
  42. data/lib/redis/connection/ruby.rb +0 -437
  43. data/lib/redis/connection/synchrony.rb +0 -148
  44. data/lib/redis/connection.rb +0 -11
@@ -5,48 +5,26 @@ class Redis
5
5
  module Transactions
6
6
  # Mark the start of a transaction block.
7
7
  #
8
- # Passing a block is optional.
9
- #
10
8
  # @example With a block
11
9
  # redis.multi do |multi|
12
10
  # multi.set("key", "value")
13
11
  # multi.incr("counter")
14
12
  # end # => ["OK", 6]
15
13
  #
16
- # @example Without a block
17
- # redis.multi
18
- # # => "OK"
19
- # redis.set("key", "value")
20
- # # => "QUEUED"
21
- # redis.incr("counter")
22
- # # => "QUEUED"
23
- # redis.exec
24
- # # => ["OK", 6]
25
- #
26
14
  # @yield [multi] the commands that are called inside this block are cached
27
15
  # and written to the server upon returning from it
28
16
  # @yieldparam [Redis] multi `self`
29
17
  #
30
- # @return [String, Array<...>]
31
- # - when a block is not given, `OK`
32
- # - when a block is given, an array with replies
18
+ # @return [Array<...>]
19
+ # - an array with replies
33
20
  #
34
21
  # @see #watch
35
22
  # @see #unwatch
36
- def multi(&block) # :nodoc:
37
- if block_given?
38
- if block&.arity == 0
39
- Pipeline.deprecation_warning("multi", Kernel.caller_locations(1, 5))
40
- end
41
-
42
- synchronize do |prior_client|
43
- pipeline = Pipeline::Multi.new(prior_client)
44
- pipelined_connection = PipelinedConnection.new(pipeline)
45
- yield pipelined_connection
46
- prior_client.call_pipeline(pipeline)
23
+ def multi
24
+ synchronize do |client|
25
+ client.multi do |raw_transaction|
26
+ yield MultiConnection.new(raw_transaction)
47
27
  end
48
- else
49
- send_command([:multi])
50
28
  end
51
29
  end
52
30
 
@@ -82,7 +60,7 @@ class Redis
82
60
  # @see #multi
83
61
  def watch(*keys)
84
62
  synchronize do |client|
85
- res = client.call([:watch, *keys])
63
+ res = client.call_v([:watch] + keys)
86
64
 
87
65
  if block_given?
88
66
  begin
@@ -125,8 +103,6 @@ class Redis
125
103
 
126
104
  # Discard all commands issued after MULTI.
127
105
  #
128
- # Only call this method when `#multi` was called **without** a block.
129
- #
130
106
  # @return [String] `"OK"`
131
107
  #
132
108
  # @see #multi
@@ -40,12 +40,7 @@ class Redis
40
40
  # where the method call will return nil. Propagate the nil instead of falsely
41
41
  # returning false.
42
42
  Boolify = lambda { |value|
43
- case value
44
- when Integer
45
- value > 0
46
- else
47
- value
48
- end
43
+ value != 0 unless value.nil?
49
44
  }
50
45
 
51
46
  BoolifySet = lambda { |value|
@@ -88,12 +83,14 @@ class Redis
88
83
  end
89
84
  }
90
85
 
86
+ FloatifyPair = lambda { |(first, score)|
87
+ [first, Floatify.call(score)]
88
+ }
89
+
91
90
  FloatifyPairs = lambda { |value|
92
91
  return value unless value.respond_to?(:each_slice)
93
92
 
94
- value.each_slice(2).map do |member, score|
95
- [member, Floatify.call(score)]
96
- end
93
+ value.each_slice(2).map(&FloatifyPair)
97
94
  }
98
95
 
99
96
  HashifyInfo = lambda { |reply|
@@ -124,7 +121,9 @@ class Redis
124
121
  HashifyStreamAutoclaim = lambda { |reply|
125
122
  {
126
123
  'next' => reply[0],
127
- 'entries' => reply[1].map { |entry| [entry[0], entry[1].each_slice(2).to_h] }
124
+ 'entries' => reply[1].compact.map do |entry, values|
125
+ [entry, values.each_slice(2)&.to_h]
126
+ end
128
127
  }
129
128
  }
130
129
 
@@ -202,8 +201,8 @@ class Redis
202
201
  # hash, are up to consumers.
203
202
  #
204
203
  # Redis error replies are raised as Ruby exceptions.
205
- def call(*command)
206
- send_command(command)
204
+ def call(*command, &block)
205
+ send_command(command, &block)
207
206
  end
208
207
 
209
208
  # Interact with the sentinel command (masters, master, slaves, failover)
@@ -20,7 +20,7 @@ class Redis
20
20
  def initialize(node_configs, options = {})
21
21
  @tag = options[:tag] || /^\{(.+?)\}/
22
22
  @ring = options[:ring] || HashRing.new
23
- @node_configs = node_configs.dup
23
+ @node_configs = node_configs.map(&:dup)
24
24
  @default_options = options.dup
25
25
  node_configs.each { |node_config| add_node(node_config) }
26
26
  @subscribed_node = nil
@@ -41,6 +41,8 @@ class Redis
41
41
  def add_node(options)
42
42
  options = { url: options } if options.is_a?(String)
43
43
  options = @default_options.merge(options)
44
+ options.delete(:tag)
45
+ options.delete(:ring)
44
46
  @ring.add_node Redis.new(options)
45
47
  end
46
48
 
@@ -64,6 +66,10 @@ class Redis
64
66
  on_each_node :quit
65
67
  end
66
68
 
69
+ def close
70
+ on_each_node :close
71
+ end
72
+
67
73
  # Asynchronously save the dataset to disk.
68
74
  def bgsave
69
75
  on_each_node :bgsave
@@ -124,6 +130,11 @@ class Redis
124
130
  node_for(key).expireat(key, unix_time, **kwargs)
125
131
  end
126
132
 
133
+ # Get the expiration for a key as a UNIX timestamp.
134
+ def expiretime(key)
135
+ node_for(key).expiretime(key)
136
+ end
137
+
127
138
  # Get the time to live (in seconds) for a key.
128
139
  def ttl(key)
129
140
  node_for(key).ttl(key)
@@ -139,6 +150,11 @@ class Redis
139
150
  node_for(key).pexpireat(key, ms_unix_time, **kwarg)
140
151
  end
141
152
 
153
+ # Get the expiration for a key as number of milliseconds from UNIX Epoch.
154
+ def pexpiretime(key)
155
+ node_for(key).pexpiretime(key)
156
+ end
157
+
142
158
  # Get the time to live (in milliseconds) for a key.
143
159
  def pttl(key)
144
160
  node_for(key).pttl(key)
@@ -161,6 +177,7 @@ class Redis
161
177
 
162
178
  # Delete a key.
163
179
  def del(*args)
180
+ args.flatten!(1)
164
181
  keys_per_node = args.group_by { |key| node_for(key) }
165
182
  keys_per_node.inject(0) do |sum, (node, keys)|
166
183
  sum + node.del(*keys)
@@ -169,6 +186,7 @@ class Redis
169
186
 
170
187
  # Unlink keys.
171
188
  def unlink(*args)
189
+ args.flatten!(1)
172
190
  keys_per_node = args.group_by { |key| node_for(key) }
173
191
  keys_per_node.inject(0) do |sum, (node, keys)|
174
192
  sum + node.unlink(*keys)
@@ -177,23 +195,16 @@ class Redis
177
195
 
178
196
  # Determine if a key exists.
179
197
  def exists(*args)
180
- if !Redis.exists_returns_integer && args.size == 1
181
- ::Redis.deprecate!(
182
- "`Redis#exists(key)` will return an Integer in redis-rb 4.3, if you want to keep the old behavior, " \
183
- "use `exists?` instead. To opt-in to the new behavior now you can set Redis.exists_returns_integer = true. " \
184
- "(#{::Kernel.caller(1, 1).first})\n"
185
- )
186
- exists?(*args)
187
- else
188
- keys_per_node = args.group_by { |key| node_for(key) }
189
- keys_per_node.inject(0) do |sum, (node, keys)|
190
- sum + node._exists(*keys)
191
- end
198
+ args.flatten!(1)
199
+ keys_per_node = args.group_by { |key| node_for(key) }
200
+ keys_per_node.inject(0) do |sum, (node, keys)|
201
+ sum + node.exists(*keys)
192
202
  end
193
203
  end
194
204
 
195
205
  # Determine if any of the keys exists.
196
206
  def exists?(*args)
207
+ args.flatten!(1)
197
208
  keys_per_node = args.group_by { |key| node_for(key) }
198
209
  keys_per_node.each do |node, keys|
199
210
  return true if node.exists?(*keys)
@@ -297,7 +308,7 @@ class Redis
297
308
  end
298
309
 
299
310
  # Set multiple keys to multiple values.
300
- def mset(*_args)
311
+ def mset(*)
301
312
  raise CannotDistribute, :mset
302
313
  end
303
314
 
@@ -306,7 +317,7 @@ class Redis
306
317
  end
307
318
 
308
319
  # Set multiple keys to multiple values, only if none of the keys exist.
309
- def msetnx(*_args)
320
+ def msetnx(*)
310
321
  raise CannotDistribute, :msetnx
311
322
  end
312
323
 
@@ -331,11 +342,13 @@ class Redis
331
342
 
332
343
  # Get the values of all the given keys as an Array.
333
344
  def mget(*keys)
345
+ keys.flatten!(1)
334
346
  mapped_mget(*keys).values_at(*keys)
335
347
  end
336
348
 
337
349
  # Get the values of all the given keys as a Hash.
338
350
  def mapped_mget(*keys)
351
+ keys.flatten!(1)
339
352
  keys.group_by { |k| node_for k }.inject({}) do |results, (node, subkeys)|
340
353
  results.merge! node.mapped_mget(*subkeys)
341
354
  end
@@ -367,20 +380,21 @@ class Redis
367
380
  end
368
381
 
369
382
  # Count the number of set bits in a range of the string value stored at key.
370
- def bitcount(key, start = 0, stop = -1)
371
- node_for(key).bitcount(key, start, stop)
383
+ def bitcount(key, start = 0, stop = -1, scale: nil)
384
+ node_for(key).bitcount(key, start, stop, scale: scale)
372
385
  end
373
386
 
374
387
  # Perform a bitwise operation between strings and store the resulting string in a key.
375
388
  def bitop(operation, destkey, *keys)
389
+ keys.flatten!(1)
376
390
  ensure_same_node(:bitop, [destkey] + keys) do |node|
377
- node.bitop(operation, destkey, *keys)
391
+ node.bitop(operation, destkey, keys)
378
392
  end
379
393
  end
380
394
 
381
395
  # Return the position of the first bit set to 1 or 0 in a string.
382
- def bitpos(key, bit, start = nil, stop = nil)
383
- node_for(key).bitpos(key, bit, start, stop)
396
+ def bitpos(key, bit, start = nil, stop = nil, scale: nil)
397
+ node_for(key).bitpos(key, bit, start, stop, scale: scale)
384
398
  end
385
399
 
386
400
  # Set the string value of a key and return its old value.
@@ -463,23 +477,15 @@ class Redis
463
477
  timeout = if args.last.is_a?(Hash)
464
478
  options = args.pop
465
479
  options[:timeout]
466
- elsif args.last.respond_to?(:to_int)
467
- last_arg = args.pop
468
- ::Redis.deprecate!(
469
- "Passing the timeout as a positional argument is deprecated, it should be passed as a keyword argument:\n" \
470
- " redis.#{cmd}(#{args.map(&:inspect).join(', ')}, timeout: #{last_arg.to_int})" \
471
- "(called from: #{caller(2, 1).first})"
472
- )
473
- last_arg.to_int
474
480
  end
475
481
 
476
- keys = args.flatten
482
+ args.flatten!(1)
477
483
 
478
- ensure_same_node(cmd, keys) do |node|
484
+ ensure_same_node(cmd, args) do |node|
479
485
  if timeout
480
- node.__send__(cmd, keys, timeout: timeout)
486
+ node.__send__(cmd, args, timeout: timeout)
481
487
  else
482
- node.__send__(cmd, keys)
488
+ node.__send__(cmd, args)
483
489
  end
484
490
  end
485
491
  end
@@ -490,6 +496,18 @@ class Redis
490
496
  _bpop(:blpop, args)
491
497
  end
492
498
 
499
+ def bzpopmax(*args)
500
+ _bpop(:bzpopmax, args) do |reply|
501
+ reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
502
+ end
503
+ end
504
+
505
+ def bzpopmin(*args)
506
+ _bpop(:bzpopmin, args) do |reply|
507
+ reply.is_a?(Array) ? [reply[0], reply[1], Floatify.call(reply[2])] : reply
508
+ end
509
+ end
510
+
493
511
  # Remove and get the last element in a list, or block until one is
494
512
  # available.
495
513
  def brpop(*args)
@@ -498,9 +516,9 @@ class Redis
498
516
 
499
517
  # Pop a value from a list, push it to another list and return it; or block
500
518
  # until one is available.
501
- def brpoplpush(source, destination, deprecated_timeout = 0, **options)
519
+ def brpoplpush(source, destination, **options)
502
520
  ensure_same_node(:brpoplpush, [source, destination]) do |node|
503
- node.brpoplpush(source, destination, deprecated_timeout, **options)
521
+ node.brpoplpush(source, destination, **options)
504
522
  end
505
523
  end
506
524
 
@@ -534,29 +552,43 @@ class Redis
534
552
  node_for(key).ltrim(key, start, stop)
535
553
  end
536
554
 
555
+ # Iterate over keys, blocking and removing elements from the first non empty liist found.
556
+ def blmpop(timeout, *keys, modifier: "LEFT", count: nil)
557
+ ensure_same_node(:blmpop, keys) do |node|
558
+ node.blmpop(timeout, *keys, modifier: modifier, count: count)
559
+ end
560
+ end
561
+
562
+ # Iterate over keys, removing elements from the first non list found.
563
+ def lmpop(*keys, modifier: "LEFT", count: nil)
564
+ ensure_same_node(:lmpop, keys) do |node|
565
+ node.lmpop(*keys, modifier: modifier, count: count)
566
+ end
567
+ end
568
+
537
569
  # Get the number of members in a set.
538
570
  def scard(key)
539
571
  node_for(key).scard(key)
540
572
  end
541
573
 
542
574
  # Add one or more members to a set.
543
- def sadd(key, member)
544
- node_for(key).sadd(key, member)
575
+ def sadd(key, *members)
576
+ node_for(key).sadd(key, *members)
545
577
  end
546
578
 
547
579
  # Add one or more members to a set.
548
- def sadd?(key, member)
549
- node_for(key).sadd?(key, member)
580
+ def sadd?(key, *members)
581
+ node_for(key).sadd?(key, *members)
550
582
  end
551
583
 
552
584
  # Remove one or more members from a set.
553
- def srem(key, member)
554
- node_for(key).srem(key, member)
585
+ def srem(key, *members)
586
+ node_for(key).srem(key, *members)
555
587
  end
556
588
 
557
589
  # Remove one or more members from a set.
558
- def srem?(key, member)
559
- node_for(key).srem?(key, member)
590
+ def srem?(key, *members)
591
+ node_for(key).srem?(key, *members)
560
592
  end
561
593
 
562
594
  # Remove and return a random member from a set.
@@ -603,43 +635,49 @@ class Redis
603
635
 
604
636
  # Subtract multiple sets.
605
637
  def sdiff(*keys)
638
+ keys.flatten!(1)
606
639
  ensure_same_node(:sdiff, keys) do |node|
607
- node.sdiff(*keys)
640
+ node.sdiff(keys)
608
641
  end
609
642
  end
610
643
 
611
644
  # Subtract multiple sets and store the resulting set in a key.
612
645
  def sdiffstore(destination, *keys)
613
- ensure_same_node(:sdiffstore, [destination] + keys) do |node|
614
- node.sdiffstore(destination, *keys)
646
+ keys.flatten!(1)
647
+ ensure_same_node(:sdiffstore, [destination].concat(keys)) do |node|
648
+ node.sdiffstore(destination, keys)
615
649
  end
616
650
  end
617
651
 
618
652
  # Intersect multiple sets.
619
653
  def sinter(*keys)
654
+ keys.flatten!(1)
620
655
  ensure_same_node(:sinter, keys) do |node|
621
- node.sinter(*keys)
656
+ node.sinter(keys)
622
657
  end
623
658
  end
624
659
 
625
660
  # Intersect multiple sets and store the resulting set in a key.
626
661
  def sinterstore(destination, *keys)
627
- ensure_same_node(:sinterstore, [destination] + keys) do |node|
628
- node.sinterstore(destination, *keys)
662
+ keys.flatten!(1)
663
+ ensure_same_node(:sinterstore, [destination].concat(keys)) do |node|
664
+ node.sinterstore(destination, keys)
629
665
  end
630
666
  end
631
667
 
632
668
  # Add multiple sets.
633
669
  def sunion(*keys)
670
+ keys.flatten!(1)
634
671
  ensure_same_node(:sunion, keys) do |node|
635
- node.sunion(*keys)
672
+ node.sunion(keys)
636
673
  end
637
674
  end
638
675
 
639
676
  # Add multiple sets and store the resulting set in a key.
640
677
  def sunionstore(destination, *keys)
641
- ensure_same_node(:sunionstore, [destination] + keys) do |node|
642
- node.sunionstore(destination, *keys)
678
+ keys.flatten!(1)
679
+ ensure_same_node(:sunionstore, [destination].concat(keys)) do |node|
680
+ node.sunionstore(destination, keys)
643
681
  end
644
682
  end
645
683
 
@@ -680,6 +718,20 @@ class Redis
680
718
  node_for(key).zmscore(key, *members)
681
719
  end
682
720
 
721
+ # Iterate over keys, blocking and removing members from the first non empty sorted set found.
722
+ def bzmpop(timeout, *keys, modifier: "MIN", count: nil)
723
+ ensure_same_node(:bzmpop, keys) do |node|
724
+ node.bzmpop(timeout, *keys, modifier: modifier, count: count)
725
+ end
726
+ end
727
+
728
+ # Iterate over keys, removing members from the first non empty sorted set found.
729
+ def zmpop(*keys, modifier: "MIN", count: nil)
730
+ ensure_same_node(:zmpop, keys) do |node|
731
+ node.zmpop(*keys, modifier: modifier, count: count)
732
+ end
733
+ end
734
+
683
735
  # Return a range of members in a sorted set, by index, score or lexicographical ordering.
684
736
  def zrange(key, start, stop, **options)
685
737
  node_for(key).zrange(key, start, stop, **options)
@@ -700,14 +752,14 @@ class Redis
700
752
  end
701
753
 
702
754
  # Determine the index of a member in a sorted set.
703
- def zrank(key, member)
704
- node_for(key).zrank(key, member)
755
+ def zrank(key, member, **options)
756
+ node_for(key).zrank(key, member, **options)
705
757
  end
706
758
 
707
759
  # Determine the index of a member in a sorted set, with scores ordered from
708
760
  # high to low.
709
- def zrevrank(key, member)
710
- node_for(key).zrevrank(key, member)
761
+ def zrevrank(key, member, **options)
762
+ node_for(key).zrevrank(key, member, **options)
711
763
  end
712
764
 
713
765
  # Remove all members in a sorted set within the given indexes.
@@ -738,43 +790,49 @@ class Redis
738
790
 
739
791
  # Get the intersection of multiple sorted sets
740
792
  def zinter(*keys, **options)
793
+ keys.flatten!(1)
741
794
  ensure_same_node(:zinter, keys) do |node|
742
- node.zinter(*keys, **options)
795
+ node.zinter(keys, **options)
743
796
  end
744
797
  end
745
798
 
746
799
  # Intersect multiple sorted sets and store the resulting sorted set in a new
747
800
  # key.
748
- def zinterstore(destination, keys, **options)
749
- ensure_same_node(:zinterstore, [destination] + keys) do |node|
801
+ def zinterstore(destination, *keys, **options)
802
+ keys.flatten!(1)
803
+ ensure_same_node(:zinterstore, [destination].concat(keys)) do |node|
750
804
  node.zinterstore(destination, keys, **options)
751
805
  end
752
806
  end
753
807
 
754
808
  # Return the union of multiple sorted sets.
755
809
  def zunion(*keys, **options)
810
+ keys.flatten!(1)
756
811
  ensure_same_node(:zunion, keys) do |node|
757
- node.zunion(*keys, **options)
812
+ node.zunion(keys, **options)
758
813
  end
759
814
  end
760
815
 
761
816
  # Add multiple sorted sets and store the resulting sorted set in a new key.
762
- def zunionstore(destination, keys, **options)
763
- ensure_same_node(:zunionstore, [destination] + keys) do |node|
817
+ def zunionstore(destination, *keys, **options)
818
+ keys.flatten!(1)
819
+ ensure_same_node(:zunionstore, [destination].concat(keys)) do |node|
764
820
  node.zunionstore(destination, keys, **options)
765
821
  end
766
822
  end
767
823
 
768
824
  # Return the difference between the first and all successive input sorted sets.
769
825
  def zdiff(*keys, **options)
826
+ keys.flatten!(1)
770
827
  ensure_same_node(:zdiff, keys) do |node|
771
- node.zdiff(*keys, **options)
828
+ node.zdiff(keys, **options)
772
829
  end
773
830
  end
774
831
 
775
832
  # Compute the difference between the first and all successive input sorted sets
776
833
  # and store the resulting sorted set in a new key.
777
- def zdiffstore(destination, keys, **options)
834
+ def zdiffstore(destination, *keys, **options)
835
+ keys.flatten!(1)
778
836
  ensure_same_node(:zdiffstore, [destination] + keys) do |node|
779
837
  node.zdiffstore(destination, keys, **options)
780
838
  end
@@ -801,7 +859,7 @@ class Redis
801
859
  end
802
860
 
803
861
  def mapped_hmset(key, hash)
804
- node_for(key).hmset(key, *hash.to_a.flatten)
862
+ node_for(key).hmset(key, hash)
805
863
  end
806
864
 
807
865
  # Get the value of a hash field.
@@ -811,11 +869,13 @@ class Redis
811
869
 
812
870
  # Get the values of all the given hash fields.
813
871
  def hmget(key, *fields)
814
- node_for(key).hmget(key, *fields)
872
+ fields.flatten!(1)
873
+ node_for(key).hmget(key, fields)
815
874
  end
816
875
 
817
876
  def mapped_hmget(key, *fields)
818
- Hash[*fields.zip(hmget(key, *fields)).flatten]
877
+ fields.flatten!(1)
878
+ node_for(key).mapped_hmget(key, fields)
819
879
  end
820
880
 
821
881
  def hrandfield(key, count = nil, **options)
@@ -824,7 +884,8 @@ class Redis
824
884
 
825
885
  # Delete one or more hash fields.
826
886
  def hdel(key, *fields)
827
- node_for(key).hdel(key, *fields)
887
+ fields.flatten!(1)
888
+ node_for(key).hdel(key, fields)
828
889
  end
829
890
 
830
891
  # Determine if a hash field exists.
@@ -881,18 +942,22 @@ class Redis
881
942
 
882
943
  # Stop listening for messages posted to the given channels.
883
944
  def unsubscribe(*channels)
884
- raise "Can't unsubscribe if not subscribed." unless subscribed?
945
+ raise SubscriptionError, "Can't unsubscribe if not subscribed." unless subscribed?
885
946
 
886
947
  @subscribed_node.unsubscribe(*channels)
887
948
  end
888
949
 
889
950
  # Listen for messages published to channels matching the given patterns.
951
+ # See the [Redis Server PSUBSCRIBE documentation](https://redis.io/docs/latest/commands/psubscribe/)
952
+ # for further details
890
953
  def psubscribe(*channels, &block)
891
954
  raise NotImplementedError
892
955
  end
893
956
 
894
957
  # Stop listening for messages posted to channels matching the given
895
958
  # patterns.
959
+ # See the [Redis Server PUNSUBSCRIBE documentation](https://redis.io/docs/latest/commands/punsubscribe/)
960
+ # for further details
896
961
  def punsubscribe(*channels)
897
962
  raise NotImplementedError
898
963
  end
@@ -928,9 +993,7 @@ class Redis
928
993
  def multi(&block)
929
994
  raise CannotDistribute, :multi unless @watch_key
930
995
 
931
- result = node_for(@watch_key).multi(&block)
932
- @watch_key = nil if block_given?
933
- result
996
+ node_for(@watch_key).multi(&block)
934
997
  end
935
998
 
936
999
  # Execute all commands issued after MULTI.
@@ -1020,7 +1083,8 @@ class Redis
1020
1083
  end
1021
1084
 
1022
1085
  def key_tag(key)
1023
- key.to_s[@tag, 1] if @tag
1086
+ key = key.to_s
1087
+ key[@tag, 1] if key.match?(@tag)
1024
1088
  end
1025
1089
 
1026
1090
  def ensure_same_node(command, keys)