Repository navigation
perf: buffer small writes on InsertStream - #58
Merged
ShawnChen-Sirius merged 1 commit intoSep 30, 2026
Merged
Conversation
Every io::Write call was going straight to chdb_stream_append, and the engine copies each chunk. With a writeln! per row that is one FFI call (often more) per row. 200k rows took ~385ms that way vs ~37ms through a BufWriter. Writes now get gathered into a 64 KiB buffer and sent when it fills, on flush, or on finish. append() still sends straight away. The size can be changed with with_write_buffer(), and 0 turns buffering off.
ShawnChen-Sirius
approved these changes
Sep 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Writing to an
InsertStreamthroughio::Writewas sending every write straight tochdb_stream_append, and the engine copies each chunk. So the pattern inexamples/14_insert_stream.rs— awriteln!per row — ends up as one FFI call (sometimes a few) per row.I timed it with 200k rows: ~385ms with plain
writeln!, ~37ms with the same thing wrapped in aBufWriter. After this change the plain version is in the same range as theBufWriterone (~50-70ms here).What changed:
io::Writeare gathered into a 64 KiB buffer and sent when it fills, onflush(), or onfinish().append()still sends immediately (it flushes anything buffered first so order is kept).with_write_buffer(bytes)to change the size,0turns buffering off.DEFAULT_WRITE_BUFFERis exported.One behaviour note: since writes are buffered, an error from a bad row can now show up on a later write, on
flush()or onfinish()rather than on the exact write that caused it. Dropping the stream withoutfinish()still cancels it, buffered data included.Added tests for lots of small writes, a write bigger than the buffer, drop after buffered writes, and different buffer sizes including 0.
Note
Buffer small writes in
InsertStreamwith 64 KiB default thresholdstd::io::Writecalls now copy bytes into an in-memory buffer and submit them to the engine only when the threshold is exceeded; writes at or above the threshold go straight through.InsertStream::with_write_bufferto change or disable the threshold (0 disables), and exposesDEFAULT_WRITE_BUFFERfrom the crate root.appendandfinishflush pending buffered bytes before proceeding, preserving write order. A flush failure infinishreturns an error without committing; Drop still cancels.std::io::WriteonInsertStreamno longer submits each call immediately by default. Data now reaches the engine in batches — see insert_stream.rs; the zero-buffer option restores immediate submission.Macroscope summarized 68e5fa2.