XREAD(3valkey)

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

πŸ’– XREAD πŸ’–

okay bestie, welcome to the xread page from the valkey manual ✨ this one is so good, it's giving main character energy for real. here's the whole vibe:

✨ NAME ✨

xread β€” returns messages from multiple streams with IDs greater than the ones requested. blocks until a message is available otherwise. πŸ¦‹


✨ SYNOPSIS ✨

XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key...] ID [ID...]

✨ DESCRIPTION ✨

so basically you read data from one or multiple streams, and it only hands you entries with an ID greater than the last received ID that the caller reported. no cap. this command also has an option to block if items aren't available yet, kind of like how BRPOP or BZPOPMIN and friends do it πŸ’…

quick heads up before you go any further: if you're new to streams, the page literally recommends you read valkey-streams-intro(7), which is the intro to Streams. go read it, it's iconic πŸŽ€

🌸 Non-blocking usage

if you don't use the BLOCK option, the command is synchronous, and you can lowkey think of it as somewhat related to XRANGE: it returns a range of items inside streams. but it has two fundamental differences compared to XRANGE, even when we're just talking synchronous usage:

  • this command can be called with multiple streams if we want to read from a bunch of keys at the same time. this is a key feature of XREAD because, especially when blocking with BLOCK, being able to listen to multiple keys over a single connection is a vital feature. periodt. πŸ’–
  • while XRANGE returns items in a range of IDs, XREAD is more suited to consuming the stream starting from the first entry that's greater than any other entry we saw so far. so what we pass to XREAD is, for each stream, the ID of the last element that we received from that stream. πŸ‘‘

for example, if I have two streams mystream and writers, and I want to read data from both streams starting from the first element they contain, I could call XREAD like in the following example.

Note: we use the COUNT option in the example, so that for each stream the call will return at maximum two elements per stream.

> XREAD COUNT 2 STREAMS mystream writers 0-0 0-0
1) 1) "mystream"
   2) 1) 1) 1526984818136-0
         2) 1) "duration"
            2) "1532"
            3) "event-id"
            4) "5"
            5) "user-id"
            6) "7782813"
      2) 1) 1526999352406-0
         2) 1) "duration"
            2) "812"
            3) "event-id"
            4) "9"
            5) "user-id"
            6) "388234"
2) 1) "writers"
   2) 1) 1) 1526985676425-0
         2) 1) "name"
            2) "Virginia"
            3) "surname"
            4) "Woolf"
      2) 1) 1526985685298-0
         2) 1) "name"
            2) "Jane"
            3) "surname"
            4) "Austen"

the STREAMS option is mandatory and MUST be the final option, because this option takes a variable length of arguments in the following format:

STREAMS key_1 key_2 key_3 ... key_N ID_1 ID_2 ID_3 ... ID_N

so we start with a list of keys, and then continue with all the associated IDs, representing the last ID we received for that stream, so that the call will serve us only greater IDs from the same stream. πŸ’«

for instance in the above example, the last items that we received for the stream mystream has ID 1526999352406-0, while for the stream writers has the ID 1526985685298-0.

to continue iterating the two streams I'll call:

> XREAD COUNT 2 STREAMS mystream writers 1526999352406-0 1526985685298-0
1) 1) "mystream"
   2) 1) 1) 1526999626221-0
         2) 1) "duration"
            2) "911"
            3) "event-id"
            4) "7"
            5) "user-id"
            6) "9488232"
2) 1) "writers"
   2) 1) 1) 1526985691746-0
         2) 1) "name"
            2) "Toni"
            3) "surname"
            4) "Morrison"
      2) 1) 1526985712947-0
         2) 1) "name"
            2) "Agatha"
            3) "surname"
            4) "Christie"

and so forth. eventually, the call will not return any item, but just an empty array β€” that's how we know there's nothing more to fetch from our stream (and we'd have to retry the operation, which is why this command also supports a blocking mode). 😌

🌸 Incomplete IDs

using incomplete IDs is valid, just like it's valid for XRANGE. however here the sequence part of the ID, if missing, is always interpreted as zero, so the command:

> XREAD COUNT 2 STREAMS mystream writers 0 0

is exactly equivalent to

> XREAD COUNT 2 STREAMS mystream writers 0-0 0-0

🌸 Blocking for data

in its synchronous form, the command can get new data as long as there are more items available. however, at some point, we'll have to wait for producers of data to use XADD to push new entries inside the streams we're consuming. to avoid polling at a fixed or adaptive interval, the command is able to block if it could not return any data, according to the specified streams and IDs, and it automatically unblocks once one of the requested keys accepts data. 🧚

it's important to understand that this command fans out to all the clients that are waiting for the same range of IDs, so every consumer will get a copy of the data β€” unlike what happens when blocking list pop operations are used. πŸ’–

to block, the BLOCK option is used, together with the number of milliseconds we want to block before timing out. normally valkey blocking commands take timeouts in seconds, however this command takes a millisecond timeout, even if normally the server will have a timeout resolution near to 0.1 seconds. this time it's possible to block for a shorter time in certain use cases, and if the server internals improve over time, it's possible that the resolution of timeouts will improve. ⏳

when the BLOCK command is passed, but there is data to return in at least one of the streams passed, the command is executed synchronously exactly like if the BLOCK option would be missing.

this is an example of blocking invocation, where the command later returns a null reply because the timeout has elapsed without new data arriving:

> XREAD BLOCK 1000 STREAMS mystream 1526999626221-0
(nil)

🌸 The special $ ID.

when blocking, sometimes we want to receive just entries that are added to the stream via XADD starting from the moment we block. in that case we're not interested in the history of already-added entries. for this use case, we'd have to check the stream top element ID and use that ID in the XREAD command line. this is not clean and requires calling other commands, so instead it's possible to use the special $ ID to signal to the stream that we want only the new things. ✨

it is VERY important to understand that you should use the $ ID only for the first call to XREAD. later the ID should be the one of the last reported item in the stream, otherwise you could miss all the entries that are added in between. 😭

this is how a typical XREAD call looks like in the first iteration of a consumer willing to consume only new entries:

> XREAD BLOCK 5000 COUNT 100 STREAMS mystream $

once we get some replies, the next call will be something like:

> XREAD BLOCK 5000 COUNT 100 STREAMS mystream 1526999644174-3

and so forth.

🌸 How multiple clients blocked on a single stream are served

blocking list operations on lists or sorted sets have a pop behavior. basically, the element is removed from the list or sorted set in order to be returned to the client. in this scenario you want the items to be consumed in a fair way, depending on the moment clients blocked on a given key arrived. normally valkey uses FIFO semantics in these use cases.

however, note that with streams this is not a problem: stream entries are not removed from the stream when clients are served, so every client waiting will be served as soon as an XADD command provides data to the stream. πŸ’…

reading the valkey-streams-intro(7) Streams introduction is highly suggested in order to understand more about the streams' overall behavior and semantics. 🫢


✨ REPLY ✨

🌸 RESP2

one of the following:

  • valkey-protocol(7) Array reply: an array where each element is an array composed of a two elements containing the key name and the entries reported for that key. the entries reported are full stream entries, having IDs and the list of all the fields and values. field and values are guaranteed to be reported in the same order they were added by XADD. 🌈
  • valkey-protocol(7) Nil reply: if the BLOCK option is given and a timeout occurs, or if there is no stream that can be served.

🌸 RESP3

one of the following:

  • valkey-protocol(7) Map reply: a map of key-value elements where each element is composed of the key name and the entries reported for that key. the entries reported are full stream entries, having IDs and the list of all the fields and values. field and values are guaranteed to be reported in the same order they were added by XADD. 🌈
  • valkey-protocol(7) Null reply: if the BLOCK option is given and a timeout occurs, or if there is no stream that can be served.

✨ ACL CATEGORIES ✨

@blocking @read @slow @stream


✨ HISTORY ✨

  • available since: 5.0.0 πŸŽ€

✨ 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), xpending(3valkey), xrange(3valkey), xreadgroup(3valkey), xrevrange(3valkey), xsetid(3valkey), xtrim(3valkey)


and that's the whole page bestie, you ate it πŸ’…βœ¨ this was version 9.0.0, dated 2025-10-21, page XREAD(3valkey) β€” go stream something, literally. πŸ¦‹πŸ’–πŸŽ€

πŸ“¦ where this page comes from