計:按生成器劃分還是按運行時實現(xiàn)劃分?)
Protobuf Editions 中的 Feature 擴展布局設(shè)計按生成器劃分還是按運行時實現(xiàn)劃分【免費下載鏈接】protobufProtocol Buffers - Googles data interchange format項目地址: https://gitcode.com/GitHub_Trending/pr/protobuf本文以 docs/design/editions/editions-feature-extension-layout.md 設(shè)計文檔作者 mkruskal-google、zhangskz2023-08-23 批準為主體講清 Protobuf Editions 項目中的一個關(guān)鍵設(shè)計決策全局featuresoption 的語言級擴展feature extensions到底應(yīng)該歸誰擁有——是歸運行時實現(xiàn)C、upb、Java……還是歸代碼生成器protoc 插件。讀完后你將理解四個備選方案各自的利弊、最終取舍背后的權(quán)衡邏輯并能在倉庫源碼descriptor.proto 的FeatureSet定義中看到這個決策的實際落地形態(tài)。背景誰該擁有這些 Feature 擴展What are Protobuf Editions 提出了用edition ...取代syntax ...、用可繼承的features控制代碼生成與運行時行為的總體計劃并計劃用全局 features proto 的擴展來讓 protobuf 團隊之外的各方定義自己關(guān)心的特性。但當(dāng)時遺留了一個模糊點這些擴展的歸屬邊界——語言language、代碼生成器code generator、運行時實現(xiàn)runtime implementation三者相似卻不等同一種語言如 Python可能有多種運行時實現(xiàn)純 Python、Python/C、Python/upb一種運行時實現(xiàn)如 upb、C可能被多種語言共享Python、Rust、Ruby、PHP 都以它們?yōu)楹蠖?。觸發(fā)這次討論的具體案例是 Editions Zero Feature: utf8_validation該文檔未對外發(fā)布但后來去掉爭議選項的版本即倉庫中的 edition-zero-features.md 所討論的 UTF-8 校驗特性。原本唯一的擴展特性legacy_closed_enumJava/C沒有歸屬歧義而 utf8_validation 有在 Python 中proto2/proto3 下的當(dāng)前行為對三種實現(xiàn)pure Python、Python/C、Python/upb各自不同。這正是擴展按誰劃分問題的典型壓力測試。兩個讓按運行時實現(xiàn)劃分復(fù)雜化的現(xiàn)實原文檔在 Overview 中列出會議里討論出的兩大復(fù)雜性Polyglot多語言混用upb 或 C 運行時在多語言場景下應(yīng)遵循哪套 features這一歧義其實今天就已存在——所有 proto2 字符串以及大量 proto3 字符串在跨語言傳輸時本身就是不安全的。Shared Implementations共享實現(xiàn)upb 和 C 被用作多種語言的后端。如果只有一套upb或cpp級別的 features那么語言切換到這些共享實現(xiàn)時將更難遷移因為沒有按語言獨立的開關(guān)。同樣這也是現(xiàn)狀——今天切換運行時實現(xiàn)就可能帶來微妙且危險的行為變化。文檔給出的結(jié)論是既然當(dāng)前只有兩種行為且其中一種無歧義不如在 edition zero 推廣過程中先擱置這個決定等收集到更多邊界案例再說同時后續(xù) edition 對 features 的重新建模自由度很大所以初始實現(xiàn)應(yīng)保持最簡單即下文備選方案 2。四種備選方案逐一對比方案一按運行時實現(xiàn)劃分Runtime Implementation Features這是 Editions Zero Feature: utf8_validation 中的原始設(shè)想features 按運行時實現(xiàn)組織。例如 Protobuf Python 用戶需要根據(jù)底層實現(xiàn)設(shè)置不同擴展features.(pb.cpp).feature或features.(pb.upb).feature。優(yōu)點與 Editions 之前可表達的行為范圍最一致。缺點底層實現(xiàn)往往對用戶不透明用戶甚至可能不知道自己在用哪個后端而且缺乏針對語言/實現(xiàn)組合的獨立開關(guān)——例如無法獨立設(shè)置Python-on-C的行為而不動 C 本身這會加大從其他 Python 實現(xiàn)遷移的難度。方案二按代碼生成器劃分Generator Features最終采納方向features 只按生成器劃分——每個 protoc 插件擁有一套自己的 features。這是團隊在后續(xù)討論中做出的第二個決定。它與方案一非常相似但更貼合features 主要服務(wù)于 codegen的目標。例如所有 Python 實現(xiàn)共享同一套 featuresfeatures.(pb.python).feature若某特性確實需要針對特定實現(xiàn)可以把特性名本身定向到該實現(xiàn)如features.(pb.python).upb_utf8_validation只會被 Python/upb 使用。優(yōu)點允許對不同目標語言共享同一實現(xiàn)的情況做獨立控制例如 Python 的 upb 特性不會影響 PHP。缺點upb 需要理解自己應(yīng)該遵循哪門語言的特征而 upb 目前并不知道自己是給哪門語言服務(wù)的在行為沖突時共享實現(xiàn)的跨語言進程內(nèi)共享如 Python-upb 與 PHP-upb 同進程會受到限制可能還需要額外的檢查。方案三遷移到 bytesMigrate to bytes既然爭議圍繞 utf8 校驗干脆不在 edition zero 引入這個開關(guān)把目前不強制 UTF-8 校驗的字段全部遷移為bytes。這大概率需要一個新的代碼生成特性來把 bytes 的 getter/setter 生成為字符串 API但不存在當(dāng)前看到的歸屬歧義。文檔認為此路不可行utf8 校驗并不是簡單的開/關(guān)二值決策它在語言之間差異很大——很多情況下 UTF-8 在部分語言中校驗、在另一些語言中不校驗還有 C 那種只記日志但放行非法 UTF-8的 hint 行為。文檔同時留了個口子可以在后續(xù) LSClarge-scale change中通過定向禁用所有相關(guān)語言校驗的特性組合部分實現(xiàn)該思路。優(yōu)點回避問題不需要任何 upb 特性C 特性全部退化為純代碼生成特性避免在 edition zero 引入一個非常復(fù)雜的特性。缺點以當(dāng)前復(fù)雜度看基本做不到會有O(10M) 量級的 proto2 string 字段被盲目改為 bytes。方案四嵌套特性Nested Features允許共享的 feature set 消息upb 定義自己的 feature 消息但不把它作為全局FeatureSet的擴展使用 upb 實現(xiàn)的語言在自己的 feature 里內(nèi)嵌一個該類型的字段實現(xiàn)更細粒度的控制。C 則既擴展全局FeatureSet也允許作為其他語言的字段出現(xiàn)。此外可在特性校驗階段加入檢查強制不可能的組合不被指定——例如在當(dāng)前實現(xiàn)下features.(pb.python).cpp必須與features.(pb.cpp)恒等因為沒有機制區(qū)分二者。優(yōu)點比方案一、二更顯式。缺點可能過度顯式——proto 屬主被迫大量復(fù)制duplicate特性聲明。決策邏輯信息不足時選最簡單的模型文檔 Overview 的結(jié)論值得單獨強調(diào)只有兩種行為、且其中一種無歧義此時強行選定按實現(xiàn)劃分還是按生成器劃分的歸屬模型風(fēng)險大于收益。與其在 edition zero 就定死復(fù)雜的所有權(quán)結(jié)構(gòu)不如讓初始實現(xiàn)保持簡單備選方案 2按生成器劃分把按實現(xiàn)定向留作特性命名層面如upb_utf8_validation這樣的特征名的柔性能力待推廣期積累更多邊界案例后再決定是否升級建模方式。這一決策也呼應(yīng)了 what-are-protobuf-editions.md 中codegen backends own the definitions of their features的總體原則——特性定義權(quán)歸各語言后端而歸屬單位是生成器。倉庫源碼印證FeatureSet擴展聲明就是按生成器劃分設(shè)計文檔討論的抽象問題在當(dāng)前倉庫的 src/google/protobuf/descriptor.proto 中有明確的落地形態(tài)可以作為事實核對。1. 每個生成器獨占一個FeatureSet擴展號FeatureSet消息descriptor.proto 處定義末尾用extension_range顯式登記了已分配的語言級擴展其編號即按生成器劃分的直接證據(jù)extensions 1000 to 9994 [ declaration { number: 1000, full_name: .pb.cpp, type: .pb.CppFeatures }, declaration { number: 1001, full_name: .pb.java, type: .pb.JavaFeatures }, declaration { number: 1002, full_name: .pb.go, type: .pb.GoFeatures }, declaration { number: 1003, full_name: .pb.python, type: .pb.PythonFeatures }, declaration { number: 1004, full_name: .pb.csharp, type: .pb.CSharpFeatures }, declaration { number: 1100, full_name: .imp.impress_feature_set, type: .imp.ImpressFeatureSet }, declaration { number: 9989, full_name: .pb.java_mutable, type: .pb.JavaMutableFeatures }, declaration { number: 9990, full_name: .pb.proto1, type: .pb.Proto1Features } ]; extensions 9995 to 9999; // For internal testing extensions 10000; // for https://github.com/bufbuild/protobuf-es這段聲明印證了文檔結(jié)論的幾個要點沒有pb.upb、pb.cpp_impl這類按運行時實現(xiàn)的頂層擴展只有按語言/生成器命名的.pb.cpp、.pb.java、.pb.python……——這正是備選方案 2 的形態(tài).pb.java_mutableJavaMutableFeatures擴展號 9989作為獨立的生成器擴展存在而非嵌套在JavaFeatures里的字段——從源碼結(jié)構(gòu)看面向特定實現(xiàn)的變體被建模為另一個生成器級別的擴展而不是方案 4 的嵌套特性消息保留 9995–9999 給內(nèi)部測試、10000 給第三方生成器bufbuild/protobuf-es說明這套編號空間是留給各 codegen 自持特性的開放注冊表。各生成器的特性消息定義在獨立文件中例如 src/google/protobuf/cpp_features.proto配套生成的cpp_features.pb.h/cc同目錄與extensions聲明中.pb.CppFeatures一一對應(yīng)。2. 觸發(fā)討論的utf8_validation特性長什么樣FeatureSet中的utf8_validation字段緊隨repeated_field_encoding之后定義展示了該特性在去掉爭議選項后的最終形態(tài)enum Utf8Validation { UTF8_VALIDATION_UNKNOWN 0; VERIFY 2; NONE 3; reserved 1; } optional Utf8Validation utf8_validation 4 [ retention RETENTION_RUNTIME, targets TARGET_TYPE_FIELD, targets TARGET_TYPE_FILE, feature_support { edition_introduced: EDITION_2023, }, edition_defaults { edition: EDITION_LEGACY, value: NONE }, edition_defaults { edition: EDITION_PROTO3, value: VERIFY } ];幾點與設(shè)計文檔的呼應(yīng)retention RETENTION_RUNTIME特性會被序列化進 descriptor 供運行時使用即它確實橫跨 codegen 與 runtime這正是歸屬需要慎重設(shè)計的原因edition_defaults顯式區(qū)分了EDITION_LEGACYproto2 時代默認NONE與EDITION_PROTO3默認VERIFY把現(xiàn)狀不一致固化成按 edition 的默認值而不是在擴展布局上分叉reserved 1說明早期曾存在第三個枚舉值后被移除——與文檔提及的問題選項problematic options修訂過程一致。3. 特性解析在哪里發(fā)生從源碼結(jié)構(gòu)看倉庫中src/google/protobuf/下存在feature_resolver.h/.cc及對應(yīng)測試feature_resolver_test.cc承擔(dān)把 edition 默認值與顯式聲明合并、完成特性繼承解析的工作FeatureSetDefaultsdescriptor.proto 處定義則攜帶每個 edition 的默認特性集合讓解析退化為找最近的匹配 edition 再做 proto 合并。特性如何被各生成器/運行時消費可繼續(xù)參見 docs/design/editions/editions-life-of-a-featureset.md 的完整流程描述。對實踐者的啟示寫 editions proto 時擴展命名空間按你使用哪個生成器選擇用 C codegen 就寫features.(pb.cpp).x用 Python codegen 就寫features.(pb.python).x無論底層是不是 upb——這與文檔所有 Python 實現(xiàn)共享同一套 features的設(shè)計一致。實現(xiàn)級差異用特性名表達而不是新擴展需要區(qū)分后端時參照features.(pb.python).upb_utf8_validation的命名思路在自己擁有的生成器擴展內(nèi)加定向特性。理解共享實現(xiàn)的邊界以 upb/C 為后端的多種語言之間行為可能受同一實現(xiàn)、不同語言 features的沖突約束文檔方案 2 的 Cons同進程多語言混用時值得額外驗證序列化/校驗行為。遷移語義依賴 edition 默認值而非擴展布局utf8_validation等特性的 edition 默認值proto2→NONE、proto3→VERIFY保證了 proto2/proto3 向 editions 的機械遷移在語義上是 no-op這也是整個 feature 布局設(shè)計要服務(wù)的最終目標。延伸閱讀均在當(dāng)前倉庫內(nèi)docs/design/editions/what-are-protobuf-editions.mdEditions 總綱features/editions 的基本概念與生命周期。docs/design/editions/edition-zero-features.mdedition zero 首批特性的定義其中string_field_validationMANDATORY/HINT/NONE正是本文主題討論的前身后被utf8_validation取代。docs/design/editions/editions-life-of-a-featureset.md一個 FeatureSet 從文件到生成代碼的完整旅程。docs/design/editions/README.mdeditions 設(shè)計文檔索引。src/google/protobuf/descriptor.protoFeatureSet、FeatureSetDefaults的權(quán)威定義。【免費下載鏈接】protobufProtocol Buffers - Googles data interchange format項目地址: https://gitcode.com/GitHub_Trending/pr/protobuf創(chuàng)作聲明:本文部分內(nèi)容由AI輔助生成(AIGC),僅供參考