perf(serialization): optimize buffered tail reads - #11209
Merged
ReubenBond merged 1 commit intoSep 8, 2026
Merged
ReubenBond merged 1 commit into
ReubenBond merged 1 commit into
Conversation
Contributor
There was a problem hiding this comment.
Copilot review overview
🟢 Approval recommended
The change is narrowly scoped to an internal fast path, preserves pinned-slice behavior, and is backed by comprehensive new regression tests covering edge cases and journaling integration expectations.
Review tier: Lite
Findings: None
What changed in this PR
Optimizes ArcBufferWriter.TryPeek to avoid repeatedly traversing from the read page when peeking bytes near the end of large, unflushed owned buffers (a hot path for JSON journal framing’s “final payload byte” validation), while preserving behavior for pinned slices and earlier-page reads.
Changes:
- Add a fast-path in
ArcBufferWriter.TryPeekwhich reads directly from the current write page using an unread-relative offset when the buffer is owned (non-pinned). - Add focused unit tests validating
Peekcorrectness across page boundaries, consumption, truncation/patching, reserved/empty write pages, reset/reuse/disposal, and pinned-slice semantics. - Add journaling regression tests ensuring JSON empty-entry validation behavior is retained after large and/or consumed prefixes, including rollback behavior.
| File | Description |
|---|---|
| src/Orleans.Serialization/Buffers/ArcBufferWriter.cs | Adds an owned-buffer write-page fast-path for TryPeek to avoid tail-peek traversal. |
| test/Orleans.Serialization.UnitTests/ArcBufferWriterTests.cs | Adds coverage for Reader.Peek across boundary and mutation scenarios, including pinned slices. |
| test/Orleans.Journaling.Tests/JournalBufferWriterOwnershipTests.cs | Adds regressions for JSON empty-entry validation after large/consumed committed prefixes and across resets. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This was referenced Oct 3, 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.
JSON journal framing validates the final payload byte of every entry.
ArcBufferWriter.TryPeekcurrently reaches that byte by walking from the first unread page, so a large unflushed batch repeatedly traverses a growing prefix.This change reads bytes in an owned buffer's current write page directly using an unread-relative offset. Reads from earlier pages and pinned slices retain the existing traversal. The fast path relies on the existing write-page and length invariants, including consumed prefixes, reserved pages, truncation, and reuse.
Payload validation, public APIs, encoded bytes, and batching policy remain unchanged. Focused regressions cover page boundaries, consumption, empty/reserved write pages, truncation and patching, growing-source pinned slices, reset/disposal, and missing-payload rejection with rollback after large or consumed journal prefixes.
Measured impact
In a task-scoped Windows experiment with a fully queued burst of 4,096 jobs, 4 KiB metadata per job, and fast in-memory journal storage, the normal validation-preserving writer's uncapped drain median decreased from 181.12 ms to 80.18 ms (~2.26× faster) over five measured rounds per version. Observed ranges were 177.17–222.59 ms before and 54.81–114.75 ms after. Both versions used one append of up to 18,276,352 encoded bytes, with essentially unchanged allocations. Per-request p99 medians decreased from 168.27 ms to 48.61 ms.
The finite 128-outstanding-call workload with injected storage latency was essentially unchanged: 997.37 ms versus 984.77 ms median drain time. The burst result is workload-specific; these measurements use simulated storage, a shared Windows machine, and sequential baseline/fixed processes. The requested 2 ms storage delay actually averaged about 15 ms in the latency-controlled case.
Both versions passed an encoding-equivalence check for 1,024 fixed records, and every measured round replayed all 4,097 jobs, including its seed, through the original reader. The improvement addresses the buffer traversal directly while preserving the existing mutation batch policy.
Microsoft Reviewers: Open in CodeFlow