Skip to main content

Module stream_tail

Module stream_tail 

Source
Expand description

Direct XREAD / XREAD BLOCK tail for attempt-scoped streams.

XREAD BLOCK cannot run inside a Valkey Function (blocking commands are rejected by FUNCTION), so tailing is implemented as a direct client command against the same ferriskey::Client the server already holds.

Semantics:

  • block_ms == 0 → non-blocking XREAD (returns immediately)
  • block_ms > 0XREAD BLOCK <ms> (returns nil on timeout → empty result)

Cluster-safe: the stream key carries the execution’s {p:N} hash tag, so XREAD routes to the single owning slot.

§Timeout interaction with the ferriskey client

The client’s configured request_timeout (5s on the server default) does NOT cap blocking tail calls. ferriskey::client::get_request_timeout inspects every outgoing cmd; for XREAD/XREADGROUP with a BLOCK argument, it returns RequestTimeoutOption::BlockingCommand(block_ms + 500ms) via BLOCKING_CMD_TIMEOUT_EXTENSION. That means a block_ms = 30_000 call gets a 30_500ms effective timeout regardless of the client’s default. No manual override is needed from this module.

§Terminal signal (closed-stream propagation)

After every XREAD/XREAD BLOCK we HMGET the sibling stream_meta hash to learn whether the producer has closed the stream (closed_at / closed_reason). This is the terminal-signal contract described in RFC-006 — consumers poll tail_stream until is_closed() is true, then drain any remaining frames.

§Atomicity (XREAD vs HMGET)

The XREAD and the follow-up HMGET are separate Valkey round trips. A close can land between them, so a caller can observe a result that was not simultaneously true at any single instant: frames reflect the stream as of T1, closed_at/closed_reason reflect stream_meta as of T2 > T1.

This is correctness-safe. ff_append_frame gates on closed_at (see lua/stream.lua), so once the stream is closed no new frames can be appended — any frames returned by XREAD are guaranteed to be from before the close. The terminal signal arriving “slightly later” just means a tail loop may take one extra iteration to observe the closure. Consumers should treat closed_at.is_some() as the exit condition and perform a final read_attempt_stream drain from last_seen_id to + to pick up any frames that landed before close but after the tail’s last read — see RFC-006 Impl Notes §“Terminal-signal drain pattern”.

Functions§

xread_block
Tail frames from stream_key, blocking up to block_ms (0 = no block).