Skip to content

perf(storage): buffer regular-file PutStream copies - #297

Open
perfloop[bot] wants to merge 4 commits into
mainfrom
perfloop-pr-open-0snqkxfdhm
Open

perfloop[bot] wants to merge 4 commits into
mainfrom
perfloop-pr-open-0snqkxfdhm

Conversation

@perfloop

@perfloop perfloop Bot commented Oct 31, 2026 •

Copy link
Copy Markdown
Contributor

Owner-local embedded.Client.PutStream regular-file remainders now use the existing pooled 1 MiB buffer instead of the file's 32 KiB WriterTo fallback. Other WriterTo readers keep their direct path. Both Storage.Put and Storage.PutIfVersion use that dispatch, without changing versions, checksums, lengths, error cleanup, or sync-before-publication ordering.

A 256 MiB replay measured median caller-thread CPU of 55,000,000 ns/op before and 18,750,000 ns/op after, over 18 complete pairs.

embedded/putstream_regularfile_benchmark_test.go provides the native elapsed-time benchmark. It reuses a file, writes fresh keys in groups of eight, then deletes the keys, closes the client, and removes the directory with timing stopped. Cleanup finishes before another timed group begins. The nine-operation run crosses that boundary:

make test-embedded TESTFLAGS="-run=^NONE -bench=BenchmarkEmbeddedClientPutStreamRegularFile -benchtime=9x -count=1"

The focused Go tests for prefix copying, storage writes, CAS, and embedded APIs passed. storage/prefix_reader_test.go checks payload order, CRC32, length, and copy granularity; lint and dependency-license validation passed.

The replay is single-node and uncapped, with one Go execution thread, default size thresholds, one-hour TTL, a 16 MiB metadata cache, and two RocksDB background jobs. Caller-thread CPU excludes background-worker CPU and fsync waiting. The native benchmark reports elapsed time rather than caller-thread CPU. Production frequency and deployed impact remain unmeasured.

Case and evidence


Made by Perfloop. To ask for a change, comment or review in plain words. How Perfloop handles pull requests.


Note

Medium Risk
Changes large-value copy dispatch for all *os.File inputs on Put/CAS paths; behavior is preserved by tests but touches hot storage I/O.

Overview
Large PutStream / raw-file writes no longer take *os.File's WriteTo path through joinPrefix; regular-file remainders stay reader-only so FileManager copies them with the 1 MiB pooled buffer instead of smaller default file chunks. Other WriterTo bodies (e.g. in-memory unary puts) still use the direct-write wrapper unchanged.

Adds TestJoinPrefixRegularFileUsesPooledBuffer (payload, CRC32, write boundaries, no retained WriterTo) and BenchmarkEmbeddedClientPutStreamRegularFile for owner-local 256 MiB puts with batched cleanup outside the timer.

Reviewed by Cursor Bugbot for commit f7e879d. Bugbot is set up for automated code reviews on this repo. Configure here.

Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Signed-off-by: Perfloop Agent <agent@perfloop.ai>
@greptile-apps

greptile-apps Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; the previous benchmark cleanup issue is fixed.

Summary

File inputs now use the existing pooled copy buffer, while other readers keep their direct-write path.

  • Large regular-file streams copy their remainder through the pooled buffer.

The previous cleanup finding is fully addressed. No new actionable issues were found.

Reviews (2) · Last reviewed commit: "test(storage): reuse the prefix writer f..." · Reviewed by Greptile

Comment on lines +76 to +79
b.StopTimer()
if err := client.Delete(ctx, key); err != nil {
b.Fatal(err)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Cleanup overlaps timed writes

client.Delete only queues the backing file for removal. Stopping the timer around that call does not keep file removal outside later timed PutStream calls. The default worker removes at most 1,000 files each second. If the benchmark writes faster than that, pending files grow and longer runs measure different amounts of background cleanup.

Bound the pending files and finish their cleanup outside the timer so comparisons use a steady workload.

Knowledge Base Used: Validation and performance coverage

Fix in Claude Code

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The benchmark now finishes cleanup before starting another timed batch. Each batch contains at most eight fresh keys; deletion, client shutdown, and storage-directory removal all happen with timing stopped.

Signed-off-by: Perfloop Agent <agent@perfloop.ai>
@perfloop

perfloop Bot commented Nov 4, 2026

Copy link
Copy Markdown
Contributor Author

Perfloop stopped work on this pull request with comments still to address.

Signed-off-by: Perfloop Agent <agent@perfloop.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant