十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Protobuf Editions 命名设计解析:从自由字符串到 Edition 枚举的取舍

Protobuf Editions 命名设计解析:从自由字符串到 Edition 枚举的取舍 Protobuf Editions 命名设计解析从自由字符串到 Edition 枚举的取舍【免费下载链接】protobufProtocol Buffers - Googles data interchange format项目地址: https://gitcode.com/GitHub_Trending/pr/protobuf本文导读本篇基于 Protobuf 仓库中的设计文档 edition-naming.md2023-08-25 批准展开完整还原 Editions 命名方案的设计动机、五条原始设计意图、推荐方案Edition枚举与五个被否决的备选方案的权衡过程并结合当前仓库中descriptor.proto的实际枚举定义与protoc解析器的实现源码说明这一设计是如何落地演进的。读完后你将理解为什么edition 2023这个看似简单的字符串最终被约束成任意语言都能直接比较的离散枚举值。背景松散的命名约定与跨语言比较负担Life of an Edition 为 edition 命名给出了一套非常宽松的方案只定义了顺序规则和.分隔符除此之外对 edition 名称没有任何约束。社区随后约定俗成地采用年份 可选修订号的形式例如2023、2024.3。问题出在另一份设计文档 Editions: Life of a FeatureSet 中的决策上feature 解析feature resolution至少需要部分地在每一种支持的语言中重复实现。按照Life of a FeatureSet的界定需要重复的最小操作是两件事edition 比较edition comparisonproto 合并proto mergingedition 比较在当时并不算复杂但由于 edition 名称的约束过于松散比较逻辑里潜藏着大量可能遗漏的边界情况。设计者希望在任何语言里都能用简单的字典序字符串比较lexicographical string comparison完成 edition 比较从而把每个运行时里的重复实现降到最简。问题描述松散命名的三类风险边界情况与 Hyrums Law 风险允许无限多实践中永远不会出现的 edition 名称会带来真实的 [Hyrums Law] 风险。原文举了一个典型例子2023.a在当时是合法的 edition 名而它与2023.10的相对顺序并不直观——按字典序2023.10 2023.a因为1a按数值语义却应该是2023.a在前若 a 代表修订版。此外早期的 editions 测试中实际使用过一个叫very-cool的 edition 名这显然不是一个需要长期支持的形态。结论是edition 名应尽可能简单且约束必须可执行、有文档。Calver 视觉误导与patch edition混淆edition 看起来像 calver按日历年的版本号导致大家把修订版称为patch editions暗示它是对早期 edition 的 bug 修复——但这并非原始设计意图。文档中明确了五条原始意图值得逐条对照当前仓库的实现来理解Editions 严格按时间排序。修订号revision只是一年内可以发布多个 edition的机制但不能往更早的槽位里插入新 edition。新 edition 可以随时添加。只要排在既有 edition 之后就是非破坏性变更可以在 patch release 中完成。新 feature 可以随时添加无需变更 edition。它按定义是非破坏性的也可以放在 patch release。feature 只能在破坏性 release 中删除。editions 模型不支持删除 feature——那永远是 breaking change只会在 protobuf 的主版本号major version提升时发生。feature 默认值只能在新 edition 中变更。一旦某个 feature 选定了默认值只能靠发布带新默认值的新 edition 来改变这仍是非破坏性变更可走 patch release。注意第 2、3 条说明新增 edition / 新增 feature 都不破坏兼容而删除 feature / 改默认值分别被限制在 major release 和新 edition 里——这正是 edition 作为单向时间轴的语义基础也是后文枚举必须时间有序的根源。设计目标五条期望属性文档列出了对 edition 命名机制的期望允许的值是离散的、由 protobuf 团队控制的易于比较跨语言支持集合规模较小未来一个世纪内少于 100 个增长缓慢大约每年一次。这五条目标直接把解法指向了枚举值域由维护者独占、整数天然可比、任意语言都能处理、规模可控。推荐方案Edition 枚举方案草案最简单的做法是专门建一个Edition枚举来指定 edition。proto 文件里继续使用字符串但解析器会立刻把字符串转成枚举之后的所有代码都按枚举处理。这样就有了一个集中式的、跨语言共享的所有合法 edition 列表。文档当时的草案是enum Edition { EDITION_UNKNOWN 0; EDITION_2023 1; EDITION_2024 2; // ... }proto 文件中的写法与最初决策完全一致仍是字符串edition 2023;文档同时声明这些值意图上是可按数值比较的用于确定 edition 的时间顺序。与当前仓库实现的对照当前 descriptor.proto 中的Edition枚举已经落地并进一步演化比草案多了多个特殊占位值// The full set of known editions. enum Edition { // A placeholder for an unknown edition value. EDITION_UNKNOWN 0; // A placeholder edition for specifying default behaviors *before* a feature // was first introduced. This is effectively an infinite past. EDITION_LEGACY 900; // Legacy syntax editions. These pre-date editions, but behave much like // distinct editions. These cant be used to specify the edition of proto // files, but feature definitions must supply proto2/proto3 defaults for // backwards compatibility. EDITION_PROTO2 998; EDITION_PROTO3 999; // Editions that have been released. The specific values are arbitrary and // should not be depended on, but they will always be time-ordered for easy // comparison. EDITION_2023 1000; EDITION_2024 1001; EDITION_2026 1002; // A placeholder edition for developing and testing unscheduled features. EDITION_UNSTABLE 9999; // Placeholder editions for testing feature resolution. ... EDITION_1_TEST_ONLY 1; EDITION_2_TEST_ONLY 2; EDITION_99997_TEST_ONLY 99997; EDITION_99998_TEST_ONLY 99998; EDITION_99999_TEST_ONLY 99999; // Placeholder for specifying unbounded edition support. ... EDITION_MAX 0x7FFFFFFF; }对照草案可以确认三点演进事实均有源码注释为证正式 edition 的值不是草案里的1、2而是1000、1001、1002注释明确写着The specific values are arbitrary and should not be depended on, but they will always be time-ordered for easy comparison——恰好兑现了文档中数值可比较的承诺同时把具体数值声明为不可依赖。枚举中额外加入了EDITION_LEGACYfeature 引入之前的无限过去、EDITION_PROTO2/PROTO3用于兼容 feature 默认值定义但不能用于指定 proto 文件的 edition、EDITION_UNSTABLE开发/测试未排期 feature以及若干*_TEST_ONLY占位值——这些正是文档所警告的边界情况被枚举收编之后的形态。仓库内确实存在使用这些 edition 的真实 proto例如 editions/golden/test_messages_proto2_editions.proto 以edition 2023;开头而 editions/codegen_tests/ 目录下按edition2023_*、edition2024_*组织着各命名风格与语言特性的测试 proto。解析器如何把字符串变成枚举文档说parser will quickly convert them这条路径在当前 parser.cc 中可以完整看到。ParseSyntaxIdentifier处理文件首句if (has_edition) { if (!Edition_Parse(absl::StrCat(EDITION_, syntax), edition_) || edition_ Edition::EDITION_PROTO2 || edition_ Edition::EDITION_PROTO3 || edition_ Edition::EDITION_UNKNOWN) { RecordError(syntax_token.line, syntax_token.column, [] { return absl::StrCat(Unknown edition \, syntax, \.); }); return false; } syntax_identifier_ editions; return true; }这段代码与文档描述逐条对应字符串拼接成EDITION_名字后调用生成的Edition_Parse未知 edition无论未来还是已被移除的直接报Unknown edition ...错误——这正是推荐方案 Pros 里说的 Automatic rejection of unknown editions不需要任何自定义逻辑EDITION_PROTO2 / EDITION_PROTO3 / EDITION_UNKNOWN被显式排除与枚举注释cant be used to specify the edition of proto files一致解析成功即syntax_identifier_ editions——即文档中提到的syntaxgets set toeditionsby the parser when an edition is found这一事实文件必须以 edition 或 syntax 声明开头parser.cc 中require_syntax_identifier_ || LookingAt(syntax) || LookingAt(edition)分支缺失时回退 proto2 并告警。开放枚举的取舍与revision 暂缓文档还讨论了理想状态下应使用开放枚举open enum避免某个 edition 值落入 unknown field set。但该枚举必须存在于descriptor.proto因此在完成 edition zero 迁移之前无法改为开放枚举。过渡方案正是上面看到的解析器行为edition 出现时syntax被置为editions此时未设置 edition 应视为错误等迁移到开放枚举后可以再换成更简单的合法性检查。关于修订号revisions文档的决定是直接不做暂时不允许2023.1这类修订版如果我们真需要超过一个 revision说明犯了大错到时再讨论命名比如EDITION_2023_OOPS。规划上保持每年恰好一个 edition。推荐方案的利弊文档原文立场Prosedition 比较更简单了——就是整数比较从而每个语言里的 feature resolution 都变得平凡trivial自动拒绝未知 edition包括未来的、被移除的、未知修订号其他方案需要 protoc 里的自定义逻辑来强制不再长得像 calver避免上述命名混淆没有修订号简化了文档edition 更易理解和维护。Cons将来把descriptor.proto迁移到 editions 时可能有挑战解析器实现上可能有点棘手但文档指出 Prototiller 已经处理得很好——事实上当前 C 解析器也已如上实现。被否决的备选方案文档逐一评估了五个替代方案权衡过程对理解最终形态很有价值。1. Proto 文件内直接用枚举值不用字符串edition 直接写枚举例如enum Edition { E2023 1; E2023A 2; E2024 3; }Pros整数比较更简单一年内可有任意数量 revision闭枚举自动拒绝未知 edition不像 calver。Cons可读性变差edition E2023A不如edition 2023.1直观可能要求解析器变更才能把descriptor.proto迁上 editions是大改动需要更新文档与对外沟通插件作者无法预发布即将上线的 feature文档还自问我们是否真该允许这件事。Neutraledition 必须严格时间有序不能回头给旧 revision 加东西原方案本来也不允许edition 顺序与名字完全脱钩需要写反射测试强制值严格递增。2. 截断修订号Truncated Revisions最贴近原计划的方案限制每年至多 9 个中间 edition即 edition 要么只是年份2023要么带一位修订号2024.3修订号约束在(0,9]年份必须是2023的整数。这样 edition 排序就退化为简单的字典序字符串比较。Pros严格收紧命名、避免意外边界情况比较代码极易在各语言复制就是字符串比较消除 revision 0 的歧义2023.0不合法。Cons限制每年至多 10 个 edition看起来合理由于 protoc 负责强制日后还可重审只要保持字典序就可任意扩展。3. 固定长度 editionFixed Length总是从.0开起Edition Zero 就成了2023.0。利弊同上一方案额外地Pros修订版不再突兀——习惯2023.0的用户看到2023.1不会困惑。Cons对外发布的沟通与文档已经称首个 edition 为2023改名需要更新传播目前没有修订号的真实用例却给典型情况增加了复杂度。4. Edition 消息message用带结构的消息建模 editionmessage Edition { uint32 year 1; uint32 revision 2; }Prosschema 本身自动强制大部分约束允许任意数量 revision不可能误用字符串比较。Cons每种语言仍需要自定义比较代码代价略高于字符串比较要么在解析期把 edition 字符串转成该 message要么彻底改变 edition 的声明语法。5. 什么都不做Do NothingPros当下省事。Cons明天要把比较代码复制到每种语言时更难向 Hyrums Law 和意外滥用敞开大门。从设计到落地仓库中的验证点把文档结论映射回当前仓库可以形成一条完整证据链文档主张仓库证据proto 文件仍用字符串解析器立即转枚举edition 2023;test_messages_proto2_editions.protoEdition_Parse(EDITION_ syntax)parser.cc未知 edition 自动拒绝解析失败时报Unknown edition ...parser.ccedition 存在时syntax置为editionssyntax_identifier_ editions;parser.cc数值可比较、时间有序Edition枚举注释always be time-ordered for easy comparisondescriptor.proto值域集中、离散、跨语言共享枚举定义于 descriptor.proto供所有语言运行时引用同时What are Protobuf Editions 中protocspecifies which editions it understands, and will reject.protofiles (that use editions it does not understand)的表述与上述解析器行为相互印证拒绝未知 edition 的机制不是附加逻辑而是枚举本身带来的免费能力。小结Edition 命名设计的核心洞见是把edition 名从一个人类可读的自由字符串重新定义为一个机器可比较的离散枚举字符串只是 proto 文件里的声明语法糖。这一取舍用很小的解析器改动字符串 → 枚举名 → 整数换来了三个持久收益任意语言里 edition 比较都是整数比较、未知 edition 被自动拒绝、且彻底摆脱了 calver 外观造成的patch edition误解。而被否决的方案截断修订号、固定长度、edition message 等则留档说明了为何每年一个 edition、无修订号是当前最简且可扩展的约束。若后续需要追踪 feature 在各 edition 间的默认值流转可继续阅读 editions-life-of-a-featureset.md 与 life-of-an-edition.md。【免费下载链接】protobufProtocol Buffers - Googles data interchange format项目地址: https://gitcode.com/GitHub_Trending/pr/protobuf创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表