Skip to content

chore(release): promote reviewed dev fixes for 2.0.4 - #804

Merged
SunSi12138 merged 14 commits into
mainfrom
release/2.0.4-main
Oct 11, 2026
Merged

SunSi12138 merged 14 commits into
mainfrom
release/2.0.4-main

Conversation

@SunSi12138

Copy link
Copy Markdown
Owner

Scope

Promote the nine existing dev commits through eb99fe8 to main while preserving both branch histories. Main's tree was verified identical to the merge-base tree a14e212; the candidate contains only those dev changes and 2.0.4 release metadata.

Updates VersionPrefix, the reviewed release calibration version, the generator version snapshot, and CHANGELOG. Public API, protocol, and generated ABI version axes remain unchanged.

Validation and release sequence

  • Existing dev-tip Nightly and CodeQL passed; these are not substitutes for final-candidate verification.
  • Run the canonical main-targeted Release Gate and required checks on this PR.
  • After the authorized main merge, rerun Release Gate, full performance validation, ten-minute release soak and ABI churn on the exact final main SHA.
  • Create v2.0.4 and the GitHub Release only after all required gates pass, then verify eight packages and matching symbols through the existing OIDC publication pipeline.
  • The limited 2.0.3 performance exception does not apply to 2.0.4.

No unmerged 3.0 feature PR is included. PRs #799, #802, and #754 remain outside this release. No tags or packages have been published yet.

SunSi12138 and others added 13 commits October 9, 2026 11:58
Restore ServerCallAdmissionHotPathBenchmarks after the connection registry refactor, add setup smoke coverage and full BenchmarkDotNet evidence, and keep the RPC session flush A/B base harness buildable.

Validated head 06ff132: PR Fast, PR Extended with codec compatibility, CodeQL, codec padding, pending/codec regression checks, RPC session flush A/B, and full server call admission BDN evidence passed. BDN measured 44.24 ns/op and 0 B/op on its runner.

Fixes #756.
)

* perf(abstractions): shrink RpcMethodDescriptor from 40 to 32 bytes

`RpcMethodDescriptor` is a `readonly record struct` passed **by value** through every
client invocation boundary — the generated proxy, `IRpcChannel`, the call-control
preamble, the retry selector and the unary/streaming cores. It measured 40 bytes.

The width came from `TimeSpan? MethodTimeout`, whose `Nullable<TimeSpan>` form occupies
16 bytes (8-byte value + presence + padding) and pushed the struct past the 32-byte
boundary:

    long ContractId + long MethodId + Nullable<TimeSpan> + int + byte + byte = 38 -> 40

Storing the timeout as a tick count with a negative sentinel removes 8 bytes:

    long ContractId + long MethodId + long ticks + int + byte + byte = 30 -> 32

The public surface is unchanged: `MethodTimeout` still reads and writes `TimeSpan?`, and
record equality is preserved because the tick field is a 1:1 encoding of the property.

One non-obvious constraint is documented inline: the backing field must be **declared**
ahead of the `int ClientStreamCount` and the `byte` members. The struct uses sequential
layout, so declaring the `long` after them re-introduces the padding and the descriptor
stays at 40 bytes no matter how the timeout is stored.

This is a layout/ABI-tightening change. Measured on the issue #734 ablation, the 40 -> 32
transition produced no resolvable throughput change (the by-value cost is dominated by the
call and interface dispatch rather than the copy width), so it is proposed for descriptor
compactness and not as a speed-up.

Validation:
- `Unsafe.SizeOf<RpcMethodDescriptor>() == 32` (was 40).
- `SharpLink.UnitTests`: 1854/1854 pass with only this change applied on top of `dev`,
  including `RpcMethodDescriptorTests`, which pins flag packing, both `Deconstruct`
  shapes and a `with { HasClientStreams = false }` record copy.

Refs #734

* test(abstractions): preserve timeout through combined with flag updates

---------

Co-authored-by: SunSi12138 <sunsi12138@users.noreply.github.com>
…751)

`InvokeUnaryWithOptionalRetryAsync` and `CaptureRetryGenerationForInvocation` both
start with `method.Kind != RpcMethodKind.Unary || !method.IsIdempotent`.

The Kind conjunct cannot be true on any generated call path:

- `SharpLinkClient.InvokeUnaryWithOptionalRetryAsync` is only reached from
  `InvokeUnaryAsync` (SharpLinkClient.Invokers.cs), and
  `CaptureRetryGenerationForInvocation` is only reached from
  `ResolveCallControlForInvocation`, which every entry point calls with its own
  descriptor.
- `RpcGenerator.ProxyEmitter.GetMethodKind` maps `RpcMethodKind.Unary` 1:1 onto
  `InvokeUnaryAsync` (no other entry point emits Kind == Unary), so the descriptor
  that arrives here always carries Kind == Unary.

Removing the dead conjunct leaves the retry decision driven solely by
`IsIdempotent`, which is the documented behaviour (`doc/public-rpc-semantics.md`).

Measured on the issue #734 harness (isolated descriptor-mechanism ablation,
batch-timed at 1 ns resolution, median of four runs of the same binary):

  with the Kind conjunct     17.90 ns/call
  without the Kind conjunct  14.60 ns/call
  delta                      -3.30 ns/call

Validation: SharpLink.UnitTests 1854/1854 pass with only this change applied.

Refs #734

Co-authored-by: SunSi12138 <sunsi12138@users.noreply.github.com>
Replace this manually timed test's polling and independent read timeout with cancellation-aware semantic barriers. Cover deferred reader scheduling and assert the original deadline, exact ordered frames, one timed flush, and resource cleanup.

Refs #387. This addresses only the timed send-batch test subcase; the tracking issue remains open.
Remove the unreachable singleton fallback, cache registration module presence, reuse resolved method descriptors across dynamic acquisition/telemetry and intercepted call-context creation, and allocate singleton gates only for singleton registrations. Preserve singleton construction/disposal synchronization and public API/protocol behavior.

Add regression coverage for dispatch invariants, module lease cleanup, gate allocation, descriptor query counts, and the no-interceptor fast path.

Refs #755.
Split the existing OneWay invocation core into plain and streaming async state machines after one descriptor check. Preserve public/generated APIs, wrappers and lifecycle ownership.

Add descriptor-shape regression coverage for producer ownership, flags, owned cancellation and cleanup. Paired hosted evidence shows approximately 48 B/op saved for suspended plain calls and 8 B/op for streaming calls, with no latency claim.

Related to #729. Research evidence remains in unmerged Draft #795.
Refs #737.

Reuse the producer deadline snapshot and validate full request identity without acquiring the completion gate per item; keep expiration on PendingRequestTable's authoritative terminal path.

Accepted after review for measured local CPU savings with unchanged controlled allocation. Full-RPC gains remain unresolved within hosted-runner variability; this merge does not claim proven end-to-end improvement or full-workload non-regression.

Exact candidate 72f2f97 on dev 130fae6 passed PR Fast, allocation, PR Extended, package, CodeQL, pending/codec, extension containment, padding, RPC and JIT/NativeAOT evidence checks.
Refs #740.

Restore dev ownership logic and use the BCL pooling async builder only for AwaitReadAsync. Migrate valid ownership, cancellation, fault, context, retention and real-transport tests; preserve accepted continuation-behavior differences explicitly.

Accepted for simpler implementation and scoped warm-serial allocation savings under the standard ValueTask contract. Burst allocations and reported c32 tail-latency limitations remain; this does not claim all-workload zero allocation, throughput non-inferiority or automatic PGO gains.

Reviewed head 37efdbe, tree 324abbc. PR Fast/allocation, fresh canonical PR Extended, Package Smoke and CodeQL pass. Hosted validation includes 2018 unit, 269 generator, 77 load and 453 integration cases.
@SunSi12138
SunSi12138 marked this pull request as ready for review October 11, 2026 12:25
@SunSi12138
SunSi12138 merged commit 85f5f87 into main Oct 11, 2026
66 checks passed
@SunSi12138
SunSi12138 deleted the release/2.0.4-main branch October 11, 2026 12:25
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