Skip to content

[Runtime Architecture][Phase 05] 以 NegotiatedSessionOptions 一次性发布握手结果 #73

Closed

Description

@SunSi12138

总跟踪:#67
分析基线:main @ 201b1621cc706ee221e10ac20947af5fc62b087e
阶段依赖:Phase 02 #70
建议交付:一个聚焦 PR;可拆为多个 commit,但合并时不得保留新旧双路径。

Use case

capability、frame limit、flow-control window、compression binding 当前通过多个字段/setter 逐项写入。新增 capability 时,Client/Server 多分支必须以正确顺序设置全部字段,容易出现 Ready 已发布但 negotiated state 不完整的组合。

Proposed behavior

本阶段按下述工程原则和任务清单实施。目标不是增加抽象数量,而是减少非法状态、所有权分支、并发状态组合与测试不确定性。

通用工程原则(本阶段必须遵守)

  1. 让非法状态不可表示。 构造完成的对象必须立即合法;握手结果、BuildPlan、拓扑和其他稳定配置使用 immutable snapshot 一次发布,不依赖调用顺序完成二次绑定。
  2. 单一所有权、单一终态。 每个 transport、counter、lease、timer、后台任务和构建期资源都必须写明谁创建、谁拥有、哪个终态释放、停止或失败时由谁等待/观察;竞争终态必须由同一个 winner 收敛,禁止重复释放和 underflow。
  3. 分离状态域。 Session 的 transport/protocol 状态、ClientConnection 的新调用资格、ServerConnectionState 的认证/调用排空状态分别建模,不用一个布尔值或一个巨型状态机互相冒充。
  4. 并发必须有界且可监督。 不引入无界队列、无限重试或无人等待的 fire-and-forget;framework-owned detached task 必须登记、观察异常,并在 owner 停止时可 drain。批量 drain 前先关闭 owner-level 写入门槛。
  5. 统一时间语义。 UTC 只服务 wire deadline、日志、Activity 和公开诊断;本地 deadline、elapsed、heartbeat、retry、breaker、drain 和 shutdown budget 使用同一 TimeProvider 的 monotonic timestamp,并能用 fake time 确定测试。
  6. 资源构建是事务。 Builder 先编译完全验证且不可变的 plan,再 materialize resource,最后 commit 所有权;失败时保留 primary exception,并按登记逆序恰好清理一次。Caller-owned 资源不得被框架擅自释放。
  7. 保护热路径。 继续读取 immutable snapshot,不增加全局锁;supervisor、BuildPlan、ManifestSource 查询不得进入每次 RPC/每帧/每 item 路径;不得引入无依据的每调用 allocation 或新的锁竞争。
  8. 明确兼容性边界。 默认保持 Protocol v2 wire format、Contract ID/Method ID/DTO member ID 与 schema 规则;CLR/source API 和 Generated ABI 可以 breaking,但必须显式升级版本、注册/启动阶段 fail fast,且不保留 legacy constructor、Default Context、reflection adapter 或旧 ABI 转接层。
  9. 增量实施、独立验证和回滚。 每个阶段对应一个聚焦 PR,可单独测试、基准和回滚;不一次性重写 Runtime,也不为了目录整齐或抽象形式扩大范围。
  10. 保留已经正确的并发模式。 例如 StreamManager.CompleteAll 先 CAS 发布终态再逐 stripe drain、PendingCall 单槽位终态竞争、dispatcher acquire/close/detach lease;不得以“统一”为名改成全局锁或 ConcurrentDictionary 全面替换。
  11. 测试与诊断先行。 生命周期变更必须有竞态、fault-injection、fake-time、stress/chaos、ALC/NativeAOT(按影响范围)测试;触及热路径时必须提交 BenchmarkDotNet 前后数据。
  12. 每个新增可变状态都回答四个问题。 谁创建?谁拥有?哪个终态释放?停止/失败时如何被等待和观察?无法用一两句话回答时不得合并。

本阶段任务

  • 引入 immutable NegotiatedSessionOptions,至少包含 protocol minor、capabilities、max frame payload、stream/connection receive window、compression binding。
  • Session 仅暴露 internal TryCompleteHandshake(NegotiatedSessionOptions):验证不超过 local limit、配置 flow controller、固定 compression、原子发布 snapshot、Handshaking -> Ready,并拒绝二次发布。
  • 热路径通过一次 Volatile.Read(ref _negotiated) 获得完整快照;如需优化,在握手后由 SendPump/Parser 缓存引用,不得重新拆回多组 mutable 字段。
  • 迁移期间可短暂保留旧字段,但必须只由 snapshot 填充并有一致性断言;PR 最终删除 negotiated setter 和双写路径。
  • 集中 phase/frame eligibility:Handshaking 仅 handshake/error;Ready 允许业务与控制帧;Draining 禁止新 Request 但保留已有调用所需 Response/Stream/Cancel/WindowUpdate/Heartbeat/GoAway;Stopping/Terminal 只清理。
  • 握手失败不得发布 Ready;第二次 response、畸形/越界参数和 compression/flow 初始化失败必须有确定终态。

交付物

  • NegotiatedSessionOptions 与一次性 handshake completion API。
  • 集中 phase/frame matrix 和删除零散 setter。
  • negotiated publication、失败和二次发布测试。

验收标准

  • negotiated state 只发布一次,Ready 可见时 snapshot 必然完整且不可变。
  • flow controller 与 compression binding 只由 handshake completion 固定。
  • 二次发布、越界参数和握手失败均不会进入 Ready。
  • Draining 禁止新 Request,但已有调用必须的控制帧仍可发送/接收。
  • 无 negotiated 零散 setter 或双写兼容路径残留。

本阶段补充验证

  • capability/frame/window/compression 组合与二次发布 race。
  • Protocol v2 golden、SendPump、StreamManager、RpcSession lifecycle 与 integration tests。
  • 基准验证 snapshot 读取没有每帧 allocation/明显回退。

通用验证门禁

基础门禁(按本阶段影响范围运行,并在 PR 中记录命令与结果):

dotnet build Sharplink.slnx -c Release
dotnet test --project test/SharpLink.UnitTests/SharpLink.UnitTests.csproj -c Release
dotnet test --project test/SharpLink.Generator.Tests/SharpLink.Generator.Tests.csproj -c Release
dotnet run --project test/SharpLink.IntegrationTests/SharpLink.IntegrationTests.csproj -c Release -- --timeout 120s

触及 Session、Transport、TimeProvider、DynamicModule 或 generated ABI 时,补充相关的 SharpLink.AotSmoke、SharpLink.ChaosTests、TransportConnectionIntegrationTests、RuntimeAssemblyIntegrationTests、RuntimeMultiClusterIntegrationTests、SendPumpTests、RpcSessionLifecycleTests、StreamManagerTests。

若改动进入 Unary 调用、PendingRequestTable、StreamManager、SendPump、P2C 或 codec resolve 热路径,必须提供 BenchmarkDotNet 前后对比:不得无说明增加每调用 allocation;吞吐回退超过基准噪声(CI 稳定前人工参考 3%–5%)时阻塞;p99 不得出现由新抽象引起的锁竞争。

Compatibility and performance impact

不改变 Protocol v2 wire;这是 Runtime 内部状态发布方式变化。任何 public/internal setter 删除都不提供 shim。新增 snapshot 不得产生每帧 allocation。

风险与回滚

风险是改变 handshake 时序或漏掉某个能力字段。先用现有算法组装 snapshot,再删除 setter;golden/integration tests 锁定 wire。出现问题可回滚本 PR,不回到长期双写。

Out of scope

不集中 Client/Server 协商算法(Phase 06),只建立唯一结果与发布边界。

Alternatives considered

不采用长期保留 legacy constructor、obsolete forwarding overload、Default Context、Runtime reflection adapter、旧 Generated ABI adapter、无界任务/队列或全局锁的方案。若真实扩展需求出现,应设计窄而稳定的新 SPI;若阶段实现需要扩大到无关 package/子系统,应另开 issue 并给出可验证收益。

Checks

  • 已搜索现有 Issues 与当前文档,未发现同一实施阶段的重复 issue。
  • 本 issue 保留了原实施方案的阶段任务、通用原则、验收、兼容性、性能和回滚约束,并补充了可执行细节。

Activity

  1. SunSi12138 commented on Aug 10, 2026

    @SunSi12138
    OwnerAuthor

    Implemented by #108 and merged into dev at c8e3863. Ubuntu bare-metal validation, Quick, CodeQL, and the exact-head cross-platform Release Gate all passed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions