You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 053a9b4
Browse filesBrowse the repository at this point in the historyBrowse files
fix(query)!: stream results instead of buffering them (#300)
* fix(query): stream results instead of buffering them
`hotdata query` held every fetched result in memory: the IPC bytes, the
decoded batches, and a heap-allocated JSON cell per value, all at once.
Peak RSS ran to ~190 bytes per row for a single BIGINT column, so a
10M-row result was SIGKILLed on a 2GB host after minutes of apparent
hanging, leaving an empty file and no error — the kernel kills the
process, so nothing can be printed.
csv and json now render batch by batch as they arrive, so peak memory is
one record batch whatever the result size. Output is unchanged: the
streamed renderers are held byte-for-byte against the buffered ones in
tests, over nulls, embedded commas/quotes/newlines, nested lists and
38-digit decimals. Measured on a 4M-row result: 353MB -> 23MB, and twice
as fast, since a buffered writer replaces a syscall per row.
`-o table` fetches a bounded window with `?limit=`, so the rest is never
downloaded. A window smaller than the result is reported by the existing
incomplete-preview footer and exit code rather than looking complete.
A result the server is still writing is now waited for. Fetching once and
giving up handed back the bounded preview with a non-zero exit while the
real result finished writing a moment later — data loss wearing an exit
code. The wait honours the server's Retry-After and is bounded by the
same 5 minutes the async path already allows.
* chore(deps): take hotdata 0.18.0 from crates.io
* fix(query): derive the table cap from the rows, not a header
`fetch_capped` decided truncation from `total_row_count`, which
`arrow_result_to_query_response` backfills from the rows in hand when the
response carries no `X-Total-Row-Count`. A window then had
total == row_count, so a 10,000-row slice of a million-row result printed
with a plain footer and exit 0 — the silent truncation this command
exists to avoid, reintroduced on the table path.
The fetch now asks for one row past the window. That row's presence is
the proof there is more, needs nothing from the server but the rows, and
is dropped before rendering. The reported total is used only when the
server actually sent one, so an unheadered window shows `?` rather than a
total derived from the window itself.
`stream_json` also wrote `truncated: false` unconditionally while the
process exited non-zero on a short body. The field is written after the
rows, so it now carries what the body turned out to be.
* fix(query): trust the server's next-page link for the table cap
The probe row only proves there is more when `?limit=` is honoured
exactly. A server free to clamp the limit to its own maximum returns the
window full and no further row, so the probe never arrives, and a window
with no X-Total-Row-Count printed as a complete table again — the same
silent truncation through a different door.
`next_link` is the server stating there is another page, and it was
already being constructed and dropped. It now counts alongside the probe
row, so either signal alone is enough to mark the window a preview.
0 commit comments