Skip to content

Commit 7ff90f1

Browse files
authored
Merge pull request #811 from redis/stream-improvements
Stream feature polish
2 parents dde1af7 + 60079e4 commit 7ff90f1

File tree

5 files changed

+144
-132
lines changed

5 files changed

+144
-132
lines changed

.travis.yml

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@ before_install:
99
script: make
1010

1111
rvm:
12-
- 2.2.2
13-
- 2.3.3
14-
- 2.4.1
15-
- 2.5.0
12+
- 2.3.8
13+
- 2.4.5
14+
- 2.5.3
1615
- jruby-9.1.17.0
1716

1817
gemfile: ".travis/Gemfile"
@@ -30,11 +29,10 @@ env:
3029
matrix:
3130
- DRIVER=ruby REDIS_BRANCH=3.0
3231
- DRIVER=ruby REDIS_BRANCH=3.2
33-
- DRIVER=hiredis REDIS_BRANCH=3.0
34-
- DRIVER=hiredis REDIS_BRANCH=3.2
35-
- DRIVER=synchrony REDIS_BRANCH=3.0
36-
- DRIVER=synchrony REDIS_BRANCH=3.2
3732
- DRIVER=ruby REDIS_BRANCH=4.0
33+
- DRIVER=ruby REDIS_BRANCH=5.0
34+
- DRIVER=hiredis REDIS_BRANCH=5.0
35+
- DRIVER=synchrony REDIS_BRANCH=5.0
3836

3937
branches:
4038
only:
@@ -52,8 +50,6 @@ matrix:
5250
env: DRIVER=ruby REDIS_BRANCH=3.2 LOW_TIMEOUT=0.3
5351
- rvm: jruby-9.1.17.0
5452
env: DRIVER=ruby REDIS_BRANCH=4.0 LOW_TIMEOUT=0.3
55-
- rvm: 2.5.3
56-
env: DRIVER=ruby REDIS_BRANCH=5.0
5753

5854
notifications:
5955
irc:

bin/console

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env ruby
2+
3+
$LOAD_PATH.unshift(File.expand_path('../../lib', __FILE__))
4+
require 'redis'
5+
6+
require 'irb'
7+
IRB.start

lib/redis.rb

Lines changed: 61 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -2990,13 +2990,31 @@ def xdel(key, *ids)
29902990
# redis.xrange('mystream', count: 10)
29912991
#
29922992
# @param key [String] the stream key
2993-
# @param first [String] first entry id of range, default value is `-`
2994-
# @param last [String] last entry id of range, default value is `+`
2993+
# @param start [String] first entry id of range, default value is `+`
2994+
# @param end [String] last entry id of range, default value is `-`
29952995
# @param count [Integer] the number of entries as limit
29962996
#
29972997
# @return [Hash{String => Hash}] the entries
2998-
def xrange(key, first: '-', last: '+', count: nil)
2999-
args = [:xrange, key, first, last]
2998+
2999+
# Fetches entries of the stream in ascending order.
3000+
#
3001+
# @example Without options
3002+
# redis.xrange('mystream')
3003+
# @example With a specific start
3004+
# redis.xrange('mystream', '0-1')
3005+
# @example With a specific start and end
3006+
# redis.xrange('mystream', '0-1', '0-3')
3007+
# @example With count options
3008+
# redis.xrange('mystream', count: 10)
3009+
#
3010+
# @param key [String] the stream key
3011+
# @param start [String] first entry id of range, default value is `-`
3012+
# @param end [String] last entry id of range, default value is `+`
3013+
# @param count [Integer] the number of entries as limit
3014+
#
3015+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
3016+
def xrange(key, start = '-', _end = '+', count: nil)
3017+
args = [:xrange, key, start, _end]
30003018
args.concat(['COUNT', count]) if count
30013019
synchronize { |client| client.call(args, &HashifyStreamEntries) }
30023020
end
@@ -3005,21 +3023,21 @@ def xrange(key, first: '-', last: '+', count: nil)
30053023
#
30063024
# @example Without options
30073025
# redis.xrevrange('mystream')
3008-
# @example With first entry id option
3009-
# redis.xrevrange('mystream', first: '0-1')
3010-
# @example With first and last entry id options
3011-
# redis.xrevrange('mystream', first: '0-1', last: '0-3')
3026+
# @example With a specific end
3027+
# redis.xrevrange('mystream', '0-3')
3028+
# @example With a specific end and start
3029+
# redis.xrevrange('mystream', '0-3', '0-1')
30123030
# @example With count options
30133031
# redis.xrevrange('mystream', count: 10)
30143032
#
3015-
# @param key [String] the stream key
3016-
# @param first [String] first entry id of range, default value is `-`
3017-
# @param last [String] last entry id of range, default value is `+`
3018-
# @param count [Integer] the number of entries as limit
3033+
# @param key [String] the stream key
3034+
# @param end [String] first entry id of range, default value is `+`
3035+
# @param start [String] last entry id of range, default value is `-`
3036+
# @params count [Integer] the number of entries as limit
30193037
#
3020-
# @return [Hash{String => Hash}] the entries
3021-
def xrevrange(key, first: '-', last: '+', count: nil)
3022-
args = [:xrevrange, key, last, first]
3038+
# @return [Array<Array<String, Hash>>] the ids and entries pairs
3039+
def xrevrange(key, _end = '+', start = '-', count: nil)
3040+
args = [:xrevrange, key, _end, start]
30233041
args.concat(['COUNT', count]) if count
30243042
synchronize { |client| client.call(args, &HashifyStreamEntries) }
30253043
end
@@ -3055,8 +3073,8 @@ def xlen(key)
30553073
# @return [Hash{String => Hash{String => Hash}}] the entries
30563074
def xread(keys, ids, count: nil, block: nil)
30573075
args = [:xread]
3058-
args.concat(['COUNT', count]) if count
3059-
args.concat(['BLOCK', block.to_i]) if block
3076+
args << 'COUNT' << count if count
3077+
args << 'BLOCK' << block.to_i if block
30603078
_xread(args, keys, ids, block)
30613079
end
30623080

@@ -3113,9 +3131,9 @@ def xgroup(subcommand, key, group, id_or_consumer = nil, mkstream: false)
31133131
# @return [Hash{String => Hash{String => Hash}}] the entries
31143132
def xreadgroup(group, consumer, keys, ids, opts = {})
31153133
args = [:xreadgroup, 'GROUP', group, consumer]
3116-
args.concat(['COUNT', opts[:count]]) if opts[:count]
3117-
args.concat(['BLOCK', opts[:block].to_i]) if opts[:block]
3118-
args << 'NOACK' if opts[:noack]
3134+
args << 'COUNT' << opts[:count] if opts[:count]
3135+
args << 'BLOCK' << opts[:block].to_i if opts[:block]
3136+
args << 'NOACK' if opts[:noack]
31193137
_xread(args, keys, ids, opts[:block])
31203138
end
31213139

@@ -3186,26 +3204,31 @@ def xclaim(key, group, consumer, min_idle_time, *ids, **opts)
31863204
# @example With key and group
31873205
# redis.xpending('mystream', 'mygroup')
31883206
# @example With range options
3189-
# redis.xpending('mystream', 'mygroup', first: '-', last: '+', count: 10)
3207+
# redis.xpending('mystream', 'mygroup', '-', '+', 10)
31903208
# @example With range and consumer options
3191-
# redis.xpending('mystream', 'mygroup', 'consumer1', first: '-', last: '+', count: 10)
3209+
# redis.xpending('mystream', 'mygroup', '-', '+', 10, 'consumer1')
31923210
#
3193-
# @param key [String] the stream key
3194-
# @param group [String] the consumer group name
3195-
# @param consumer [String] the consumer name
3196-
# @param opts [Hash] several options for `XPENDING` command
3197-
#
3198-
# @option opts [String] :first first entry id of range
3199-
# @option opts [String] :last last entry id of range
3200-
# @option opts [Integer] :count the number of entries as limit
3211+
# @param key [String] the stream key
3212+
# @param group [String] the consumer group name
3213+
# @param start [String] start first entry id of range
3214+
# @param end [String] end last entry id of range
3215+
# @param count [Integer] count the number of entries as limit
3216+
# @param consumer [String] the consumer name
32013217
#
32023218
# @return [Hash] the summary of pending entries
32033219
# @return [Array<Hash>] the pending entries details if options were specified
3204-
def xpending(key, group, consumer = nil, **opts)
3205-
args = [:xpending, key, group, opts[:first], opts[:last], opts[:count], consumer].compact
3206-
summary_needed = consumer.nil? && opts.empty?
3220+
def xpending(key, group, *args)
3221+
command_args = [:xpending, key, group]
3222+
case args.size
3223+
when 0, 3, 4
3224+
command_args.concat(args)
3225+
else
3226+
raise ArgumentError, "wrong number of arguments (given #{args.size + 2}, expected 2, 5 or 6)"
3227+
end
3228+
3229+
summary_needed = args.empty?
32073230
blk = summary_needed ? HashifyStreamPendings : HashifyStreamPendingDetails
3208-
synchronize { |client| client.call(args, &blk) }
3231+
synchronize { |client| client.call(command_args, &blk) }
32093232
end
32103233

32113234
# Interact with the sentinel command (masters, master, slaves, failover)
@@ -3365,7 +3388,7 @@ def method_missing(command, *args)
33653388
lambda { |reply|
33663389
reply.map do |entry_id, values|
33673390
[entry_id, values.each_slice(2).to_h]
3368-
end.to_h
3391+
end
33693392
}
33703393

33713394
HashifyStreamPendings =
@@ -3459,7 +3482,9 @@ def _subscription(method, timeout, channels, block)
34593482
def _xread(args, keys, ids, blocking_timeout_msec)
34603483
keys = keys.is_a?(Array) ? keys : [keys]
34613484
ids = ids.is_a?(Array) ? ids : [ids]
3462-
args.concat(['STREAMS'], keys, ids)
3485+
args << 'STREAMS'
3486+
args.concat(keys)
3487+
args.concat(ids)
34633488

34643489
synchronize do |client|
34653490
if blocking_timeout_msec.nil?

test/cluster_commands_on_streams_test.rb

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ def test_xread_with_multiple_keys_and_hash_tags
1919
redis.xadd('{s}1', { f: 'v02' }, id: '0-2')
2020
redis.xadd('{s}2', { f: 'v11' }, id: '1-1')
2121
redis.xadd('{s}2', { f: 'v12' }, id: '1-2')
22+
2223
actual = redis.xread(%w[{s}1 {s}2], %w[0-1 1-1])
23-
assert_equal 1, actual['{s}1'].size
24-
assert_equal 1, actual['{s}2'].size
25-
assert_equal 'v02', actual['{s}1']['0-2']['f']
26-
assert_equal 'v12', actual['{s}2']['1-2']['f']
24+
25+
assert_equal %w(0-2), actual['{s}1'].map(&:first)
26+
assert_equal %w(v02), actual['{s}1'].map { |i| i.last['f'] }
27+
28+
assert_equal %w(1-2), actual['{s}2'].map(&:first)
29+
assert_equal %w(v12), actual['{s}2'].map { |i| i.last['f'] }
2730
end
2831

2932
def test_xreadgroup_with_multiple_keys
@@ -38,10 +41,13 @@ def test_xreadgroup_with_multiple_keys_and_hash_tags
3841
redis.xgroup(:create, '{s}2', 'g1', '$')
3942
redis.xadd('{s}1', { f: 'v02' }, id: '0-2')
4043
redis.xadd('{s}2', { f: 'v12' }, id: '1-2')
44+
4145
actual = redis.xreadgroup('g1', 'c1', %w[{s}1 {s}2], %w[> >])
42-
assert_equal 1, actual['{s}1'].size
43-
assert_equal 1, actual['{s}2'].size
44-
assert_equal 'v02', actual['{s}1']['0-2']['f']
45-
assert_equal 'v12', actual['{s}2']['1-2']['f']
46+
47+
assert_equal %w(0-2), actual['{s}1'].map(&:first)
48+
assert_equal %w(v02), actual['{s}1'].map { |i| i.last['f'] }
49+
50+
assert_equal %w(1-2), actual['{s}2'].map(&:first)
51+
assert_equal %w(v12), actual['{s}2'].map { |i| i.last['f'] }
4652
end
4753
end

0 commit comments

Comments
 (0)