diff --git a/PLAN.md b/PLAN.md index 59090b24e2564c73554230e8b45a34f556b14401..649a077930bb5873d3835dedc58e58d7318c422e 100644 --- a/PLAN.md +++ b/PLAN.md @@ -447,7 +447,71 @@ Both codegen modes implement it: runtime mode in `pb.codec`, full (inline) mode emits per-message capture/re-emit blocks. Tests live in `test/unknown_test.lua` and run against both modes. -### 4.6 gRPC service descriptors +### 4.6 Protobuf editions *(not implemented; tracking)* + +We advertise `CodeGeneratorResponse_FEATURE_PROTO3_OPTIONAL` only, not +`FEATURE_SUPPORTS_EDITIONS`. If protoc is invoked against an +`edition = "2023";` file with our plugin, it errors out — mainline +refuses to call an editions-unaware plugin. + +**What editions changes.** Editions replaces the proto2-vs-proto3 split +with one language whose behavior is controlled by per-file / per-field +`FeatureSet` annotations. The knobs that matter for us: + +- `field_presence = EXPLICIT | IMPLICIT | LEGACY_REQUIRED` — presence + becomes an opt-in/opt-out per field instead of a syntax-wide default. +- `repeated_field_encoding = PACKED | EXPANDED` — per-field opt-out + from packed encoding for scalar `repeated` fields. +- `enum_type = OPEN | CLOSED` — closed enums route unknown values to + unknown fields (proto2 semantics) instead of round-tripping the int. +- `utf8_validation = VERIFY | NONE` — per-field strictness on `string`. +- `json_format = ALLOW | LEGACY_BEST_EFFORT` — JSON behavior overrides. + +**What we'd actually gain.** Honestly modest, ranked by user-visible +value: + +1. **Per-field packed opt-out** — the only knob proto3 doesn't expose + today. Real interop scenarios (talking to legacy proto2 services, + certain gRPC gateways) sometimes need expanded encoding on a + specific field. Right now our users have no escape hatch. +2. **Migration path off proto3.** Proto3 syntax is being phased out in + favor of edition 2023. This is a forcing function, not a feature + win — at some point users will write `edition = "2023";` and we'll + need to read it. +3. **Explicit presence by default** without the `optional` keyword (and + the synthetic-oneof wart it creates today). +4. **Per-field UTF-8 toggle** for users carrying not-quite-UTF-8 bytes + in `string` fields for legacy compat. +5. **Closed enums** for users who want proto2-style unknown-value + routing. +6. **`LEGACY_REQUIRED` field presence** — niche; only way to express + "required" without writing proto2. + +**What it would cost.** Setting the bit is one line; the work is: + +- Plugin (`cmd/protoc-gen-tarantool/internal/gen`) needs to read each + field's resolved `FeatureSet` and drive codegen off it (packed, + presence, UTF-8 strictness, enum closedness) instead of the proto3 + defaults baked in. +- Runtime parser (`runtime/pb/parser.lua`) needs to accept + `edition = "2023";` and the `features = { ... }` option syntax. +- `runtime/pb/dynamic.lua` and `runtime/pb/fileset.lua` need to + propagate resolved features onto the descriptor. The descriptor + contract in `docs/codegen.md` would gain a `features` field per + message/field/enum. +- `CodeGeneratorResponse` needs `minimum_edition` / `maximum_edition` + set, else protoc rejects an editions-supporting plugin. +- The conformance suite has editions-specific buckets we'd start + encountering. + +**Current verdict.** Deferred. No user pull today, proto3 will keep +working for years, and the practical-win list above is mostly small +quality-of-life items rather than blocking gaps. Revisit when (a) +someone needs per-field packed control, (b) the conformance suite +starts gating on editions, or (c) protoc deprecates proto3 syntax +hard enough that real users hit the error. + +### 4.7 gRPC service descriptors ```lua M.Greeter_service = { @@ -570,7 +634,9 @@ ## 8. Non-goals (for now) - **proto2** syntax — spec is more complex (required, default values, groups), real demand is rare on Tarantool. -- **Protobuf editions** beyond what proto3 enables. +- **Protobuf editions** beyond what proto3 enables — see §4.6 for + the per-feature analysis (what we'd gain, what it would cost, the + conditions that would force a revisit). - **HTTP/2 termination** for gRPC. Push it to Envoy; see [`docs/specs/grpc_transports.md`](docs/specs/grpc_transports.md). Connect-JSON over HTTP/1.1 is the default external transport.