Skip to content

Commit ba30d02

Browse files
xautoclaim (#1529)
1 parent 9c60670 commit ba30d02

File tree

2 files changed

+94
-1
lines changed

2 files changed

+94
-1
lines changed

redis/client.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,12 @@ def parse_xclaim(response, **options):
316316
return parse_stream_list(response)
317317

318318

319+
def parse_xautoclaim(response, **options):
320+
if options.get('parse_justid', False):
321+
return response[1]
322+
return parse_stream_list(response[1])
323+
324+
319325
def parse_xinfo_stream(response):
320326
data = pairs_to_dict(response, decode_keys=True)
321327
first = data['first-entry']
@@ -684,6 +690,7 @@ class Redis:
684690
'SSCAN': parse_scan,
685691
'TIME': lambda x: (int(x[0]), int(x[1])),
686692
'XCLAIM': parse_xclaim,
693+
'XAUTOCLAIM': parse_xautoclaim,
687694
'XGROUP CREATE': bool_ok,
688695
'XGROUP DELCONSUMER': int,
689696
'XGROUP DESTROY': bool,
@@ -2601,6 +2608,46 @@ def xadd(self, name, fields, id='*', maxlen=None, approximate=True,
26012608
pieces.extend(pair)
26022609
return self.execute_command('XADD', name, *pieces)
26032610

2611+
def xautoclaim(self, name, groupname, consumername, min_idle_time,
2612+
start_id=0, count=None, justid=False):
2613+
"""
2614+
Transfers ownership of pending stream entries that match the specified
2615+
criteria. Conceptually, equivalent to calling XPENDING and then XCLAIM,
2616+
but provides a more straightforward way to deal with message delivery
2617+
failures via SCAN-like semantics.
2618+
name: name of the stream.
2619+
groupname: name of the consumer group.
2620+
consumername: name of a consumer that claims the message.
2621+
min_idle_time: filter messages that were idle less than this amount of
2622+
milliseconds.
2623+
start_id: filter messages with equal or greater ID.
2624+
count: optional integer, upper limit of the number of entries that the
2625+
command attempts to claim. Set to 100 by default.
2626+
justid: optional boolean, false by default. Return just an array of IDs
2627+
of messages successfully claimed, without returning the actual message
2628+
"""
2629+
try:
2630+
if int(min_idle_time) < 0:
2631+
raise DataError("XAUTOCLAIM min_idle_time must be a non"
2632+
"negative integer")
2633+
except TypeError:
2634+
pass
2635+
2636+
kwargs = {}
2637+
pieces = [name, groupname, consumername, min_idle_time, start_id]
2638+
2639+
try:
2640+
if int(count) < 0:
2641+
raise DataError("XPENDING count must be a integer >= 0")
2642+
pieces.extend([b'COUNT', count])
2643+
except TypeError:
2644+
pass
2645+
if justid:
2646+
pieces.append(b'JUSTID')
2647+
kwargs['parse_justid'] = True
2648+
2649+
return self.execute_command('XAUTOCLAIM', *pieces, **kwargs)
2650+
26042651
def xclaim(self, name, groupname, consumername, min_idle_time, message_ids,
26052652
idle=None, time=None, retrycount=None, force=False,
26062653
justid=False):

tests/test_commands.py

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2374,13 +2374,59 @@ def test_xadd_nomkstream(self, r):
23742374
r.xadd(stream, {'some': 'other'}, nomkstream=True)
23752375
assert r.xlen(stream) == 3
23762376

2377+
@skip_if_server_version_lt('6.2.0')
2378+
def test_xautoclaim(self, r):
2379+
stream = 'stream'
2380+
group = 'group'
2381+
consumer1 = 'consumer1'
2382+
consumer2 = 'consumer2'
2383+
2384+
message_id1 = r.xadd(stream, {'john': 'wick'})
2385+
message_id2 = r.xadd(stream, {'johny': 'deff'})
2386+
message = get_stream_message(r, stream, message_id1)
2387+
r.xgroup_create(stream, group, 0)
2388+
2389+
# trying to claim a message that isn't already pending doesn't
2390+
# do anything
2391+
response = r.xautoclaim(stream, group, consumer2, min_idle_time=0)
2392+
assert response == []
2393+
2394+
# read the group as consumer1 to initially claim the messages
2395+
r.xreadgroup(group, consumer1, streams={stream: '>'})
2396+
2397+
# claim one message as consumer2
2398+
response = r.xautoclaim(stream, group, consumer2,
2399+
min_idle_time=0, count=1)
2400+
assert response == [message]
2401+
2402+
# reclaim the messages as consumer1, but use the justid argument
2403+
# which only returns message ids
2404+
assert r.xautoclaim(stream, group, consumer1, min_idle_time=0,
2405+
start_id=0, justid=True) == \
2406+
[message_id1, message_id2]
2407+
assert r.xautoclaim(stream, group, consumer1, min_idle_time=0,
2408+
start_id=message_id2, justid=True) == \
2409+
[message_id2]
2410+
2411+
@skip_if_server_version_lt('6.2.0')
2412+
def test_xautoclaim_negative(self, r):
2413+
stream = 'stream'
2414+
group = 'group'
2415+
consumer = 'consumer'
2416+
with pytest.raises(redis.DataError):
2417+
r.xautoclaim(stream, group, consumer, min_idle_time=-1)
2418+
with pytest.raises(ValueError):
2419+
r.xautoclaim(stream, group, consumer, min_idle_time="wrong")
2420+
with pytest.raises(redis.DataError):
2421+
r.xautoclaim(stream, group, consumer, min_idle_time=0,
2422+
count=-1)
2423+
23772424
@skip_if_server_version_lt('5.0.0')
23782425
def test_xclaim(self, r):
23792426
stream = 'stream'
23802427
group = 'group'
23812428
consumer1 = 'consumer1'
23822429
consumer2 = 'consumer2'
2383-
23842430
message_id = r.xadd(stream, {'john': 'wick'})
23852431
message = get_stream_message(r, stream, message_id)
23862432
r.xgroup_create(stream, group, 0)

0 commit comments

Comments
 (0)