Skip to content

[Runtime Architecture][Phase 09] 迁移 heartbeat、reconnect、retry、breaker、drain 与 shutdown 到统一时间源 #77

Closed

Description

@SunSi12138

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

Use case

Phase 08 只统一调用 deadline 和两个 scheduler;Heartbeat、Reconnect、Retry、Circuit Breaker、DynamicModule drain、Server cleanup budget 等仍散落使用 Task.Delay、Stopwatch、UTC、Timer 或 CancelAfter。它们必须共享 RuntimeContext monotonic time,才能消除 wall-clock 影响并实现全链路确定测试。

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. 每个新增可变状态都回答四个问题。 谁创建?谁拥有?哪个终态释放?停止/失败时如何被等待和观察?无法用一两句话回答时不得合并。

本阶段任务

  • 逐 subsystem 迁移 heartbeat send/check 与 timeout、reconnect backoff/jitter、retry delay/deadline gate、circuit breaker open/half-open、admission delay gate、dynamic module drain、graceful shutdown/framework cleanup budget。
  • 所有 elapsed/deadline/budget 计算使用 RuntimeContext TimeProvider monotonic timestamp;Task scheduling 使用 provider-aware delay/timer。
  • 复用 Phase 08 的同一 RpcDeadline,Retry/Admission 在开始等待前判断是否会越过 deadline,不能创建第二套剩余时间。
  • 对每个 subsystem 建立 fake-time tests:推进到边界前/边界/边界后,取消与 stop 竞争,多实例不同 provider 隔离。
  • 保留 System TimeProvider integration/stress,确认真实 timer、线程调度和 stop 顺序。
  • 评估 reconnect jitter 与 P2C Random.Shared;仅当测试可重复性确有需要时引入极小 internal IRandomSource,不做通用随机 service locator,也不进入 codec/protocol 热路径。
  • 全仓库审计 local timeout 对 DateTimeOffset.UtcNow、Stopwatch.GetTimestamp、裸 Task.Delay、new Timer、CancelAfter 的剩余引用并分类;wire/diagnostic 合理引用保留并注明。

交付物

  • Runtime 子系统统一 monotonic timing implementation。
  • 全仓 timing reference 审计与允许项清单。
  • Heartbeat/Retry/Breaker/Drain/Shutdown fake-time 与 System integration tests。

验收标准

  • 本地 deadline、heartbeat、retry、breaker、drain、shutdown 不依赖 wall clock。
  • 每类子系统至少一组 fake-time 确定测试,并有 System provider 集成覆盖。
  • 多 RuntimeContext 的时间推进互不影响。
  • 允许保留的 UTC/Stopwatch/Delay/Timer 引用都有 wire/diagnostic/平台理由。
  • 无新的无界 backoff/retry 或未监督 timer/task。

本阶段补充验证

  • Heartbeat、Reconnect、Retry、Breaker、DynamicModule、Server stop 单测与 integration/chaos。
  • 对频繁 elapsed/check 路径提交 allocation、throughput、p99 对比。

通用验证门禁

基础门禁(按本阶段影响范围运行,并在 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

不改变 wire deadline、retry public policy 或 breaker externally documented semantics,除非修复已有 wall-clock bug并在迁移文档说明。随机源若加入保持 internal。

风险与回滚

风险是 fake time 与真实 scheduler 差异、停止预算语义变化。按 subsystem 小步迁移并保留 System tests;可回滚单一 subsystem,但不恢复 UTC 作为本地 deadline 基准。

Out of scope

不重写 retry/breaker 算法,不把 TimeProvider 查询放进每帧或每流 item 热路径。

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

    已由 #118 完整实现并合并到 dev(merge commit b44b735)。统一 TimeProvider 迁移、确定性边界测试、构建/生成器/集成/AOT/打包/负载与 Chaos 门禁均已通过。

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