Skip to content

[WIP #742] Integrate writer-owned stream scheduling and RPC handoff - #803

Closed
SunSi12138 wants to merge 14 commits into
perf/735-resolved-flow-statefrom
work/742-b3-production-ci
Closed

SunSi12138 wants to merge 14 commits into
perf/735-resolved-flow-statefrom
work/742-b3-production-ci

Conversation

@SunSi12138

@SunSi12138 SunSi12138 commented Oct 11, 2026 •

Copy link
Copy Markdown
Owner

Refs #735, #742.

Current status: scheduler core validated; full RPC integration not complete

This stacked Draft targets perf/735-resolved-flow-state at e834d3c28c87ad496989af925515cf21babd308d. It does not modify dev or the original #742 branch.

Latest validated head: 1c76ad131eda1333cf62c87f87a37a5f2b2fc8c8.
Tree: 6c597948f6f40e25e9a331574074e6657717d67c.

Implemented

  • Writer-ready handoff into the existing SendPump: shared normal byte budget, progress headroom, bounded ordinary/ready turns, explicit timed flush policy and durable buffer settlement.
  • Standalone writer-owned stream scheduler: one authority debits both stream and connection credit, bounded prepared frames, immutable lifecycle handles, late returns and retirement, DATA/EOF ordering and item-level round robin.
  • Request-emission predecessor fence; it does not create stream state before the Request's durable emission succeeds.
  • Abort cleanup pins the old key until keyed preparation cancellation and buffer return finish; producer release preserves independent cleanup uses. Late preparation-waiter registration rechecks the abort outcome.

Verified with existing workflows

PR Fast 38111320489: Release solution build 0 warnings/errors; UnitTests 2020/2020, Generator.Tests 269/269, LoadTest.Tests 77/77, zero failures/skips. Formatting, reference boundaries, maintainability and the existing allocation gate passed.

PR Package Smoke 38111320511: trim-only, isolated NativeAOT, packaging and package smoke passed.

Detailed red/green record retains the deterministic old-abort/new-lifecycle failure and a separately disclosed test-oracle correction. Original failure-reproduction assertions are unchanged. No workflow, existing test population, timeout or performance threshold was relaxed.

Not yet production-ready

Normal generated/client RPC producers have NOT yet switched to this scheduler. The remaining adapter must select exactly one sender-credit authority for a connection and connect Request/DATA/StreamComplete ordering, compression, deadlines, cancellation and preparation budgets. Existing full-suite success does not mean those missing adapters were tested.

This candidate retains both stream and connection flow-control windows. It is not the historical B3 quantum-16 benchmark configuration, and historical 60–70% tiny-item gains are NOT results for this PR. No new integrated RPC throughput measurements exist for this head. NativeAOT smoke is not full native B3 pipeline validation.

Keep this PR Draft until the production adapters and exact-head end-to-end correctness/performance verification are complete.

Build on fixed PR #742 head e834d3c. Add the existing three-file handoff candidate and 14 real-SendPump regression cases. Keep ordinary/progress budgets, timed flush policy, bounded ordinary/ready selection and buffer ownership through flush and stop.

This is the handoff layer only, not a completed B3 RPC integration or a performance Go claim. No workflow, test population, timeout, allocation threshold or production flow-control default is relaxed. CI has not yet run for this tree.

Copy link
Copy Markdown
Owner Author

Existing-workflow CI completed for the handoff candidate

Candidate head: e079c227330fac920c2b5b00396839890b33318b.
GitHub Actions checkout: PR merge commit a5e3f0ff4a8d560e53e7e961089e781a6e1f7844.
Both commits have the exact same source tree: cb404dc4462df297aff0f59e8287339935ce8d74 (verified through Git data and checkout logs). Base remains #742 e834d3c28c87ad496989af925515cf21babd308d.

PR Fast — PASS

Run 38107190489, first attempt; fast job 114375077573, allocation job 114375077404.

  • Restore, production-reference boundaries, whitespace formatting and unchanged maintainability gates: pass.
  • Release solution build: 0 warnings, 0 errors.
  • Generated-assembly dependency check: pass.
  • UnitTests: 1992 passed / 1992 total; 0 failed, 0 skipped. The 14 newly added WriterReadyPumpBridgeTests are included in this unfiltered suite (the source was compiled in the test project; the earlier base suite contained 1978 cases).
  • Generator.Tests: 269/269, 0 failed, 0 skipped.
  • LoadTest.Tests: 77/77, 0 failed, 0 skipped.
  • Existing four-case managed-allocation gate: pass. The injected 512 B/op negative control was correctly rejected.

Allocation artifact 11689953661 was downloaded and its SHA256 verified as 6e162494541eababedefc345d713ac0131ecf8c4b6ac2787d48ff3f8932f6ec4. The artifact contains the original gate, self-test and negative-control JSON. This is a budget gate for existing paths, not a B3 throughput comparison.

PR Package Smoke — PASS

Run 38107190258, first attempt; job 114375076380.
Release build, ProjectReference trim-only UnsafeBlit smoke, isolated NativeAOT smoke, pack, NuGet package contract verification and NuGet package smoke all completed successfully.

Remaining scope

No workflow or acceptance threshold was modified, and no failed run was retried to green. The original #742 branch and dev were not modified. This closes the previous inability to compile/test the handoff candidate: existing Actions provide a working validation path.

Keep this PR Draft. It still does not connect a single B3 send-credit authority to normal generated/client RPC production. Request/DATA/StreamComplete ordering, bounded preparation, deadlines/cancellation and generation-safe refunds need their production adapter and dedicated integration/performance validation. NativeAOT smoke is not a claim that the full B3 pipeline has been exercised natively. The separate coupled-window-update and retirement-fusion patches are not included in this tested commit.

…e tests

Stage a standalone runtime source on the validated writer-ready handoff. One send authority owns connection/stream debit, window returns, ready ordering and retirement; no per-item controller dictionary lookup or connection credit gate is used. Preparation has independent byte/slot admission and immutable lifecycle handles.

Add 20 focused C# cases covering queue-before-credit admission, DATA/EOF ordering, connection FIFO with credit-free terminal progress, local-blocked-stream skip, oversized borrowing, coupled late returns, stale/foreign handles, bounded preparation, cancellation, tombstone capacity and drain snapshots.

This commit does not yet switch normal generated/client RPC producers to the new source. It deliberately does not pool stream handles or claim the historical B3 quantum-16 performance numbers. Existing workflow files, test populations and acceptance thresholds are unchanged.
…s on Request emission

Add four Request predecessor admission checks and two deterministic abort/drain reentry regressions. The latter are expected to fail on the current scheduler: a TaskScheduler.QueueTask callback can service the sole writer and reopen the same key before old Abort performs its key-scoped pre-credit cleanup, causing the old error to reject a new lifecycle.

This test-first revision deliberately does not fix that lifecycle race. Use the unchanged PR Fast workflow to retain the failing control before applying the cleanup pin. No test, population, timeout, workflow or performance threshold is relaxed. Normal RPC producers remain unchanged; the Request fence is not yet wired into them.
…ate budget joins

PR Fast run 38110719730 compiled the test-first revision and failed exactly the two intended abort/drain-reentry regressions: the old abort exception reached a newly opened same-key lifecycle. Retain that red control and both original assertions unchanged.

Reuse ProducerActive as a lifecycle-use word: bit zero is the single producer, upper units pin each abort cleanup. The producer releases only its bit, and retirement still requires zero uses. Keep the key pinned until old keyed preparation cancellation and packet returns finish, including nested notification reentry. No new per-stream field, gate, allocation or hot-path Interlocked is introduced.

Register preparation waiters before rechecking the published abort error so an abort immediately before waiter registration cannot leave a late join stranded. Add a nested-cleanup regression and an explicitly labeled actual-helper checkpoint test. All existing tests and CI workflows remain unchanged; this is not a full RPC/B3 integration or performance claim.
…nc-wrapper completion

PR Fast 38111130100 confirmed both original cross-generation failures are fixed and the nested cleanup case passes (2019/2020). Its sole failure is the new helper-checkpoint test's IsCompleted assertion: the underlying waiter is rejected, but PreCreditSerializedBudget.WaitForGrantAsync resumes asynchronously.

Remove only that newly introduced synchronous-completion assumption. Keep the existing 10-second bound, exact abort exception identity, fully occupied independent budget and later reuse/conservation checks. A genuinely lost rejection still times out because the blocking stream is not released until after the await. No production code, original red-test assertions, workflow or limit changes.

Copy link
Copy Markdown
Owner Author

Request-emission fence and abort key-lifetime follow-up — CI verified

Validated head: 1c76ad131eda1333cf62c87f87a37a5f2b2fc8c8.
Actions checkout: d41ca7e3c917028121850a9bd5cdc8cb96fe7ed3.
Both have tree 6c597948f6f40e25e9a331574074e6657717d67c, verified using Git data and checkout logs. Base remains #742 at e834d3c28c87ad496989af925515cf21babd308d; dev and the original PR branch were not modified.

Retained red/green history

  1. Test-first control e26be5e3 — PR Fast 38110719730, job 114385529147: Release build 0 warnings/errors; UnitTests 2016 passed / 2018 total, exactly two failures and zero skips. Both AbortCleanupMustPinTheKeyUntilKeyedBudgetCleanupFinishes(false/true) reported old failure reached replacement: True. A drain-notification scheduler serviced the sole writer during old Abort, reopened the same key, and the old key-scoped preparation cleanup rejected the replacement with the identical old exception. No sleep or production-only test hook is used.
  2. Runtime fix 6181f03c — PR Fast 38111130100, job 114386748429: 2019/2020 passed. Both original regressions and the nested-abort case passed. The sole failure was my newly added helper-checkpoint test incorrectly requiring immediate IsCompleted, despite the inner rejection resuming an async wrapper.
  3. Test correction 1c76ad13 — PR Fast 38111320489, job 114387315465: 2020/2020 UnitTests, 269/269 Generator.Tests and 77/77 LoadTest.Tests, zero failures/skips; Release build 0 warnings/errors. Formatting, reference boundaries, maintainability and the existing allocation gate also passed. This commit changes no runtime code. The checkpoint now awaits the exact abort error within the same 10-second bound while the independent stream STILL holds all preparation capacity; a lost rejection therefore still fails by timeout.

The original RequestFence/regression test file is byte-identical between red and green: Git blob 9f100cd1ebaa1eadb9dd97b8e7a9285fb046f7b6. No workflow, existing threshold or existing test population was changed. Runs were not retried to green.

PR Package Smoke 38111320511, job 114387325716, also completed successfully: build, ProjectReference trim-only smoke, isolated NativeAOT smoke, pack, package contract checks and package smoke. This is package/native smoke coverage, NOT an executed full B3 RPC performance test.

Implementation

  • Reuse ProducerActive as a lifecycle-use word: the low bit owns the producer and each abort cleanup pins two upper units. Retirement still requires zero uses. Producer release clears only its bit, so it cannot erase an in-progress cleanup pin. No extra per-stream field, lock, allocation or hot-path Interlocked is added by this fix.
  • Keep the old identity pinned through key-scoped preparation cancellation and buffer returns, including nested notification reentry.
  • Register a preparation waiter before rechecking the published abort error; this closes abort-before-budget-join without changing the flow-credit algorithm. Its additional test is explicitly an actual-helper checkpoint, not a claimed full asynchronous producer interleaving.
  • Add OpenAfterRequestEmissionAsync and four tests: pending/failed/canceled Request emission must not create a ready-stream identity. Canceling the observer does not cancel the pump's durable Request ownership; a late fault is observed.

Remaining production scope

The writer-owned scheduler and these fences are implemented, but normal generated/client RPC producers are still unchanged. They must be connected to ONE send-credit authority per connection, with compression/deadline/publication and DATA/terminal ordering validated end to end. The current scheduler retains both stream and connection windows and uses item-level round robin; it is not the historical quantum-16 B3 benchmark configuration. No new throughput result or production-ready claim is made. Keep this PR Draft.

@SunSi12138 SunSi12138 changed the title [WIP #742] Integrate writer-ready SendPump handoff and run existing CI [WIP #742] Integrate writer-owned stream scheduling and RPC handoff Oct 11, 2026
… bridge

Add an immutable internal connection-level opt-in. The selected session routes WindowUpdate, cancellation and shutdown to its single writer-owned sender while the original flow controller remains the receiver. Producer-side legacy credit and raw StreamData/StreamComplete bypasses are rejected rather than silently running a second connection ledger.

The real generated sender now performs codec/snapshot/compression work on the producer, charges decoded item bytes to flow credit and actual serialized bytes to preparation/queue budgets, and sends EOF/mapped error through the same ordered stream lane. Terminal completion joins accepted frames; the client-role session helper fences its first DATA behind an ordinary Request-emission barrier.

Add 15 real-session integration cases (14 methods, one two-value parameter) for generated sized/unsized sending, zero/empty data, exhausted/shared credit, actual compression, cancel/shutdown, mapped errors, Request ordering and bypass rejection. Existing workflows and assertions remain unchanged. This staged mode is NOT the default, and the full ClientConnection adapter, deadline hardening and integrated performance acceptance remain pending. Do not interpret a legacy-suite pass as those pending paths being validated.
…y extraction

PR Fast 38115391655 stopped at the debt gate because RpcSession.cs is now 752 LOC, below the unchanged 800 LOC rule. Remove only its old 862-LOC exception, as the existing guard requires. All thresholds and other allowances remain byte-for-byte unchanged; no workflow or test is modified. The earlier allocation gate passed; functional tests were skipped by the maintenance failure and are not counted as passed.
…dline cleanup

Route the actual ClientConnection item loop and its success/error terminal through the selected connection's writer-owned lane. Retain the existing pending-call progress claims and hoisted codec capability. Register immutable-handle cancellation after first publication so a blocked user MoveNext cannot retain unadmitted prepared DATA; clean a canceled first serialization before its handle has been returned.

Extract the generated stream pump as a lifecycle unit. Cover the entire EOF operation with the frozen deadline, including capacity/Request predecessor waits, and preserve prompt deadline completion when user disposal ignores cancellation. A service-thrown DeadlineExceeded before the framework clock boundary remains a business error and must keep its previously accepted DATA ordered before the mapped error terminal.

Add six real ClientConnection cases and six real-session deadline/cancellation cases. Existing legacy assertions, workflows and limits remain unchanged; the internal writer-owned option remains disabled by default pending full transport/duplex/native/performance validation. The previous generated-session integration has passed PR Fast on 5ba577c; this new tree requires its own CI.
… memory

Carry the staged internal sender selection through the existing immutable runtime-option snapshots and session construction. It remains instance-scoped and disabled by default; no public API, protocol bit, receive window default or existing workflow is changed.

Add 13 full-path cases: six mixed/new sender interoperability cells across TCP/SharedMemory covering generated unary, server/client/duplex and named request streams; c8 bounded-window duplex, compressed byte-array duplex, early consumer disposal, and snapshot-isolation controls. The transport fixtures use normal public builders, generated proxies and the actual server dispatch loop, then inspect the real established sessions to require that the intended sender was exercised rather than a silent legacy fallback.

Prior client/session integration on 57ced2e passed PR Fast. These new full-transport tests require separate CI and are correctness controls, not throughput measurements. Explicit 64/512-byte windows are limited to this stress fixture.
…t-waiting packets

PR Fast 38117157331 built with zero warnings/errors, but all twelve new transport cells stopped at client Build because the fixture omitted the required request-timeout policy. Select the ordinary recommended policy; keep the existing 20-second outer bound, all cells and every assertion unchanged. The snapshot-isolation cell and all 2047 prior tests passed.

Add a deterministic actual-Enqueue red control: with one occupied stream slot and a 48-byte preparation budget, a second retained 24-byte packet waits for space while PreparedBytes incorrectly remains 24. The expected accounting is 48 until that waiting packet is canceled or transferred. This revision intentionally does not change the scheduler; retain its failure before fixing admission order. No workflow or production default changes.
…ream slots

PR Fast 38117572272 deterministically failed PacketWaitingForAStreamSlotMustAlreadyOwnPreparationBudget: the actual Enqueue retained two serialized 24-byte packets while only 24 bytes were charged. Preserve this test unchanged.

Acquire the existing global preparation budget before waiting for the stream-local queue slot. Keep the producer/abort cleanup pin, cancellation, packet-return and exactly-once budget release logic unchanged. The writer never needs preparation capacity to remove an older frame, so the reordered reservation does not require a new queue, lock or wake authority.

The same control run had twelve separate transport-fixture registration failures before RPC; these are not evidence of transport behavior and require generator-enabled fixture assembly wiring. This commit fixes only the confirmed budget defect; it is not a full production/performance acceptance claim.
…PC generator

The corrected timeout fixture in PR Fast 38117572272 reached connection setup, then all twelve transport cases stopped before RPC because UnitTests does not run the RPC source generator. Keep that failure record rather than attributing it to B3 runtime behavior.

Move only the transport contract/service into a dedicated non-packable test project referencing the actual generator as an analyzer. Add its solution/project references and initialize its normal generated module before builder catalog snapshots. Do not enable the generator on UnitTests, which contains intentionally malformed metadata fixtures. Preserve all thirteen transport/snapshot cases, their assertions and their existing deadlines/windows. The preceding commit independently fixes the deterministic slot-budget defect; its original red regression is unchanged.

No workflow, product default, timeout threshold or test population was relaxed. This tree requires its own CI; no full-transport or performance pass is claimed yet.
… A/A controls

Stage additive evidence without modifying the existing PR Fast, Package Smoke or acceptance workflows. Copy identical generated-ABI and environment harness sources to frozen dev before building separate binaries. Require opt-in on both endpoints and verify the real connected sender mode; benchmark dev A, dev B, PR legacy and PR writer-owned in rotating order for TCP and SharedMemory c1/c8/c32.

Measure RPC throughput, CPU, allocated B/RPC and concurrent-batch latency in fresh warmed processes. Preserve every raw result, stderr/stdout, process position, exact source SHA and failure. The 4-position diagnostic median does not establish formal performance Go; NativeAOT and further contention remain outstanding. This is a benchmark-only instrumentation change; production protocol/windows/default mode and all existing tests/thresholds remain unchanged.

Copy link
Copy Markdown
Owner Author

Exact-head CI: two cancellation failures; matched performance evidence in progress

Head 39f27f08d9704e19b5ec7523f4a9f7494fe828cc adds only benchmark instrumentation (identical shared harness, explicit B3 selection/active-session assertion, all raw records retained) and an additive performance workflow. It does not change shipping runtime, flow windows, production mode defaults, any existing workflow or any regression assertion.

PR Fast run 38140928742: Release build 0 warnings/errors; allocation gate passed; 2059/2061 UnitTests with two failures, both existing cancellation tests:

  • WriterOwnedClientConnectionTests.PendingCallCancellationMustCleanActualClientPreparation: call cancellation leaked queued preparation or closed the physical connection.
  • WriterOwnedSessionIntegrationTests.RemoteCancelMustUnblockPreparedStreamWithoutFaultingConnection: remote abort did not reach the actual producer/drain.

They previously passed on an earlier run, but that is no reason to discard this evidence. Treat as a potential race and a production No-Go, not runner noise or a reason to weaken assertion/timeouts.

Package Smoke 38140928744 passed, including its isolated NativeAOT smoke (NOT a generated B3 native performance result).

Matched JIT E2E 38140928821 successfully built frozen dev and candidate harnesses, completed explicit B3 real-generated-RPC smoke, and is collecting the full dev-A/dev-B, PR-legacy, PR-B3 rotating TCP/SharedMemory c1/c8/c32 population. This report is not yet complete; no throughput outcome is claimed from a running job. Keep #803 Draft and do not merge.

…anged test oracles

PR Fast 38140928742 found two existing cancellation tests failing on exact candidate 39f27f0 (2059/2061). Add diagnostic state (connected, prepared bytes, retained streams, delivered error) to their failure messages without changing either condition, test deadline, scheduler, flow-control implementation or test population. Add an independent eight-full-suite-process stress workflow that archives every failed and passed log, and fails on any error. Existing PR Fast, packaging, performance evidence and product defaults remain untouched. Do not classify the observed failures as noise or a performance Go. Earlier 416/416 JIT E2E measurements stay pinned to 39f27f0 and must not be relabeled.

Copy link
Copy Markdown
Owner Author

First real generated-RPC B3 performance A/B — complete collection, production No-Go

Pinned subject: candidate 39f27f08d9704e19b5ec7523f4a9f7494fe828cc vs dev 0fe26024b114bb6e78411a9b86276086c045d03d, and the SAME candidate with writer-owned mode disabled.

Workflow 38140928821: successfully collected 416/416 raw JIT process records, 0 failed measurements. Artifact 11703138985 (writer-owned-rpc-39f27f08...-attempt1) retains all JSON, each process's stdout/stderr, order, source and harness checksums, A/A and environment. Collection success is not a production Go.

Same generated-ABI benchmark source compiled against fixed dev and candidate; three modes dev legacy, candidate legacy, candidate B3, plus second dev A/A arm. Real generated RPC, TCP + SharedMemory, concurrency 1/8/32, separate process per arm, four rotating repetitions, JIT TieredPGO=1, four logical CPUs. B3 mode is explicitly selected and verified on the actual connected client session, not inferred from the PR SHA.

Median c1 RPC/s delta B3 vs pinned dev (negative means slower):

Scenario TCP SharedMemory
Server1x16 (short) -15.49% -15.65%
Server100x16 -24.03% -5.27%
Client100x16 -8.17% -14.33%
Duplex100x16 -31.18% -32.82%
Server100x4096 -4.26% -4.71%
Client100x4096 -5.14% +5.90% (dev A/A +8.52%, inconclusive)
Duplex100x4096 -14.24% -25.90%

Across all 26 transport/shape/concurrency cells, 25 median throughput deltas are negative. Hosted runner A/A variance, especially c32, prevents asserting statistical significance for every cell. But TCP c1 Server100x16 and Duplex100x16 regress in all four paired rounds; B3 vs candidate's own legacy mode also regresses (-18.09%, -32.26%). CPU/RPC often rises substantially (TCP Duplex100x16 c1 +53.81%). Allocations show ~+3004 B/RPC TCP short Server1x16, +5976 B/RPC TCP Server100x16, +11491 B/RPC TCP Duplex100x16 relative to dev; production B/item requirement is not met. A/A and exact per-arm distributions remain in artifact; no historic isolated B3 +60–70% figures are relabeled.

Potential mechanisms to profile, not validated causal attributions: eagerly allocated StreamHandle/queue/node per stream, producer-side asynchronous budget/dispatch per item, client Request pre-flush predecessor barrier, and item-level RR rather than historic q16. Do not optimize blindly or change window, fairness and on-wire meaning for an attractive micro-score.

Formal decision: No-Go, keep PR Draft and writer-owned mode default-off. PR Fast on same subject 38140928742 also recorded 2 existing cancellation failures (2059/2061). A follow-up 8ac4c849 preserves exact assertions, adds contextual failure messages and an additive eight-full-suite stress workflow; its different CI runs are NOT evidence for 39f27f08 or a reason to discard old failures. Package Smoke at 39f passed; isolated AOT smoke is not full native generated-RPC B3 performance.

No existing gate, population, or runtime default was weakened.

Copy link
Copy Markdown
Owner Author

Closing this B3 production-integration experiment at maintainer request (No-Go). Real generated RPC JIT measurements against pinned dev showed material regressions (notably one-concurrency 100×16 B duplex approximately -31% TCP and -33% SharedMemory); follow-up full-suite cancellation stress failed in 3 of 8 runs, including a 42-byte retained prepared-frame observation and wrong remote-cancel exception. Passing single-run PR Fast or package/native smoke does not waive correctness or throughput failures. Stop pursuing this candidate; retain commits, measurement artifacts, and failed-run logs for provenance. This does not close or approve parent #742. Any separate route-footprint improvements and any credit-conservation correctness correction must be validated on current dev independently.

@SunSi12138 SunSi12138 closed this Oct 11, 2026
SunSi12138 added a commit that referenced this pull request Oct 11, 2026
… full-suite failures

Keep Open rejection semantics intact and reuse the existing immutable lease only for terminal delivery. Preserve all four deterministic red/green assertions and the first run's three full-suite failures (38163848907). This remains an isolated cause experiment for closed #803, not a production repair.
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