Skip to content

Use Buf to guard protobuf schema compatibility - #1378

Draft
jwils wants to merge 1 commit into
mainfrom
joshuaw/buf-breaking-validation
Draft

jwils wants to merge 1 commit into
mainfrom
joshuaw/buf-breaking-validation

Conversation

@jwils

@jwils jwils commented Sep 11, 2026

Copy link
Copy Markdown
Collaborator

Why

Stable field numbers prevent protobuf data from being reinterpreted, but they do not catch incompatible field-type, syntax, package, or file-option changes. Maintaining those compatibility rules ourselves would duplicate Buf and leave room for subtle gaps.

What

  • Add proto_schema_version, defaulting to 1, and record it in the generated schema.proto.
  • Compile the existing and proposed schemas with Buf and apply its strict FILE breaking rules before dumping a changed artifact at the same version.
  • Reuse one logical proto path for both Buf images so file-level changes are compared accurately, while resolving external imports through the project Buf configuration and excluding imported schemas from the comparison.
  • Allow an explicit version bump to accept a breaking change, with a prototyping-only enforce_proto_schema_version false escape hatch.
  • Install Buf 1.72.0 for the CI job that validates README examples and document the workflow.

Risk

This adds a development-time dependency on the Buf CLI whenever an existing schema.proto changes without a version bump. First-time generation and explicitly versioned breaking changes do not invoke Buf. Projects can temporarily disable enforcement while prototyping.

Test plan

  • script/run_gem_specs elasticgraph-proto_ingestion: 101 examples, 100% line and branch coverage.
  • Real Buf 1.72.0 smoke checks: compatible additions pass; field-type and file-option changes fail; imported types compile successfully.
  • script/lint, script/type_check, script/spellcheck, documentation coverage, workflow/config validation, README snippet validation, and site:validate.
  • script/quick_build: 5,291 of 5,292 examples passed; one unrelated datastore acceptance example observed a record from another test environment and passed immediately when rerun in isolation.

References

@jwils
jwils added this pull request to stack #1381 September 11, 2026 20:58
@jwils jwils changed the title joshuaw/buf breaking validation Use Buf to guard protobuf schema compatibility Sep 11, 2026
@jwils
jwils removed this pull request from stack #1381 September 13, 2026 15:10
@jwils
jwils force-pushed the joshuaw/buf-breaking-validation branch from cd9bb00 to 4fd140d Compare September 13, 2026 15:24
jwils added a commit that referenced this pull request Sep 13, 2026
)

## Summary

Closes #1360.

`schema.proto` only had messages for the indexed types and the types
they reference, so a publisher could not send an event for a pure-source
type: one that feeds a `sourced_from` field but has no index of its own.
#1346 widened the JSON schema event envelope to accept these events, but
proto ingestion never got the same treatment. Nothing in an indexed
type's proto representation refers to a pure-source type, so the
traversal never reached it.

Both JSON and proto now use `Results#ingestible_types_by_name` to select
indexed document types and `sourced_from` source types, excluding
derived indexing types. `Results` resolves the completed schema once and
passes the shared map to both builders. Proto traverses referenced types
and emits abstract wrappers; JSON selects concrete event names.

The messages themselves needed no change, as #1360 predicted.
`proto_fields` builds from `indexing_fields_by_name_in_index`, which
does not depend on the type having an index:

```protobuf
message ComponentDesign {
  string id = 1;
  string component_id = 2;
  string designer_name = 3;
  // Next field number: 4
}
```

## Derived indexing types (a Buf-breaking change)

Derived indexing types are now excluded from the seed. ElasticGraph
builds their documents from the events of other types, so a publisher
never sends one, and the JSON schema artifact has never described them
either. Proto ingestion described them only because a derived type has
its own index, which put it in the old seed.

Removing a message is a breaking change under the Buf check that #1378
adds. No published schema depends on these messages yet, so this is the
moment to align the two formats.

## Abstract source types

A `sourced_from` source type can be an interface or a union. Such a type
keeps the `oneof` wrapper message it would get as an indexed abstract
type, and its subtypes get messages too.

Note the JSON event envelope does not accept an abstract type by name,
and it does not accept the concrete subtypes either, because they are
neither indexed nor source types. So an abstract source type is not
publishable over JSON today. That gap is tracked in #1386 and is not
addressed here.

## Notes

- No schema artifact churn. The root `Rakefile` does not register the
proto extension, and this repo commits no `schema.proto`.
- Proto schema generation now runs the `sourced_from` resolver, so it
can raise relationship and `sourced_from` validation errors that it
previously skipped. No existing proto spec defines a relationship, so no
fixture needed a repair.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant