Skip to content

[Runtime Architecture][Phase 17] 提升 Generated ABI/Manifest 版本并对旧 artifact 启动期 fail fast #85

Closed

Description

@SunSi12138

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

Use case

SDK/Runtime 解耦和 public control surface 裁剪会形成新的 Generated ABI。若 Runtime 同时保留 old/vNext adapter,会把兼容成本推入注册甚至调用热路径,并长期背负历史 engine API。Generated ABI 与 Protocol wire version 必须分开管理。

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

本阶段任务

  • 定义并提升 SharpLinkGeneratedManifestVersions.Api 或新的 Generated ABI version;记录 version 的适用程序集、manifest/stub capability 与兼容规则。
  • 当前 Runtime 只接受 vNext generated artifact;在 manifest registration、assembly load 或 host startup 早期校验版本。
  • 旧 artifact 返回明确 incompatibility error,包含 expected/actual version 与“使用当前 generator 重新生成/重新编译”的行动说明;不得拖到首次调用出现 MissingMethodException。
  • 删除 old ABI reflection/forwarding/compatibility adapter、legacy behavior branch 和旧 generated runtime entrypoint。
  • Generator、Runtime、Client/Server/Hosting 与 sample artifacts 同步升级;NativeAOT smoke 必须由当前 generator 重新生成。
  • 增加 version matrix tests:current-current 成功,old-current/current-old(若可构造 fixture)在注册/启动阶段失败,错误稳定;Protocol v2 cross-process/golden 仍兼容。
  • 更新 migration 文档,明确 Generated ABI version != Protocol wire version,升级包后需重新生成 artifacts。

交付物

  • vNext Generated ABI/version contract 与 early validation。
  • old adapter 删除、错误诊断和 version matrix tests。
  • 重新生成的 samples/NativeAOT artifacts 与迁移说明。

验收标准

  • Runtime 只接受 vNext generated ABI,不包含 old adapter/legacy branch。
  • 旧 artifact 在注册/启动阶段以明确 version mismatch 失败。
  • 当前 generator artifacts 在 JIT/NativeAOT、Unary/streaming、dynamic assembly 中通过。
  • Generated ABI 升级不改变 Protocol v2 wire golden 或 contract/schema ID。
  • migration 文档给出重新生成步骤和错误示例。

本阶段补充验证

  • Generator tests、RuntimeAssembly/MultiCluster integration、NativeAOT smoke、Protocol golden/contract compatibility。
  • 检查 version validation 不进入每次调用热路径。

通用验证门禁

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

Generated CLR ABI 有意 breaking;Protocol v2 wire 和 RPC schema 继续兼容。NuGet binary compatibility 对新 major 建立新 baseline,不要求兼容旧 major。

风险与回滚

最大风险是 version check 太晚或遗漏某个 loader。列出所有 registration/load entrypoint 并负向测试;发现旧 artifact 用户需求时完善诊断/迁移,不加入 adapter。

Out of scope

不设计 wire protocol upgrade,不维持跨 major Generated ABI compatibility layer。

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

    Implementation Supplement — Phase 17 执行手册(基于 dev,保留原正文)

    重要修正:当前 dev 已经完成一轮 Generated ABI bump 与 early-fail 基础设施。 本阶段不能再从“Api=3、locator只有 Type、loader必须先 Activator”这个历史 main 假设出发。第一步应判断 #84 是否真的产生新的 Generated ABI delta;没有 ABI delta 就不要为了 issue 标题机械把 4 再改成 5。

    0. dev 当前已经具备的机制

    当前:

    SharpLinkGeneratedManifestVersions.Api      = 4
    SharpLinkGeneratedManifestVersions.Protocol = 2
    

    Generator 已生成 self-describing locator:

    SharpLinkGeneratedAssemblyManifestAttribute(
        manifestType,
        apiVersion: 4,
        protocolVersion: 2,
        generatorVersion)
    

    并且 manifest 自身也返回 literal ApiVersion = 4 / ProtocolVersion = 2。

    SharpLinkAssemblyManifestLoader.TryLoad 已经在实例化 manifest 之前读取 CustomAttributeData:

    1. legacy 单参数 locator 直接分类为 API 3 legacy;
    2. 4 参数 locator 先读取 api/protocol/generator;
    3. metadata mismatch 先返回 IncompatibleManifest;
    4. 只有 metadata compatible 才 Activator.CreateInstance;
    5. materialize 后再由 SharpLinkGeneratedManifestCompatibility.Validate(...) 做 shape/ownership/metadata一致性验证。

    所以历史补充里“必须先设计 pre-instantiation metadata”已经完成,不是 Phase 17 待办。

    当前还已经有:

    • SharpLinkGeneratedManifestCompatibility central validator;
    • fixed API 3 binary fixture;
    • generated-ABI mixing fixtures/scripts;
    • Protocol v2 cross-version verification;
    • generated ABI baseline/evidence 文档与 runner。

    这些应作为 Phase 17 的已有 oracle,不要删除后重新造一套。


    1. 第一性原理:先判断 #84 是否改变 Generated ABI

    Phase 17 开始时对 #84 合并前后做 ABI inventory diff,至少检查:

    SharpLink.Abstractions:
      IRpcGeneratedServerBridge
      IRpcStub
      IRpcChannel
      generated manifest attribute/descriptor types
      codec factory/provider contracts used by generated code
    
    Generator emitted signatures:
      proxy constructor/calls
      stub constructor/invoke signatures
      streaming bridge calls
      manifest locator + manifest descriptor shape
    

    情况 A:#84 只 internalize RpcSession / IStreamManager / engine extensions

    如果 generated code继续只通过当前 IRpcGeneratedServerBridge / IRpcStub 等 API 4 surface,Generated ABI 没变。

    此时:

    • 不 bump 4 -> 5;
    • Phase 17 重点变成确认 API 4 已是最终 post-decoupling ABI;
    • 完成所有 startup/registration fail-fast matrix;
    • 删除任何仍残留的 old adapter/legacy branch;
    • 文档明确 API 4 是当前 major Generated ABI。

    情况 B:#84 实际修改 generated-visible interface/signature/descriptor

    只有这种情况才 bump:

    Api = 5   // 示例,以实际 current+1 为准
    Protocol = 2
    

    并同步 locator metadata、Generator emitted literal、Runtime validator、fixtures、AOT artifacts。

    “public Runtime engine API breaking”不自动等于“Generated ABI breaking”。 两个边界必须按真实依赖判断。


    2. Protocol version 必须继续独立

    无论是否 bump Generated ABI:

    Generated ABI -> CLR/source-generated artifact contract
    Protocol 2    -> on-wire frame compatibility
    

    禁止:

    • 因 Generated ABI bump 改 Protocol;
    • 把 Generated ABI version写入 wire handshake;
    • 用 Protocol version决定 stub/manifest CLR shape。

    Protocol v2 golden/cross-version fixture必须继续通过。


    3. 复用现有 self-describing locator,不再设计第二个版本系统

    如果需要 API 5:扩展现有 self-describing locator/validator语义即可。

    不要新建另一套:

    GeneratedAbiVersionAttribute
    ManifestVersionAttribute
    RuntimeAbiAttribute
    

    除非现有 locator无法表达真实新需求。

    目标保持:loader在依赖 current manifest interface实例化之前,能从 assembly metadata判定 major compatibility。


    4. legacy API 3 路径已存在;下一版要继续支持“识别并拒绝”,不是适配

    当前 loader明确把单参数 locator识别为 API 3 legacy,并在 Activator前返回 incompatibility。

    如果 bump API 5:

    • API 3 legacy仍应早拒绝;
    • API 4 self-describing locator应能读出 actual=4,并早拒绝;
    • API 5 current成功;
    • future API 6同样 expected/actual早拒绝。

    禁止 runtime translation:

    if api == 4 -> adapter to 5
    

    迁移路径仍是 regenerate/rebuild。


    5. central validator 当前已经存在,Phase 17 做入口覆盖审计

    不要再新造 GeneratedArtifactCompatibility 平行实现。

    审计所有入口是否最终经过当前 compatibility逻辑:

    • ordinary RuntimeContext/Client build;
    • Server build;
    • dynamic assembly loader;
    • Client/Server runtime dynamic registration;
    • multi-cluster child/static manifest snapshot;
    • package/AOT startup。

    版本不兼容必须先于 semantic manifest conflict 报错。

    如果某入口自己复制 if ApiVersion != ...,优先收敛到 central validator/metadata reader。


    6. module initializer/global catalog:区分“加载时二进制限制”和“Runtime接纳边界”

    当前 generated module initializer仍调用 catalog Register(Instance)。

    要验证:

    • current artifact module load安全;
    • API 3/API 4 legacy binary在测试支持的加载方式下,能够在 Runtime接纳/registration/startup边界给 actionable incompatibility;
    • 不为旧 artifact保留 deleted Runtime engine API。

    如果 CLR 在 assembly module initializer阶段因为真正缺失的 binary member已经无法加载,必须诚实记录这是 pre-runtime binary load limitation;不要声称 Runtime能捕获所有任意旧 DLL。

    Phase 17 的可保证范围应写清:对能够被 CLR加载并进入 SharpLink registration/startup边界的旧 generated artifact,必须 early fail fast。


    7. 现有 fixtures 是 oracle,按真实 delta扩展

    当前 dev 已有:

    • test/fixtures/generated-api3 固定旧 binary;
    • test/fixtures/generated-abi-mixing/old-generator-new-abstractions;
    • .../new-generator-old-abstractions;
    • Api3BinaryFixtureIntegrationTests;
    • eng/verify-generated-abi-mixing.sh;
    • eng/verify-protocol-v2-cross-version.sh。

    不要用“current interface返回旧数字”的 fake 取代这些真实 fixture。

    若 bump到 API 5,应保留 API 3 fixture并新增一个冻结的 API 4 fixture,形成:

    API3 old binary -> reject
    API4 previous binary -> reject
    API5 current -> success
    

    如果不 bump,API 4 current + API 3 old matrix已经是主要 oracle。


    8. version matrix

    无论是否 bump,最终至少覆盖:

    current ABI + protocol2                     success
    API3 legacy locator                         early incompatible
    previous self-describing ABI(若存在)      early incompatible
    future ABI metadata                         early incompatible
    current ABI + wrong protocol                incompatible
    locator metadata != materialized manifest   InvalidManifest
    missing locator                              MissingManifest
    malformed locator                            InvalidManifest
    current metadata + malformed manifest        semantic error
    

    入口覆盖 ordinary build、dynamic registration、multi-cluster、collectible ALC、package startup、NativeAOT(适用项)。


    9. 如果需要 bump,Generator + Abstractions + Runtime 必须原子升级

    推荐:

    1. test: freeze current API4 fixture/baseline
    2. breaking: update Abstractions version constant + locator contract(仅若需要)
    3. generator: emit new version/signatures
    4. runtime: accept only new ABI and report old actual version
    5. test: update version matrix / regenerated artifacts / AOT
    6. cleanup: remove obsolete current-major legacy branches
    7. docs: regeneration/migration guidance
    

    不要出现“Runtime先只接受5,但repo generator还生成4”的中间合并状态。


    10. error contract

    继续要求稳定字段:

    Assembly identity
    LoadContext identity(dynamic)
    Expected Generated ABI
    Actual Generated ABI / legacy marker
    Expected Protocol
    Actual Protocol
    GeneratorVersion(metadata可读时)
    Action: regenerate/rebuild with current SharpLink SDK
    

    当前错误已经有 expected/actual/generator基础;Phase 17 需要补的是跨入口一致性与行动说明,不是换错误体系。


    11. NativeAOT

    当前 self-describing metadata路径已存在。若改 ABI:

    • 不引入动态代码 requirement;
    • current manifest仍被正确root;
    • legacy detection不要求实例化旧 type;
    • AOT smoke必须由 current generator重新生成;
    • package AOT consumer也要验证,不只solution project。

    12. 性能边界

    通常无需 BenchmarkDotNet。结构门禁:

    • version/manifest compatibility只在 load/registration/build/startup;
    • runtime保存validated snapshot/registration;
    • call/proxy/stream item热路径不比较 ApiVersion;
    • 不为兼容保留 per-call adapter。

    发现 hot path version branch即设计错误。


    13. Phase 17 完成条件

    如果 #84 没改变 Generated ABI,正确结果可以是:保留 API 4,不制造无意义 API 5,只完成 fail-fast/coverage/cleanup 审计。

  2. SunSi12138 commented on Aug 12, 2026

    @SunSi12138
    OwnerAuthor

    关联:#167 [codec][architecture] 将 RPC 序列化收归 Contract 并在编译期固定 Codec 图。

    #167 会影响 vNext Generated ABI/Manifest 的 Codec descriptor、Contract-scoped CodecSet、Proxy/Stub construction-time binding,以及 dynamic registration candidate 的 prepare/commit 形状。建议在本 Phase 17 最终冻结 Generated ABI 前先完成 #167 的核心架构决策,再由 #85 一次性 bump ABI 并删除旧路径,避免先固化当前 StubFactory / runtime codec resolution 形状后在 2.0 内再次 breaking。

    推荐顺序:#167 architecture decision -> #85 ABI vNext/fail-fast -> #86 final public API/package baseline。

  3. SunSi12138 commented on Aug 17, 2026

    @SunSi12138
    OwnerAuthor

    Phase 17 完成 — Generated ABI 冻结为 API 5,旧 artifact 启动期 fail fast(PR #227)

    判定与结果

    dev 上 Api=4 原子切分(b1aebf2)之后,#167 contract-owned codec 架构与 generated sized-codec 工作改变了 generated-visible 表面(生成 DTO Codec 新增 IRpcSizedCodec<T>、RpcGeneratedCodecSizing、RpcGeneratedCodecWire.MaximumStringPayloadBytes,Codec factory adapter-free + schema identity)。Api=4 已无法区分切分前后两种能力面,按执行手册"情况 B"将 post-#167 表面冻结为 API 5,Protocol 保持 2,两轴独立。

    交付

    • SharpLinkGeneratedManifestVersions.Api = 5;Generator 发射 apiVersion 5 的 locator/manifest,referenced-manifest bootstrap 扫描同步。
    • Runtime 门禁全部经常量流转:API 3 legacy 与 API 4 self-describing artifact 在 manifest materialize 之前被拒绝,错误稳定包含 expected/actual、GeneratorVersion 与"删除旧生成输出、用当前 SDK 重新生成/重新编译"行动说明;无 old ABI reflection/forwarding adapter、无 legacy branch、无兼容开关。
    • 冻结真实 API 4 binary fixture(bump 前 dev 树构建,SHA-256 见 test/fixtures/generated-api4/PROVENANCE.md),新增 Api4BinaryFixtureIntegrationTests:direct loader、Client/Server/multi-cluster 的 registration+replacement 共 7 个入口全部 early reject、无 snapshot 发布、collectible ALC 释放;保留既有 API 3 fixture。
    • Version matrix:current(5) 成功 / API3 早拒 / API4 早拒 / future(6) 早拒 / wrong Protocol 早拒 / locator-metadata 不一致 / missing / malformed。
    • 版本校验只在 load/registration/startup 边界,不进入调用热路径,无 per-call adapter(执行手册 §12,未做 BenchmarkDotNet)。

    验证证据(PR #227 描述中记录完整命令)

    迁移

    doc/migration.md 已更新:Generated ABI(API 5)≠ Protocol v2,升级后删除旧 bin/obj 并用当前 2.0 SDK 重新生成;含错误示例与重新生成步骤。详见新增 doc/runtime-phase-17-generated-abi-vnext.md。

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