Introduction
When a consumer reads a message via XREADGROUP, Redis adds it to the Pending Entries List (PEL) — a per-group tracking structure. The message stays there until explicitly acknowledged.
Key Concepts
- PEL (Pending Entries List): per-group structure tracking all delivered-but-unacknowledged messages with consumer name, idle time, and delivery count
- XACK: removes one or more entries from the PEL, signaling successful processing
- At-least-once delivery: because messages stay in the PEL until acknowledged, a crashed consumer's messages can be reclaimed and reprocessed
- Idempotency: processing the same message twice must produce the same result
Real World Context
Payment processing: after charging a customer, call XACK. If the worker crashes before XACK, another worker reclaims the message. Since the payment API is called again, idempotency keys prevent a double charge.
Deep Dive
The PEL tracks:
- Which messages have been delivered
- Which consumer has each message
- When each message was last delivered
- How many times each message has been delivered
bash# After successfully processing a message: XACK orders processors 1691234567890-0 # => (integer) 1 <- number of entries acknowledged # Acknowledge multiple at once XACK orders processors 1691234567890-0 1691234567891-0 1691234567892-0 # Check pending messages XPENDING orders processors - + 10
Once acknowledged, the entry is removed from the PEL (but remains in the stream itself until trimmed).
Common Pitfalls
- Calling XACK before processing is complete: if the process crashes after XACK but before finishing, the message is lost
- Not calling XACK on error: messages accumulate in the PEL indefinitely; build a dead-letter pattern instead
- Batch-acknowledging without processing: acknowledging messages you haven't processed defeats at-least-once delivery
Best Practices
- Call XACK only after all side effects of processing are complete and durable
- On processing failure, do NOT acknowledge; let the PEL accumulate idle time so XCLAIM can recover the message
- Monitor
XPENDINGregularly to detect consumers that are not acknowledging messages
Summary
XACK removes a message from the PEL, signaling successful processing. Unacknowledged messages remain tracked and can be reclaimed. The combination of PEL + XACK gives Redis Streams at-least-once delivery semantics.
Code Examples
bash
# Read a message
XREADGROUP GROUP processors worker-1 COUNT 1 STREAMS orders >
# => 1) 1) "orders"
# 2) 1) 1) "1691234567890-0"
# 2) 1) "status" 2) "pending"
# Process it... then acknowledge
XACK orders processors 1691234567890-0
# => (integer) 1
# Check what's still pending
XPENDING orders processors - + 10