-
Notifications
You must be signed in to change notification settings - Fork 0
Plan zero-copy frame migration: ADRs 008–010 and 16.x/17.x roadmap; refactor nested conditionals (Epic #284)
#523
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
fd147a3
feat(zero-copy-migration): add ADRs and roadmap for zero-copy Vec<u8>…
leynos d14b7b9
feat(execplans): rename roadmap items from 11.x to 17.x and 10.x to 16.x
leynos 92cd244
docs(adr,migration): add traceability sections linking ADRs to roadma…
leynos defce06
docs(adr): fix trailing period in ADR date lines
leynos 0e0724a
docs(adr): update status and clarify zero-copy ADRs and transport fra…
leynos 1899caa
docs(docs/frame-vec-u8-inventory): clarify coordination notes with im…
leynos b4dafc6
docs(adr): add cross-references to zero-copy migration roadmap
leynos 7b10e35
docs(documentation): fix typos in markdown files
leynos c2106a2
docs(adr-009): correct typos and improve phrasing in migration ADR
leynos fec5561
docs(documentation): update wording and validation instructions in ex…
leynos e9dd14d
refactor(frame, message_assembler): refactor frame conversion and seq…
leynos f60c7f0
refactor(frame): simplify fill_prefix_buffer with direct slicing and …
leynos 6cab373
refactor(frame): simplify fill_prefix_buffer by removing size param
leynos 92bbaa8
test(memory_budget): refactor connection abort assertion by extractin…
leynos 81c769b
docs(message sequence validation): document message sequence validati…
leynos File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,205 @@ | ||
| # Architectural decision record (ADR) 008: zero-copy public byte container | ||
|
|
||
| ## Status | ||
|
|
||
| Proposed | ||
|
|
||
| ## Date | ||
|
|
||
| 2026-04-12 | ||
|
|
||
| ## Context and Problem Statement | ||
|
|
||
| `FrameCodec` already supports `Bytes`-backed frame types, and the default | ||
| length-delimited codec uses `Bytes` today. The inventory in | ||
| [`frame-vec-u8-inventory.md`](frame-vec-u8-inventory.md), however, shows that | ||
| the public packet, middleware, serializer, and client hook surfaces still | ||
| assume owned `Vec<u8>` payloads. | ||
|
|
||
| Those APIs are not equivalent in how they use bytes: | ||
|
|
||
| - `PacketParts`, `Envelope`, and serializer output are primarily transport and | ||
| routing hand-off surfaces. | ||
| - `ServiceRequest`, `ServiceResponse`, and `BeforeSendHook` promise editable | ||
| bytes and therefore embed a mutation model into the public API. | ||
| - The default codec path requires shared, cheap-to-clone byte buffers to | ||
| eliminate the final copy identified in epic 284. | ||
|
|
||
| The project needs a single public byte-container strategy that preserves | ||
| zero-copy behaviour for read-only paths without forcing every caller to manage | ||
| buffer taxonomy manually. | ||
|
|
||
| ## Traceability | ||
|
|
||
| This ADR governs the Epic 284 work tracked in: | ||
|
|
||
| - [`frame-vec-u8-inventory.md`](frame-vec-u8-inventory.md), especially the | ||
| public payload surfaces and resolved direction in sections "Public | ||
| `payload-bound` surfaces" and "Resolved direction for epic 284". | ||
| - [`zero-copy-frame-and-payload-migration-roadmap.md`](zero-copy-frame-and-payload-migration-roadmap.md), | ||
| which tracks the dedicated zero-copy migration phases that apply this public | ||
| byte-container decision across the internal migration, public API flip, and | ||
| release workstream. | ||
| - [`roadmap.md`](roadmap.md), specifically: | ||
| - roadmap item `10.1.1`, which approves the stable public byte-container and | ||
| edit-on-demand model; | ||
| - roadmap item `11.1.1`, which converts internal packet payload storage and | ||
| serializer output to the approved byte representation; | ||
| - roadmap items `12.1.1` and `12.1.2`, which migrate packet, envelope, and | ||
| middleware surfaces to the approved byte and editing model; | ||
| - roadmap items `12.2.1` and `12.2.3`, which migrate client hooks and | ||
| serializers and publish downstream migration examples; | ||
| - roadmap item `13.1.2`, which writes the migration guide section covering | ||
| the zero-copy API flip. | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| ## Decision Drivers | ||
|
|
||
| - Remove the final owned-byte copy from the default outbound path. | ||
| - Keep read-only packet and routing paths zero-copy by default. | ||
| - Preserve a clear and ergonomic mutation story for middleware and client | ||
| hooks. | ||
| - Avoid exposing two equally primary byte-container APIs forever. | ||
| - Keep the design compatible with the existing `FrameCodec` and `Bytes` | ||
| default. | ||
|
|
||
| ## Requirements | ||
|
|
||
| ### Functional requirements | ||
|
|
||
| - Public packet and routing surfaces must support cheap cloning and transport | ||
| without copying when the underlying bytes are already shared. | ||
| - Middleware and client hooks must still support intentional mutation of | ||
| serialized bytes. | ||
| - The selected API must support migration examples that downstream users can | ||
| apply without learning multiple internal buffer types. | ||
|
|
||
| ### Technical requirements | ||
|
|
||
| - The default codec path must be able to move from serialization to | ||
| `wrap_payload` without materializing a fresh `Vec<u8>`. | ||
| - The design must not require `F::Frame = Vec<u8>` or any equivalent | ||
| frame-level coupling. | ||
| - Mutation must be explicit, and it must not trigger copies on read-only | ||
| paths. | ||
|
|
||
| ## Options Considered | ||
|
|
||
| ### Option A: switch every public byte surface directly to `Bytes` | ||
|
|
||
| This would maximize zero-copy reuse for read-only paths, but it would also | ||
| replace the current `frame_mut()` and `Fn(&mut Vec<u8>)` contracts with a more | ||
| awkward mutation story. Middleware authors would need to choose when to clone, | ||
| freeze, or reallocate, and the API would no longer describe the intended | ||
| editing workflow clearly. | ||
|
|
||
| ### Option B: use `Bytes` for stable storage and expose explicit edit-on-demand wrappers (preferred) | ||
|
|
||
| Under this option, public packet and routing surfaces store `Bytes` (or a | ||
| single project-defined wrapper over `Bytes`) as the stable representation. | ||
| Editable surfaces expose mutation through an explicit helper or editor that | ||
| performs copy-on-write only when a caller actually mutates the bytes. | ||
|
|
||
| This preserves zero-copy behaviour for pass-through traffic while keeping the | ||
| current "inspect, optionally edit, then forward" workflow legible. | ||
|
|
||
| ### Option C: keep `Vec<u8>` as the stable public representation | ||
|
|
||
| This preserves the existing mutation model, but it also preserves the final | ||
| copy that epic 284 is trying to remove. Any internal `Bytes` use would still | ||
| collapse back to owned vectors at the public boundary. | ||
|
|
||
| ### Option D: make the public API permanently generic or dual-surfaced over `Vec<u8>` and `Bytes` | ||
|
|
||
| This avoids choosing one primary abstraction, but it increases API surface | ||
| area, complicates documentation, and pushes conversion logic onto downstream | ||
| users. The inventory explicitly calls out the risk of forcing consumers to | ||
| write repetitive adapters between byte containers. | ||
|
|
||
| | Topic | Option A: `Bytes` only | Option B: `Bytes` + explicit editor | Option C: keep `Vec<u8>` | Option D: dual support | | ||
| | ------------------------- | ---------------------- | ----------------------------------- | ------------------------ | ---------------------- | | ||
| | Zero-copy read path | Excellent | Excellent | Poor | Good | | ||
| | Editing ergonomics | Weak | Strong | Strong | Medium | | ||
| | Public API complexity | Medium | Medium | Low | High | | ||
| | Long-term maintainability | Good | Good | Poor | Poor | | ||
| | Migration burden | Medium | Medium | Low short-term | High | | ||
|
|
||
| _Table 1: Trade-offs for the public byte-container choice._ | ||
|
|
||
| ## Decision Outcome / Proposed Direction | ||
|
|
||
| Adopt Option B: use `Bytes`-compatible storage as the stable public payload | ||
| representation, and expose mutation through an explicit edit-on-demand helper | ||
| for middleware and client hooks. | ||
|
|
||
| The proposed direction is: | ||
|
|
||
| - Packet and routing surfaces (`PacketParts`, `Envelope`, serializer output, | ||
| and equivalent internal hand-offs) standardize on shared bytes. | ||
| - Middleware and hook surfaces expose an explicit editing entry point that | ||
| copies only when the caller mutates the payload. | ||
| - `Vec<u8>` remains available only through explicit compatibility helpers, | ||
| adapters, or migration constructors defined by the rollout ADR. | ||
|
|
||
| This keeps the zero-copy path obvious and makes the mutating path explicit. | ||
|
|
||
| ## Goals and Non-Goals | ||
|
|
||
| ### Goals | ||
|
|
||
| - Remove the final default-path copy without sacrificing middleware | ||
| ergonomics. | ||
| - Make the read-only versus mutating cost model visible in the API. | ||
| - Give downstream users a single primary byte model to target. | ||
|
|
||
| ### Non-Goals | ||
|
|
||
| - Guarantee zero allocation for every mutation path. | ||
| - Eliminate every internal `Vec<u8>` immediately, including bounded one-shot | ||
| replay buffers that remain intentionally separate. | ||
| - Rework unrelated message-body APIs that are outside the frame and payload | ||
| migration scope. | ||
|
|
||
| ## Migration Plan | ||
|
|
||
| ### Phase 1: establish the stable byte representation | ||
|
|
||
| Convert packet payload storage and serializer output to the shared byte | ||
| representation, and add explicit conversions for compatibility callers. | ||
|
|
||
| ### Phase 2: introduce edit-on-demand wrappers | ||
|
|
||
| Replace `Vec<u8>`-backed middleware and hook edit points with explicit editing | ||
| helpers that preserve the current workflow while copying only when modified. | ||
|
|
||
| ### Phase 3: update docs and examples | ||
|
|
||
| Update the user guide, middleware examples, and migration guide to teach the | ||
| new read-only and mutating workflows distinctly. | ||
|
|
||
| ## Known Risks and Limitations | ||
|
|
||
| - Copy-on-write editing helpers still need a concrete public shape, and poor | ||
| naming could hide when a clone occurs. | ||
| - Some downstream users may rely on direct `Vec<u8>` methods such as `push` or | ||
| `extend_from_slice`; the migration guide must map those patterns to the new | ||
| editor API explicitly. | ||
| - If the project exposes both raw `Bytes` and a custom editor type too widely, | ||
| the API could still feel like a dual-surface design in practice. | ||
|
|
||
| ## Outstanding Decisions | ||
|
|
||
| - Should the public editing surface expose `BytesMut`, a project-defined editor | ||
| wrapper, or closure-based mutation helpers? | ||
| - Should serializer output move to the new byte representation in the same | ||
| release as packet and middleware changes, or in a preparatory release | ||
| beforehand? | ||
| - Which compatibility constructors or `into_vec` helpers are required for the | ||
| migration window? | ||
|
|
||
| ## Architectural Rationale | ||
|
|
||
| The wider architecture already treats frame transport and payload ownership as | ||
| separate concerns. Standardizing stable payload storage on a `Bytes`-compatible | ||
| representation aligns the public API with the default codec path, while | ||
| explicit editing helpers preserve the middleware-first ergonomics that made | ||
| `Vec<u8>` attractive originally. | ||
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,168 @@ | ||
| # Architectural decision record (ADR) 009: rollout strategy for the `Vec<u8>` to zero-copy migration | ||
|
|
||
| ## Status | ||
|
|
||
| Proposed | ||
|
|
||
| ## Date | ||
|
|
||
| 2026-04-12 | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| ## Context and Problem Statement | ||
|
|
||
| Epic 284 is a breaking change. The inventory in | ||
| [`frame-vec-u8-inventory.md`](frame-vec-u8-inventory.md) shows that downstream | ||
| users are exposed to `Vec<u8>` not only through one public `Frame` alias, but | ||
| through packet construction, middleware wrappers, client hooks, serializer | ||
| output, tests, and documentation. A zero-copy internal implementation is not | ||
| enough; the project also needs a release and compatibility strategy that keeps | ||
| the change reviewable and predictable for downstream users. | ||
|
|
||
| The key rollout question is whether the project should: | ||
|
|
||
| - switch to the new byte model in one hard break, | ||
| - support both `Vec<u8>` and the new representation indefinitely, or | ||
| - ship a staged major release with finite compatibility helpers and explicit | ||
| migration guidance. | ||
|
|
||
| ## Traceability | ||
|
|
||
| This ADR governs the Epic 284 compatibility and rollout work tracked in: | ||
|
|
||
| - [`frame-vec-u8-inventory.md`](frame-vec-u8-inventory.md), especially the | ||
| "Generalization paths and conceptual risks", "Resolved direction for epic | ||
| 284", and "Coordination notes" sections. | ||
| - [`zero-copy-frame-and-payload-migration-roadmap.md`](zero-copy-frame-and-payload-migration-roadmap.md), | ||
| which tracks the dedicated migration publish and breaking-release workstream | ||
| that this rollout policy governs, including the Phase 1 decision closure and | ||
| the later publication milestones. | ||
| - [`roadmap.md`](roadmap.md), specifically: | ||
| - roadmap item `10.1.2`, which approves the compatibility and rollout | ||
| policy; | ||
| - roadmap item `10.2.3`, which publishes the migration-guide outline and | ||
| exact public surfaces affected by the change; | ||
| - roadmap item `12.1.2`, which preserves explicit edit-on-demand ergonomics | ||
| as part of the public migration story; | ||
| - roadmap item `12.2.2`, which documents how client preamble leftovers fit | ||
| into the compatibility plan; | ||
| - roadmap items `13.1.1` and `13.1.2`, which remove obsolete `Vec<u8>` | ||
| compatibility surfaces and publish the migration guide section; | ||
| - roadmap items `14.1.1`, `14.1.2`, `14.1.3`, and `14.2.1`, which prepare | ||
| and review the breaking release and any retained helpers. | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| ## Decision Drivers | ||
|
|
||
| - Minimize downstream boilerplate during the migration. | ||
| - Keep the long-term public API coherent rather than permanently dual-shaped. | ||
| - Make the breaking change easy to communicate in release notes and examples. | ||
| - Avoid indefinite maintenance of compatibility shims that preserve the old | ||
| bottlenecks. | ||
| - Leave room for downstream users to migrate in bounded, observable steps. | ||
|
|
||
| ## Options Considered | ||
|
|
||
| ### Option A: one-shot hard break with no compatibility helpers | ||
|
|
||
| Ship the new zero-copy API and require all downstream users to update in one | ||
| step. This keeps the final API clean, but it also maximizes upgrade pain and | ||
| forces every consumer to solve migration details independently. | ||
|
|
||
| ### Option B: permanent dual support for `Vec<u8>` and the zero-copy type | ||
|
|
||
| Expose both old and new constructors, accessors, and hook signatures as first- | ||
| class supported APIs. This reduces short-term friction, but it risks turning a | ||
| breaking release into a permanent maintenance burden and keeps the old | ||
| allocation-heavy path alive indefinitely. | ||
|
|
||
| ### Option C: staged breaking release with finite compatibility helpers (preferred) | ||
|
|
||
| Adopt the zero-copy API as the long-term default, but ship the breaking release | ||
| with bounded helper conversions, migration examples, and clear removal criteria | ||
| for any retained `Vec<u8>` adapters. | ||
|
|
||
| | Topic | Option A: hard break | Option B: permanent dual support | Option C: staged release | | ||
| | ------------------------- | -------------------- | -------------------------------- | ------------------------ | | ||
| | Long-term API coherence | Strong | Weak | Strong | | ||
| | Short-term upgrade pain | High | Low | Medium | | ||
| | Maintenance burden | Low | High | Medium | | ||
| | Migration guidance needed | High | High | High | | ||
| | Risk of preserving copies | Low | High | Low | | ||
|
|
||
| _Table 1: Trade-offs for the migration rollout policy._ | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| ## Decision Outcome / Proposed Direction | ||
|
|
||
| Adopt Option C: ship the zero-copy API as the new primary surface in the next | ||
| breaking release, and include finite compatibility helpers plus a committed | ||
| migration guide. | ||
|
|
||
| The proposed rollout policy is: | ||
|
|
||
| - The new byte model becomes the canonical API in the breaking release. | ||
| - Compatibility helpers remain only where they clearly reduce upgrade cost and | ||
| do not reintroduce the old bottlenecks as first-class behaviour. | ||
| - Release notes, examples, and migration documentation explicitly show how to | ||
| move middleware, hooks, serializer code, and custom codecs off `Vec<u8>`. | ||
| - Any retained compatibility surface has a documented review point for later | ||
| removal or retention. | ||
|
|
||
| ## Goals and Non-Goals | ||
|
|
||
| ### Goals | ||
|
|
||
| - Make the breaking release adoptable without forcing users to reverse-engineer | ||
| internal design intent. | ||
| - Prevent the project from carrying two equally primary byte APIs | ||
| indefinitely. | ||
| - Bound the lifetime of compatibility helpers. | ||
|
|
||
| ### Non-Goals | ||
|
|
||
| - Preserve full source compatibility. | ||
| - Hide the fact that this is a semver-significant release. | ||
| - Guarantee that every downstream crate can upgrade without any code changes. | ||
|
|
||
| ## Migration Plan | ||
|
|
||
| ### Phase 1: announce the target API and migration path | ||
|
|
||
| Publish the roadmap, proposed ADRs, and a migration-guide outline before the | ||
| public API flip lands. | ||
|
|
||
| ### Phase 2: land the breaking release with bounded helpers | ||
|
|
||
| Ship the new canonical API together with explicitly documented adapters such as | ||
| `from_vec`, `into_vec`, or equivalent helper wrappers where they materially | ||
| reduce migration churn. | ||
|
|
||
| ### Phase 3: evaluate retained helpers after the release | ||
|
|
||
| Review which helpers are still needed after the initial migration cycle, and | ||
| either accept them as narrow compatibility tools or schedule their removal in a | ||
| follow-up release plan. | ||
|
|
||
| ## Known Risks and Limitations | ||
|
|
||
| - Compatibility helpers can easily become permanent in practice if their removal | ||
| criteria are not written down. | ||
| - A breaking release without concrete before-and-after examples will still feel | ||
| abrupt even if helper APIs exist. | ||
| - If too many helpers survive unchanged, downstream code may continue to depend | ||
| on the `Vec<u8>` mental model, weakening the benefits of the migration. | ||
|
|
||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
| ## Outstanding Decisions | ||
|
|
||
| - Which compatibility helpers are important enough to ship in the breaking | ||
| release? | ||
| - Should any helpers be feature-gated, or should they ship in the default | ||
| build for one release cycle? | ||
| - What benchmark or adoption signals justify removing retained helpers later? | ||
|
|
||
| ## Architectural Rationale | ||
|
|
||
| The zero-copy migration is not only a transport optimization. It changes how | ||
| middleware, hooks, serializers, and packet routing represent bytes. A staged | ||
| breaking release acknowledges that architectural reality: the new API becomes | ||
| the primary one immediately, but the release still carries enough local | ||
| documentation and narrowly scoped adapters to keep downstream adoption | ||
| manageable. | ||
|
coderabbitai[bot] marked this conversation as resolved.
|
||
Oops, something went wrong.
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.