redis 4.3.1 → 4.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: 416a2f007042c19453c13361aa4440a507e47fb32c28adc68e7c574c6651f5b4
4
- data.tar.gz: 1a845f2af649d64f8b274962c9d5d10e6eb5d046474b6e44288676432fe8a98b
3
+ metadata.gz: d5ff2ee4b6a6f2b087ac26bf96a3c1769cf42f70ea90008361157f7ef04cdb14
4
+ data.tar.gz: f2c24654294c4fa81a5cff4ddb545bc59e6f85b64ee61273278cb1e286a4edb8
5
5
  SHA512:
6
- metadata.gz: 3766992242ae284ca474bc8564c6760de88e635a8c3bc3c80da08062d698cc891bf00455b5d98768709ecc766f8ad305fe03cc5806f03fda3ebb93049e0a1cce
7
- data.tar.gz: f440c984ec58ff091a6a696952239cb04cf145752b485543e5da7215a327b40be4391b3fe6ca67753f84ec43913b9d90ec0b6f812e1696890a7c17cbf3aa3630
6
+ metadata.gz: 0d88c6621659a178dca04d3ca62f25520f5f1a846b6300f93fb008966551fb4795a3e6c37810f99ea18f77a6292b955fa180ae3e06f8c90df7e6a3ca27087ae1
7
+ data.tar.gz: 1427cc268e867872388f214184d23a5c6062104fcb9d1e50d6026dd9daf6e51b9833866fee9fcf80c7a64e4de705295de8399d8e8ac22fa890bf358bd63ff707
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Unreleased
2
2
 
3
+ # 4.4.0
4
+
5
+ * Redis cluster: fix cross-slot validation in pipelines. Fix ##1019.
6
+ * Add support for `XAUTOCLAIM`. See #1018.
7
+ * Properly issue `READONLY` when reconnecting to replicas. Fix #1017.
8
+ * Make `del` a noop if passed an empty list of keys. See #998.
9
+ * Add support for `ZINTER`. See #995.
10
+
3
11
  # 4.3.1
4
12
 
5
13
  * Fix password authentication against redis server 5 and older.
data/lib/redis.rb CHANGED
@@ -555,6 +555,9 @@ class Redis
555
555
  # @param [String, Array<String>] keys
556
556
  # @return [Integer] number of keys that were deleted
557
557
  def del(*keys)
558
+ keys.flatten!(1)
559
+ return 0 if keys.empty?
560
+
558
561
  synchronize do |client|
559
562
  client.call([:del] + keys)
560
563
  end
@@ -2062,6 +2065,45 @@ class Redis
2062
2065
  end
2063
2066
  end
2064
2067
 
2068
+ # Return the intersection of multiple sorted sets
2069
+ #
2070
+ # @example Retrieve the intersection of `2*zsetA` and `1*zsetB`
2071
+ # redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0])
2072
+ # # => ["v1", "v2"]
2073
+ # @example Retrieve the intersection of `2*zsetA` and `1*zsetB`, and their scores
2074
+ # redis.zinter("zsetA", "zsetB", :weights => [2.0, 1.0], :with_scores => true)
2075
+ # # => [["v1", 3.0], ["v2", 6.0]]
2076
+ #
2077
+ # @param [String, Array<String>] keys one or more keys to intersect
2078
+ # @param [Hash] options
2079
+ # - `:weights => [Float, Float, ...]`: weights to associate with source
2080
+ # sorted sets
2081
+ # - `:aggregate => String`: aggregate function to use (sum, min, max, ...)
2082
+ # - `:with_scores => true`: include scores in output
2083
+ #
2084
+ # @return [Array<String>, Array<[String, Float]>]
2085
+ # - when `:with_scores` is not specified, an array of members
2086
+ # - when `:with_scores` is specified, an array with `[member, score]` pairs
2087
+ def zinter(*keys, weights: nil, aggregate: nil, with_scores: false)
2088
+ args = [:zinter, keys.size, *keys]
2089
+
2090
+ if weights
2091
+ args << "WEIGHTS"
2092
+ args.concat(weights)
2093
+ end
2094
+
2095
+ args << "AGGREGATE" << aggregate if aggregate
2096
+
2097
+ if with_scores
2098
+ args << "WITHSCORES"
2099
+ block = FloatifyPairs
2100
+ end
2101
+
2102
+ synchronize do |client|
2103
+ client.call(args, &block)
2104
+ end
2105
+ end
2106
+
2065
2107
  # Intersect multiple sorted sets and store the resulting sorted set in a new
