
Redis Stream Claim
CertifiedReclaim abandoned deliveries of a Redis stream consumer group
Redis Stream Claim
Reclaim abandoned deliveries of a Redis stream consumer group
Runs XAUTOCLAIM to take over entries that were delivered to a consumer and never acknowledged, for example because the worker or the execution that read them failed. The entries become pending for consumer, and are returned with their stream name, exact stream ID and field-value map exactly as ReadGroup returns them, so the same flow can process and then Ack them.
Only entries idle for at least minIdleTime are taken. Choose a value longer than the slowest legitimate handler: an entry that is still being processed looks idle to Redis, and taking it makes two workers handle it. The task cannot tell the two cases apart, which is why minIdleTime is required and has no default.
The work is bounded three ways. count is the page size of one XAUTOCLAIM, maxRecords caps how many entries one run takes, and maxPages caps how many pages one run scans. Redis may answer a page with no entry and a cursor other than 0-0, because it inspects a limited number of pending entries per call, so the task keeps following the cursor until it reaches 0-0 or a budget is spent. cursor and completed in the output tell the two apart: pass cursor as startId of a later run to continue the scan.
Taking an entry increments its delivery counter and resets its idle time. Entries that were deleted or trimmed from the stream while pending cannot be delivered; Redis 7.0 or later removes them from the pending list and they are listed in deletedIds instead of being returned as empty entries. Needs Redis 6.2 or later.
type: io.kestra.plugin.redis.stream.ClaimExamples
Take over entries that no worker acknowledged for 10 minutes, process them and acknowledge them.
id: redis_stream_recover
namespace: company.team
tasks:
- id: claim
type: io.kestra.plugin.redis.stream.Claim
url: "{{ secret('REDIS_URI') }}"
key: orders
group: billing
consumer: billing_recovery
minIdleTime: PT10M
maxRecords: 100
- id: process
type: io.kestra.plugin.core.log.Log
message: "recovering {{ outputs.claim.count }} entries: {{ outputs.claim.messages }}"
runIf: "{{ outputs.claim.count > 0 }}"
- id: acknowledge
type: io.kestra.plugin.redis.stream.Ack
url: "{{ secret('REDIS_URI') }}"
key: orders
group: billing
ids: "{{ outputs.claim.ids }}"
runIf: "{{ outputs.claim.count > 0 }}"
Scan a large pending list in several runs by keeping the cursor of the previous run.
id: redis_stream_recover_cursor
namespace: company.team
inputs:
- id: start
type: STRING
defaults: "0-0"
tasks:
- id: claim
type: io.kestra.plugin.redis.stream.Claim
url: "{{ secret('REDIS_URI') }}"
key: orders
group: billing
consumer: billing_recovery
minIdleTime: PT10M
startId: "{{ inputs.start }}"
count: 50
maxPages: 5
fetchType: NONE
- id: report
type: io.kestra.plugin.core.log.Log
message: "claimed {{ outputs.claim.count }}, scan completed: {{ outputs.claim.completed }}, resume from {{ outputs.claim.cursor }}"
Installation
Our standard Docker images already include this plugin. If you use a -slim image or another setup, install it with the following command:
kestractl plugins get io.kestra.plugin:plugin-redis --compatible-for $KESTRA_VERSION For kestractl, use your Kestra version with --compatible-for, or specify an exact plugin version.
For Kestra CLI, replace LATEST with a specific version or version range to pin the plugin version.
Properties
consumer *string
Consumer that takes the entries over
Rendered consumer name. Claimed entries become pending for this consumer, and stay so until they are acknowledged. Use a stable name for the recovery role, not the name of the consumer that lost them.
group *string
Consumer group name
Rendered group name whose pending entries are scanned. The group must already exist.
key *string
Redis stream key
Rendered key passed to XAUTOCLAIM.
minIdleTime *string
Minimum idle time before an entry can be taken
Only pending entries not delivered or claimed for at least this long are taken. Must be strictly positive and is required: choose a value longer than the slowest handler that reads from this group, otherwise an entry that is still being processed is processed a second time. Truncated to whole milliseconds.
url *string
Redis connection string
assets
Assets this task consumes as inputs or produces as outputs, for lineage tracking and the asset graph (Enterprise Edition). A flow declaring this property on a task is rejected in the open-source edition.
io.kestra.core.models.assets.AssetsDeclaration
IGNOREFAILWARNAsset failure behavior
Behavior applied to the task state when a declared asset fails to render, emit, or be persisted (e.g. a lock conflict): FAIL escalates it to FAILED, WARN (default) warns it if it would otherwise succeed, IGNORE leaves the state untouched.
Whether to auto-register assets referenced dynamically at runtime that are not statically declared in inputs or outputs.
The assets consumed as inputs.
io.kestra.core.models.assets.AssetIdentifier
1The assets produced as outputs.
io.kestra.plugin.ee.assets.Dataset
1150{}1150io.kestra.plugin.ee.assets.File
1150{}1150io.kestra.plugin.ee.assets.Table
1150{}1150io.kestra.plugin.ee.assets.VM
1150{}1150io.kestra.core.models.assets.External
1150{}1150io.kestra.core.models.assets.Custom
11501Custom asset type
{}1150count integerstring
100Page size of one scan step
Defaults to 100 and maps to COUNT. Must be between 1 and 10000. Redis inspects a limited number of pending entries per call, so a page can hold fewer entries than this, or none. Lowered to the remaining maxRecords budget so a run never takes more than it can return.
fetchType string
FETCHSTOREFETCHFETCH_ONENONEHow entries are returned
Defaults to FETCH, which returns every taken entry in messages. FETCH_ONE returns a single entry in message and forces maxRecords and count to 1. STORE writes the entries as an ION file to Kestra internal storage and returns its uri. NONE returns no entry body; count, ids and the cursor are still populated.
maxPages integerstring
10Maximum scan steps of one run
Defaults to 10. Must be between 1 and 1000. Each step is one XAUTOCLAIM. This bounds the work when Redis keeps answering with empty pages and a non-final cursor. When it is spent before the cursor reached 0-0, completed is false and cursor is where the next run should continue.
maxRecords integerstring
100Maximum entries taken by one run
Defaults to 100. Must be between 1 and 10000. The run stops as soon as this many entries were taken. Forced to 1 when fetchType is FETCH_ONE, so no entry is taken that the flow cannot see.
startId string
0-0Stream ID to start the scan from
Defaults to 0-0, the start of the pending list. Pass the cursor output of a previous run to continue its scan. Must be 0-0 or a stream ID such as 1700000000000-0.
Outputs
completed boolean
Whether the scan reached the end of the pending list
False when maxRecords or maxPages was spent first; cursor is where to continue.
consumer string
Consumer the entries are now pending for
count integer
Number of entries taken over by this run
cursor string
Where the scan stopped
0-0 when the whole pending list was scanned, otherwise the startId to give the next run.
deletedCount integer
0Number of pending entries that no longer exist in the stream
Total across all scan steps. deletedIds lists the first 1000 only, so a heavily trimmed stream does not flood the output.
deletedIds array
Stream IDs that were pending but no longer exist in the stream
Entries deleted or trimmed while pending. Redis 7.0 or later removes them from the pending list, so they are not returned as entries and need no acknowledgement. Lists at most the first 1000 IDs, see deletedCount for the total. Always empty on older servers.
group string
Consumer group that was scanned
ids array
Exact stream IDs taken over by this run
In delivery order, ready to be passed to Ack. Populated for every fetchType.
message
Single entry taken over by this run
Only set when fetchType is FETCH_ONE; null when nothing was taken.
io.kestra.plugin.redis.stream.ReadGroup-Message
Field-value map of the entry
Returned exactly as stored in Redis, with no serialization format applied.
Exact stream ID of the entry
Pass it to Ack to remove the entry from the group's pending list.
Stream the entry was read from
messages array
Entries taken over by this run
Only set when fetchType is FETCH.
io.kestra.plugin.redis.stream.ReadGroup-Message
Field-value map of the entry
Returned exactly as stored in Redis, with no serialization format applied.
Exact stream ID of the entry
Pass it to Ack to remove the entry from the group's pending list.
Stream the entry was read from
pages integer
Number of scan steps this run needed
stream string
Stream that was scanned
uri string
uriURI of the ION file holding the entries
Only set when fetchType is STORE.
Metrics
claim.entries.count counter
recordsNumber of entries taken over by XAUTOCLAIM.