Repository navigation
[Runtime Architecture][Phase 17] 提升 Generated ABI/Manifest 版本并对旧 artifact 启动期 fail fast #85
Description
总跟踪:#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
本阶段按下述工程原则和任务清单实施。目标不是增加抽象数量,而是减少非法状态、所有权分支、并发状态组合与测试不确定性。
通用工程原则(本阶段必须遵守)
- 让非法状态不可表示。 构造完成的对象必须立即合法;握手结果、BuildPlan、拓扑和其他稳定配置使用 immutable snapshot 一次发布,不依赖调用顺序完成二次绑定。
- 单一所有权、单一终态。 每个 transport、counter、lease、timer、后台任务和构建期资源都必须写明谁创建、谁拥有、哪个终态释放、停止或失败时由谁等待/观察;竞争终态必须由同一个 winner 收敛,禁止重复释放和 underflow。
- 分离状态域。 Session 的 transport/protocol 状态、ClientConnection 的新调用资格、ServerConnectionState 的认证/调用排空状态分别建模,不用一个布尔值或一个巨型状态机互相冒充。
- 并发必须有界且可监督。 不引入无界队列、无限重试或无人等待的 fire-and-forget;framework-owned detached task 必须登记、观察异常,并在 owner 停止时可 drain。批量 drain 前先关闭 owner-level 写入门槛。
- 统一时间语义。 UTC 只服务 wire deadline、日志、Activity 和公开诊断;本地 deadline、elapsed、heartbeat、retry、breaker、drain 和 shutdown budget 使用同一
TimeProvider的 monotonic timestamp,并能用 fake time 确定测试。 - 资源构建是事务。 Builder 先编译完全验证且不可变的 plan,再 materialize resource,最后 commit 所有权;失败时保留 primary exception,并按登记逆序恰好清理一次。Caller-owned 资源不得被框架擅自释放。
- 保护热路径。 继续读取 immutable snapshot,不增加全局锁;supervisor、BuildPlan、ManifestSource 查询不得进入每次 RPC/每帧/每 item 路径;不得引入无依据的每调用 allocation 或新的锁竞争。
- 明确兼容性边界。 默认保持 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 转接层。
- 增量实施、独立验证和回滚。 每个阶段对应一个聚焦 PR,可单独测试、基准和回滚;不一次性重写 Runtime,也不为了目录整齐或抽象形式扩大范围。
- 保留已经正确的并发模式。 例如
StreamManager.CompleteAll先 CAS 发布终态再逐 stripe drain、PendingCall单槽位终态竞争、dispatcher acquire/close/detach lease;不得以“统一”为名改成全局锁或ConcurrentDictionary全面替换。 - 测试与诊断先行。 生命周期变更必须有竞态、fault-injection、fake-time、stress/chaos、ALC/NativeAOT(按影响范围)测试;触及热路径时必须提交 BenchmarkDotNet 前后数据。
- 每个新增可变状态都回答四个问题。 谁创建?谁拥有?哪个终态释放?停止/失败时如何被等待和观察?无法用一两句话回答时不得合并。
本阶段任务
- 定义并提升
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
SunSi12138 commentedon Aug 10, 2026
Implementation Supplement — Phase 17 执行手册(基于
dev,保留原正文)0.
dev当前已经具备的机制当前:
Generator 已生成 self-describing locator:
并且 manifest 自身也返回 literal
ApiVersion = 4 / ProtocolVersion = 2。SharpLinkAssemblyManifestLoader.TryLoad已经在实例化 manifest 之前读取CustomAttributeData:IncompatibleManifest;Activator.CreateInstance;SharpLinkGeneratedManifestCompatibility.Validate(...)做 shape/ownership/metadata一致性验证。所以历史补充里“必须先设计 pre-instantiation metadata”已经完成,不是 Phase 17 待办。
当前还已经有:
SharpLinkGeneratedManifestCompatibilitycentral validator;这些应作为 Phase 17 的已有 oracle,不要删除后重新造一套。
1. 第一性原理:先判断 #84 是否改变 Generated ABI
Phase 17 开始时对 #84 合并前后做 ABI inventory diff,至少检查:
情况 A:#84 只 internalize
RpcSession/IStreamManager/ engine extensions如果 generated code继续只通过当前
IRpcGeneratedServerBridge/IRpcStub等 API 4 surface,Generated ABI 没变。此时:
4 -> 5;情况 B:#84 实际修改 generated-visible interface/signature/descriptor
只有这种情况才 bump:
并同步 locator metadata、Generator emitted literal、Runtime validator、fixtures、AOT artifacts。
“public Runtime engine API breaking”不自动等于“Generated ABI breaking”。 两个边界必须按真实依赖判断。
2. Protocol version 必须继续独立
无论是否 bump Generated ABI:
禁止:
Protocol v2 golden/cross-version fixture必须继续通过。
3. 复用现有 self-describing locator,不再设计第二个版本系统
如果需要 API 5:扩展现有 self-describing locator/validator语义即可。
不要新建另一套:
除非现有 locator无法表达真实新需求。
目标保持:loader在依赖 current manifest interface实例化之前,能从 assembly metadata判定 major compatibility。
4. legacy API 3 路径已存在;下一版要继续支持“识别并拒绝”,不是适配
当前 loader明确把单参数 locator识别为 API 3 legacy,并在 Activator前返回 incompatibility。
如果 bump API 5:
禁止 runtime translation:
迁移路径仍是 regenerate/rebuild。
5. central validator 当前已经存在,Phase 17 做入口覆盖审计
不要再新造
GeneratedArtifactCompatibility平行实现。审计所有入口是否最终经过当前 compatibility逻辑:
版本不兼容必须先于 semantic manifest conflict 报错。
如果某入口自己复制
if ApiVersion != ...,优先收敛到 central validator/metadata reader。6. module initializer/global catalog:区分“加载时二进制限制”和“Runtime接纳边界”
当前 generated module initializer仍调用 catalog
Register(Instance)。要验证:
如果 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,形成:
如果不 bump,API 4 current + API 3 old matrix已经是主要 oracle。
8. version matrix
无论是否 bump,最终至少覆盖:
入口覆盖 ordinary build、dynamic registration、multi-cluster、collectible ALC、package startup、NativeAOT(适用项)。
9. 如果需要 bump,Generator + Abstractions + Runtime 必须原子升级
推荐:
不要出现“Runtime先只接受5,但repo generator还生成4”的中间合并状态。
10. error contract
继续要求稳定字段:
当前错误已经有 expected/actual/generator基础;Phase 17 需要补的是跨入口一致性与行动说明,不是换错误体系。
11. NativeAOT
当前 self-describing metadata路径已存在。若改 ABI:
12. 性能边界
通常无需 BenchmarkDotNet。结构门禁:
发现 hot path version branch即设计错误。
13. Phase 17 完成条件
-真实旧 binary/locator early reject;
如果 #84 没改变 Generated ABI,正确结果可以是:保留 API 4,不制造无意义 API 5,只完成 fail-fast/coverage/cleanup 审计。
SunSi12138 commentedon Aug 12, 2026
关联:#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。SunSi12138 commentedon Aug 17, 2026
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 扫描同步。test/fixtures/generated-api4/PROVENANCE.md),新增Api4BinaryFixtureIntegrationTests:direct loader、Client/Server/multi-cluster 的 registration+replacement 共 7 个入口全部 early reject、无 snapshot 发布、collectible ALC 释放;保留既有 API 3 fixture。验证证据(PR #227 描述中记录完整命令)
quick首轮因既有竞态失败(已另提 test: isolate phase15 global manifest poison from parallel unit tests #228/test: remove TLS handshake-limit counter race from ConnectionAdmissionTests #229),重跑后通过。迁移
doc/migration.md已更新:Generated ABI(API 5)≠ Protocol v2,升级后删除旧bin/obj并用当前 2.0 SDK 重新生成;含错误示例与重新生成步骤。详见新增doc/runtime-phase-17-generated-abi-vnext.md。