2066
2108
  # key.
2067
2109
  #
@@ -3238,6 +3280,38 @@ class Redis
3238
3280
  synchronize { |client| client.call(args, &blk) }
3239
3281
  end
3240
3282
 
3283
+ # Transfers ownership of pending stream entries that match the specified criteria.
3284
+ #
3285
+ # @example Claim next pending message stuck > 5 minutes and mark as retry
3286
+ # redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0')
3287
+ # @example Claim 50 next pending messages stuck > 5 minutes and mark as retry
3288
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', count: 50)
3289
+ # @example Claim next pending message stuck > 5 minutes and don't mark as retry
3290
+ # redis.xclaim('mystream', 'mygroup', 'consumer1', 3600000, '0-0', justid: true)
3291
+ # @example Claim next pending message after this id stuck > 5 minutes and mark as retry
3292
+ # redis.xautoclaim('mystream', 'mygroup', 'consumer1', 3600000, '1641321233-0')
3293
+ #
3294
+ # @param key [String] the stream key
3295
+ # @param group [String] the consumer group name
3296
+ # @param consumer [String] the consumer name
3297
+ # @param min_idle_time [Integer] the number of milliseconds
3298
+ # @param start [String] entry id to start scanning from or 0-0 for everything
3299
+ # @param count [Integer] number of messages to claim (default 1)
3300
+ # @param justid [Boolean] whether to fetch just an array of entry ids or not.
3301
+ # Does not increment retry count when true
3302
+ #
3303
+ # @return [Hash{String => Hash}] the entries successfully claimed
3304
+ # @return [Array<String>] the entry ids successfully claimed if justid option is `true`
3305
+ def xautoclaim(key, group, consumer, min_idle_time, start, count: nil, justid: false)
3306
+ args = [:xautoclaim, key, group, consumer, min_idle_time, start]
3307
+ if count
3308
+ args << 'COUNT' << count.to_s
3309
+ end
3310
+ args << 'JUSTID' if justid
3311
+ blk = justid ? HashifyStreamAutoclaimJustId : HashifyStreamAutoclaim
3312
+ synchronize { |client| client.call(args, &blk) }
3313
+ end
3314
+
3241
3315
  # Fetches not acknowledging pending entries
3242
3316
  #
3243
3317
  # @example With key and group
@@ -3448,6 +3522,20 @@ class Redis
3448
3522
  end
3449
3523
  }
3450
3524
 
3525
+ HashifyStreamAutoclaim = lambda { |reply|
3526
+ {
3527
+ 'next' => reply[0],
3528
+ 'entries' => reply[1].map { |entry| [entry[0], entry[1].each_slice(2).to_h] }
3529
+ }
3530
+ }
3531
+
3532
+ HashifyStreamAutoclaimJustId = lambda { |reply|
3533
+ {
3534
+ 'next' => reply[0],
3535
+ 'entries' => reply[1]
3536
+ }
3537
+ }
3538
+
3451
3539
  HashifyStreamPendings = lambda { |reply|
3452
3540
  {
3453
3541
  'size' => reply[0],
data/lib/redis/client.rb CHANGED
@@ -126,6 +126,8 @@ class Redis
126
126
  call [:auth, password]
127
127
  end
128
128
  end
129
+
130
+ call [:readonly] if @options[:readonly]
129
131
  call [:select, db] if db != 0
130
132
  call [:client, :setname, @options[:id]] if @options[:id]
131
133
  @connector.check(self)
data/lib/redis/cluster.rb CHANGED
@@ -78,11 +78,13 @@ class Redis
78
78
  end
79
79
 
80
80
  def call_pipeline(pipeline)
81
- node_keys, command_keys = extract_keys_in_pipeline(pipeline)
82
- raise CrossSlotPipeliningError, command_keys if node_keys.size > 1
81
+ node_keys = pipeline.commands.map { |cmd| find_node_key(cmd, primary_only: true) }.compact.uniq
82
+ if node_keys.size > 1
83
+ raise(CrossSlotPipeliningError,
84
+ pipeline.commands.map { |cmd| @command.extract_first_key(cmd) }.reject(&:empty?).uniq)
85
+ end
83
86
 
84
- node = find_node(node_keys.first)
85
- try_send(node, :call_pipeline, pipeline)
87
+ try_send(find_node(node_keys.first), :call_pipeline, pipeline)
86
88
  end
87
89
 
88
90
  def call_with_timeout(command, timeout, &block)
@@ -253,14 +255,14 @@ class Redis
253
255
  find_node(node_key)
254
256
  end
255
257
 
256
- def find_node_key(command)
258
+ def find_node_key(command, primary_only: false)
257
259
  key = @command.extract_first_key(command)
258
260
  return if key.empty?
259
261
 
260
262
  slot = KeySlotConverter.convert(key)
261
263
  return unless @slot.exists?(slot)
262
264
 
263
- if @command.should_send_to_master?(command)
265
+ if @command.should_send_to_master?(command) || primary_only
264
266
  @slot.find_node_key_of_master(slot)
265
267
  else
266
268
  @slot.find_node_key_of_slave(slot)
@@ -285,11 +287,5 @@ class Redis
285
287
  @node.map(&:disconnect)
286
288
  @node, @slot = fetch_cluster_info!(@option)
287
289
  end
288
-
289
- def extract_keys_in_pipeline(pipeline)
290
- node_keys = pipeline.commands.map { |cmd| find_node_key(cmd) }.compact.uniq
291
- command_keys = pipeline.commands.map { |cmd| @command.extract_first_key(cmd) }.reject(&:empty?)
292
- [node_keys, command_keys]
293
- end
294
290
  end
295
291
  end
@@ -76,8 +76,9 @@ class Redis
76
76
  clients = options.map do |node_key, option|
77
77
  next if replica_disabled? && slave?(node_key)
78
78
 
79
+ option = option.merge(readonly: true) if slave?(node_key)
80
+
79
81
  client = Client.new(option)
80
- client.call(%i[readonly]) if slave?(node_key)
81
82
  [node_key, client]
82
83
  end
83
84
 
@@ -674,6 +674,13 @@ class Redis
674
674
  node_for(key).zcount(key, min, max)
675
675
  end
676
676
 
677
+ # Get the intersection of multiple sorted sets
678
+ def zinter(*keys, **options)
679
+ ensure_same_node(:zinter, keys) do |node|
680
+ node.zinter(*keys, **options)
681
+ end
682
+ end
683
+
677
684
  # Intersect multiple sorted sets and store the resulting sorted set in a new
678
685
  # key.
679
686
  def zinterstore(destination, keys, **options)
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.3.1'
4
+ VERSION = '4.4.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.3.1
4
+ version: 4.4.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: 2021-06-11 00:00:00.000000000 Z
19
+ date: 2021-07-28 00:00:00.000000000 Z
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
22
22
  name: em-synchrony
@@ -102,9 +102,9 @@ 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.3.1
105
+ documentation_uri: https://www.rubydoc.info/gems/redis/4.4.0
106
106
  homepage_uri: https://github.com/redis/redis-rb
107
- source_code_uri: https://github.com/redis/redis-rb/tree/v4.3.1
107
+ source_code_uri: https://github.com/redis/redis-rb/tree/v4.4.0
108
108
  post_install_message:
109
109
  rdoc_options: []
110
110
  require_paths: