# Parser Span Retention Implementation Plan > **For Codex:** REQUIRED SUB-SKILLS: Use `using-git-worktrees` to create an isolated worktree before making changes, `executeplan` to implement this plan task-by-task, and `finishing-a-development-branch` to merge the verified work back to `main` when implementation is complete. **Goal:** Reduce parser hot-path allocations by keeping protocol fields and payloads in byte-oriented views until a caller explicitly needs materialized `string` or copied `byte[]` values. **Architecture:** Introduce a byte-first parser representation alongside the current `ParsedCommand` contract, then migrate `NatsClient` and adjacent hot paths to consume the new representation without changing wire behavior. Preserve compatibility through an adapter layer so functional parity stays stable while allocation-heavy paths move to spans, pooled buffers, and sequence slices. **Tech Stack:** .NET 10, C#, `System.Buffers`, `System.IO.Pipelines`, `ReadOnlySequence`, `SequenceReader`, xUnit, existing benchmark test harness. --- ## Scope Anchors - Primary source: `src/NATS.Server/Protocol/NatsParser.cs` - Primary consumer: `src/NATS.Server/NatsClient.cs` - Existing parser tests: `tests/NATS.Server.Core.Tests/ParserTests.cs` - Existing snippet/parity tests: `tests/NATS.Server.Core.Tests/Protocol/ProtocolParserSnippetGapParityTests.cs` - Documentation to update: `Documentation/Protocol/Parser.md` - Benchmark project: `tests/NATS.Server.Benchmark.Tests/NATS.Server.Benchmark.Tests.csproj` - Benchmark run instructions: `tests/NATS.Server.Benchmark.Tests/README.md` - Benchmark comparison report: `benchmarks_comparison.md` ## Task 0: Create an isolated git worktree for the parser optimization work **Files:** - Verify: `.worktrees/` - Modify if needed: `.gitignore` **Step 1: Verify the preferred worktree directory is available and ignored** - Check that `.worktrees/` exists and is ignored by git before creating a project-local worktree. - If `.worktrees/` is not ignored, add it to `.gitignore`, then commit that repository hygiene fix before continuing. **Step 2: Create the feature worktree on a `codex/` branch** - Run: ```bash git worktree add .worktrees/codex-parser-span-retention -b codex/parser-span-retention cd .worktrees/codex-parser-span-retention ``` - Expected: a new isolated worktree exists at `.worktrees/codex-parser-span-retention` on branch `codex/parser-span-retention`. **Step 3: Verify the worktree starts from a clean, passing baseline** - Run: `dotnet test tests/NATS.Server.Core.Tests/NATS.Server.Core.Tests.csproj -c Release` - Expected: PASS. If this fails, stop and resolve or explicitly confirm whether to proceed from a failing baseline. **Step 4: Commit any required worktree setup fix** - Only if `.gitignore` changed, run: ```bash git add .gitignore git commit -m "chore: ignore local worktree directories" ``` ## Task 1: Freeze parser behavior and add allocation-focused tests **Files:** - Modify: `tests/NATS.Server.Core.Tests/ParserTests.cs` - Modify: `tests/NATS.Server.Core.Tests/Protocol/ProtocolParserSnippetGapParityTests.cs` - Create: `tests/NATS.Server.Core.Tests/Protocol/ParserSpanRetentionTests.cs` **Step 1: Add failing tests for byte-first parser behavior** - Cover `PUB`, `HPUB`, `CONNECT`, and `INFO` with assertions that the new parser path can expose field data without forcing immediate `string` materialization. - Add split-payload cases to prove the parser preserves pending payload state across reads. **Step 2: Add compatibility tests for existing `ParsedCommand` behavior** - Keep current semantics for `Type`, `Subject`, `ReplyTo`, `Queue`, `Sid`, `HeaderSize`, and `Payload`. - Ensure malformed protocol inputs still throw `ProtocolViolationException` with existing snippets/messages. **Step 3: Run targeted tests to verify the new tests fail first** - Run: `dotnet test tests/NATS.Server.Core.Tests/NATS.Server.Core.Tests.csproj --filter "FullyQualifiedName~ParserTests|FullyQualifiedName~ParserSpanRetentionTests|FullyQualifiedName~ProtocolParserSnippetGapParityTests" -c Release` - Expected: FAIL in the newly added parser span-retention tests only. **Step 4: Commit the failing-test baseline** - Run: ```bash git add tests/NATS.Server.Core.Tests/ParserTests.cs tests/NATS.Server.Core.Tests/Protocol/ProtocolParserSnippetGapParityTests.cs tests/NATS.Server.Core.Tests/Protocol/ParserSpanRetentionTests.cs git commit -m "test: lock parser span-retention behavior" ``` ## Task 2: Introduce byte-oriented parser view types **Files:** - Create: `src/NATS.Server/Protocol/ParsedCommandView.cs` - Modify: `src/NATS.Server/Protocol/NatsParser.cs` **Step 1: Add a hot-path parser view contract** - Create a `ref struct` or small `readonly struct` representation for command views that can carry: - operation kind - subject/reply/queue/SID as spans or sequence-backed views - payload as `ReadOnlySequence` or `ReadOnlyMemory` when contiguous - header size and max-messages metadata **Step 2: Add an adapter to the current `ParsedCommand` shape** - Keep the public/internal `ParsedCommand` entry point usable for existing consumers and tests. - Centralize materialization so `Encoding.ASCII.GetString(...)` and `ToArray()` happen in one adapter layer instead of inside every parse branch. **Step 3: Re-run parser tests** - Run: `dotnet test tests/NATS.Server.Core.Tests/NATS.Server.Core.Tests.csproj --filter "FullyQualifiedName~Parser" -c Release` - Expected: FAIL only in branches not yet migrated to the new view path. **Step 4: Commit the parser-view scaffolding** - Run: ```bash git add src/NATS.Server/Protocol/ParsedCommandView.cs src/NATS.Server/Protocol/NatsParser.cs git commit -m "feat: add byte-oriented parser view contract" ``` ## Task 3: Rework control-line parsing and pending payload state **Files:** - Modify: `src/NATS.Server/Protocol/NatsParser.cs` **Step 1: Remove early string materialization from control-line parsing** - Change `ParsePub`, `ParseHPub`, `ParseSub`, `ParseUnsub`, `ParseConnect`, and `ParseInfo` to keep raw byte slices in the hot parser path. - Replace `_pendingSubject` and `_pendingReplyTo` string fields with byte-oriented pending state. **Step 2: Avoid unconditional payload copies** - Update `TryReadPayload()` so single-segment payloads can flow through as borrowed memory/slices. - Copy only when the payload is multi-segment or when the compatibility adapter explicitly requires a standalone buffer. **Step 3: Replace repeated tiny literal allocations** - Stop using per-call `u8.ToArray()`-style buffers for CRLF and other fixed protocol tokens inside this parser path. - Add shared static buffers where appropriate. **Step 4: Run targeted regression tests** - Run: `dotnet test tests/NATS.Server.Core.Tests/NATS.Server.Core.Tests.csproj --filter "FullyQualifiedName~ParserTests|FullyQualifiedName~ProtocolParserSnippetGapParityTests" -c Release` - Expected: PASS. **Step 5: Commit the parser hot-path rewrite** - Run: ```bash git add src/NATS.Server/Protocol/NatsParser.cs git commit -m "perf: keep parser state in bytes until materialization" ``` ## Task 4: Migrate `NatsClient` to the new parser path without changing behavior **Files:** - Modify: `src/NATS.Server/NatsClient.cs` - Modify: `tests/NATS.Server.Core.Tests/ParserTests.cs` - Modify: `tests/NATS.Server.Core.Tests/Protocol/ClientProtocolGoParityTests.cs` **Step 1: Consume parser views first, materialize only at command handling boundaries** - Update `ProcessCommandsAsync` and any parser call sites so hot `PUB`/`HPUB` handling can read subject, reply, and payload from the byte-oriented representation. - Keep logging/tracing behavior intact, but ensure tracing is the only reason strings are created on trace-enabled paths. **Step 2: Preserve feature parity** - Verify header parsing, payload size checks, connect/info handling, and slow-consumer/error behavior still match current tests. **Step 3: Run consumer-facing protocol tests** - Run: `dotnet test tests/NATS.Server.Core.Tests/NATS.Server.Core.Tests.csproj --filter "FullyQualifiedName~ClientProtocolGoParityTests|FullyQualifiedName~ParserTests" -c Release` - Expected: PASS. **Step 4: Commit the consumer migration** - Run: ```bash git add src/NATS.Server/NatsClient.cs tests/NATS.Server.Core.Tests/ParserTests.cs tests/NATS.Server.Core.Tests/Protocol/ClientProtocolGoParityTests.cs git commit -m "perf: consume parser command views in client hot path" ``` ## Task 5: Add benchmarks, document the change, run full verification, and refresh the benchmark comparison report **Files:** - Create: `tests/NATS.Server.Benchmark.Tests/Protocol/ParserHotPathBenchmarks.cs` - Modify: `Documentation/Protocol/Parser.md` - Modify: `benchmarks_comparison.md` **Step 1: Add parser-focused benchmark coverage** - Add microbenchmarks for: - `PING` / `PONG` - `PUB` - `HPUB` - split payload reads - Capture throughput and allocation deltas before/after. **Step 2: Update protocol documentation** - Document the new parser view + adapter split, why strings are deferred, and where payload copying is still intentionally required. **Step 3: Run full verification** - Run: `dotnet test tests/NATS.Server.Core.Tests/NATS.Server.Core.Tests.csproj -c Release` - Run: `dotnet test tests/NATS.Server.Benchmark.Tests/NATS.Server.Benchmark.Tests.csproj --filter "FullyQualifiedName~Parser" -c Release` - Expected: PASS; benchmark output shows reduced allocations relative to the baseline run. **Step 4: Run the full benchmark suite per the benchmark project README** - Run: ```bash dotnet test tests/NATS.Server.Benchmark.Tests \ --filter "Category=Benchmark" \ -v normal \ --logger "console;verbosity=detailed" 2>&1 | tee /tmp/bench-output.txt ``` - Expected: benchmark comparison output is captured in `/tmp/bench-output.txt`, including the "Standard Output Messages" blocks described in `tests/NATS.Server.Benchmark.Tests/README.md`. **Step 5: Update `benchmarks_comparison.md` with the new benchmark results** - Extract the comparison blocks from `/tmp/bench-output.txt`. - Update `benchmarks_comparison.md` with the latest msg/s, MB/s, ratio, and latency values. - Update the benchmark date, environment description, Summary table, and Key Observations so they match the new run. **Step 6: Commit the verification, docs, and benchmark report refresh** - Run: ```bash git add tests/NATS.Server.Benchmark.Tests/Protocol/ParserHotPathBenchmarks.cs Documentation/Protocol/Parser.md benchmarks_comparison.md git commit -m "docs: record parser hot-path allocation strategy" ``` ## Task 6: Merge the verified parser work back to `main` and clean up the worktree **Files:** - No source changes expected **Step 1: Confirm the feature branch is fully verified before merge** - Reuse the verification from Task 5. Do not merge if the core tests, parser benchmark tests, or full benchmark suite run did not complete successfully. **Step 2: Merge `codex/parser-span-retention` back into `main`** - Return to the primary repository worktree and run: ```bash git checkout main git pull git merge codex/parser-span-retention ``` - Expected: the parser optimization commits merge cleanly into `main`. **Step 3: Re-run verification on the merged `main` branch** - Run: `dotnet test tests/NATS.Server.Core.Tests/NATS.Server.Core.Tests.csproj -c Release` - Run: `dotnet test tests/NATS.Server.Benchmark.Tests/NATS.Server.Benchmark.Tests.csproj --filter "FullyQualifiedName~Parser" -c Release` - Confirm `benchmarks_comparison.md` reflects the results captured from the Task 5 full benchmark suite. - Expected: PASS on merged `main`. **Step 4: Delete the feature branch and remove the worktree** - Run: ```bash git branch -d codex/parser-span-retention git worktree remove .worktrees/codex-parser-span-retention ``` - Expected: only `main` remains checked out in the primary workspace, and the temporary parser worktree is removed. ## Completion Checklist - [ ] Parser optimization work was implemented in an isolated `.worktrees/codex-parser-span-retention` worktree on branch `codex/parser-span-retention`. - [ ] `NatsParser` no longer materializes hot-path `string` values during parse unless the compatibility adapter requests them. - [ ] Single-segment payloads can pass through without an unconditional `byte[]` copy. - [ ] Existing parser and protocol behavior remains green in core tests. - [ ] Parser-focused benchmark coverage exists in `tests/NATS.Server.Benchmark.Tests/Protocol/`. - [ ] The full benchmark suite was run using the workflow from `tests/NATS.Server.Benchmark.Tests/README.md`. - [ ] `benchmarks_comparison.md` was updated to reflect the latest benchmark run. - [ ] `Documentation/Protocol/Parser.md` explains the byte-first parser architecture. - [ ] Verified parser optimization commits were merged back into `main`.