A read (QUERY /events, GET /projections/{type}/{id}) runs on the read connection pool, sized by
readPoolSize (see
Configuration). It doesn’t go through the
write FIFO.
Consistency comes from SQLite’s WAL mode (MVCC): a read sees a steady snapshot of committed data from the moment it
starts. It never sees a write in progress, and a write never blocks it.
A read that starts just before a commit doesn’t see the new events. That’s fine: a client that then writes uses the
Sequence Position it actually read as afterSequence.
Each read runs in its own read transaction: it reads the store ID first, then the page or the projection.
SQLite takes the snapshot at the first statement, so both come from the same one. A response never pairs the data
of one store ID with another, even if a reset commits in between (see
Store ID header).
For a QUERY /events page, the transaction ends once the page is fully sent.
A QUERY /events page holds its read connection, and pins its snapshot, until the page is fully sent.
Each line of the page gets 30 seconds to go out, renewed on every line. A page that keeps moving is never cut, however
slow the client. A client that stops reading loses its connection after 30 seconds, and the read connection goes
back to the pool.
The client sees a page with no trailer, and resumes like after any dropped connection (see
A page cut short).
Why. Without this limit, readPoolSize stalled clients would block every read, /health included, and keep the
WAL from being checkpointed.