XPENDING(3valkey)

πŸ“¦
version 9.0.6 Β· 2 versions
, in fedora 44

πŸ’– XPENDING β€” bestie, let's see who's ghosting the group πŸ’–

okay so real quick, this is the Valkey Command Manual page for XPENDING, and honestly? it's giving detective energy. it's the command you run when you need to know who's holding a message hostage and hasn't acknowledged it yet. let's get into it πŸ•΅οΈβ™€οΈβœ¨


✨ NAME ✨

xpending β€” Returns the information and entries from a stream consumer group's pending entries list.

like, it tells you what's still pending in a consumer group. that's it. that's the whole job. slay. πŸ’…


🌸 SYNOPSIS 🌸

XPENDING key group [[IDLE min-idle-time] start end count [consumer]]

πŸ¦‹ DESCRIPTION πŸ¦‹

so here's the tea: when you fetch data from a stream through a consumer group and then don't acknowledge it, you've lowkey created a pending entry. this whole situation is explained properly over in the XREADGROUP command page, and even better in our valkey-streams-intro(7) intro to Streams. and then XACK? XACK comes in and immediately removes that pending entry from the Pending Entries List (PEL), because once a message has been successfully processed there's literally no reason for the consumer group to keep tracking it or remembering who currently owns it. bye bestie πŸ‘‹

so XPENDING is the interface you use to inspect the list of pending messages, which makes it a very important command for observing and understanding what's going on with your streams consumer groups: which clients are active, which messages are pending and waiting to be consumed, or whether there are idle messages sitting there collecting dust.

and on top of that, this command β€” together with XCLAIM β€” is what you use to implement recovery of consumers that have been failing for a long time, where as a result certain messages never get processed. a different consumer can claim that message and keep the party going. this is explained better in valkey-streams-intro(7) and on the XCLAIM command page, and we're not covering it here. iykyk.

πŸ’« Summary form of XPENDING

when you call XPENDING with just a key name and a consumer group name, it just outputs a summary about the pending messages in that consumer group. in the following example we create a consumer group and immediately create a pending message by reading from the group with XREADGROUP.

> XGROUP CREATE mystream group55 0-0
OK

> XREADGROUP GROUP group55 consumer-123 COUNT 1 STREAMS mystream >
1) 1) "mystream"
   2) 1) 1) 1526984818136-0
         2) 1) "duration"
            2) "1532"
            3) "event-id"
            4) "5"
            5) "user-id"
            6) "7782813"

so we fully expect the pending entries list for the consumer group group55 to have a message right now: the consumer named consumer-123 fetched the message without acknowledging that it processed it. the simple XPENDING form hands us exactly that info:

> XPENDING mystream group55
1) (integer) 1
2) 1526984818136-0
3) 1526984818136-0
4) 1) 1) "consumer-123"
      2) "1"

in this form, the command outputs:

  • the total number of pending messages for this consumer group β€” which is one
  • followed by the smallest and greatest ID among the pending messages
  • and then it lists every consumer in the consumer group with at least one pending message, plus the number of pending messages that consumer has.

periodt. πŸ’…

πŸ”₯ Extended form of XPENDING

the summary gives you a good overview, but sometimes we want the details, you know? to see all the pending messages with more associated info we need to also pass a range of IDs, in a similar way to how we do it with XRANGE, plus a non optional count argument, to limit the number of messages returned per call:

> XPENDING mystream group55 - + 10
1) 1) 1526984818136-0
   2) "consumer-123"
   3) (integer) 196415
   4) (integer) 1

in the extended form we no longer see the summary info β€” instead we get detailed info for each message in the pending entries list. for each message, four attributes come back:

  1. The ID of the message.
  2. The name of the consumer that fetched the message and still has to acknowledge it. we call this the current owner of the message.
  3. The number of milliseconds that elapsed since the last time this message was delivered to this consumer.
  4. The number of times this message was delivered.

that deliveries counter β€” the fourth element in the array β€” gets incremented when some other consumer claims the message with XCLAIM, or when the message is delivered again via XREADGROUP, when accessing the history of a consumer in a consumer group (check the XREADGROUP page for more info).

it's also possible to pass an additional argument to the command, so you can see messages that have a specific owner:

> XPENDING mystream group55 - + 10 consumer-123

but in the case above the output would be the same, since we only have pending messages for a single consumer. the important thing to keep in mind though? this operation β€” filtering by a specific consumer β€” is not inefficient even when there are a ton of pending messages from a ton of consumers: we have a pending entries list data structure both globally and for every consumer, so we can very efficiently show just the messages pending for a single consumer. no cap. πŸ”₯

πŸŽ€ Idle time filter

you can also filter pending stream entries by their idle-time, given in milliseconds (useful for XCLAIMing entries that haven't been processed for some time):

> XPENDING mystream group55 IDLE 9000 - + 10
> XPENDING mystream group55 IDLE 9000 - + 10 consumer-123

the first case returns the first 10 (or less) PEL entries of the entire group that have been idle for over 9 seconds, whereas in the second case only those of consumer-123. πŸ’–

🧚 Exclusive ranges and iterating the PEL

XPENDING lets you iterate over the pending entries just like XRANGE and XREVRANGE let you iterate over the stream's entries. how? by prefixing the ID of the last-read pending entry with the ( character β€” which denotes an open (exclusive) range β€” and passing it into the subsequent call to the command. that's the whole trick, bestie. ✨


πŸ’œ REPLY πŸ’œ

  • valkey-protocol(7) Array reply: different data depending on the way XPENDING is called, as explained on this page.

⭐ COMPLEXITY ⭐

  • O(N) with N being the number of elements returned, so asking for a small fixed number of entries per call is O(1).
  • O(M), where M is the total number of entries scanned when used with the IDLE filter.
  • when the command returns just the summary and the list of consumers is small, it runs in O(1) time; otherwise, an additional O(N) time for iterating every consumer.

ngl, pretty chill complexity-wise. πŸ‘‘


🌈 ACL CATEGORIES 🌈

@read @slow @stream


πŸ“ HISTORY πŸ“

  • Available since: 5.0.0
  • Changed in 6.2.0: Added the IDLE option and exclusive range intervals.

πŸ’€ SEE ALSO πŸ’€

xack(3valkey), xadd(3valkey), xautoclaim(3valkey), xclaim(3valkey), xdel(3valkey), xgroup(3valkey), xgroup-create(3valkey), xgroup-createconsumer(3valkey), xgroup-delconsumer(3valkey), xgroup-destroy(3valkey), xgroup-help(3valkey), xgroup-setid(3valkey), xinfo(3valkey), xinfo-consumers(3valkey), xinfo-groups(3valkey), xinfo-help(3valkey), xinfo-stream(3valkey), xlen(3valkey), xrange(3valkey), xread(3valkey), xreadgroup(3valkey), xrevrange(3valkey), xsetid(3valkey), xtrim(3valkey)


okay that's the whole page, and before you go β€” this iconic lil manual page is version 9.0.0, dated 2025-10-21. go check on your pending entries, bestie, they're probably waiting on you πŸ«ΆπŸ’–βœ¨

πŸ“¦ where this page comes from