Skip to content

Commit 616dbfd

Browse files
committed
Pin protobuf field and enum value numbers in a sidecar artifact
Field and enum value numbers were assigned sequentially in definition order, so reordering or removing a field renumbered everything after it — breaking wire compatibility with previously serialized data. `schema_artifacts:dump` now reads and writes a `proto_field_numbers.yaml` sidecar artifact: - Existing numbers stay fixed even if field order changes; new fields get the next available numbers. - `schema.proto` keeps the public GraphQL field names while the sidecar stores private `name_in_index` overrides. - A field renamed with `field.renamed_from` reuses its existing number under the new public name. - Enum value numbers are pinned in an `enums` section; removed values keep their numbers reserved so they are never reused (`0` remains the generated `*_UNSPECIFIED` value). - Mappings are validated: collisions raise clear errors, and non-integer numbers are rejected instead of letting `Integer()` silently truncate. `configure_proto_field_number_mappings` allows tests and advanced callers to seed mappings without an artifact file.
1 parent 6f76222 commit 616dbfd

18 files changed

Lines changed: 1266 additions & 16 deletions

File tree

elasticgraph-proto_ingestion/README.md

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ ElasticGraph.define_schema do |schema|
7171
end
7272
```
7373

74-
After running `bundle exec rake schema_artifacts:dump`, ElasticGraph will generate `schema.proto`.
74+
After running `bundle exec rake schema_artifacts:dump`, ElasticGraph will generate:
75+
76+
- `schema.proto`
77+
- `proto_field_numbers.yaml`
7578

7679
## Schema Definition Options
7780

@@ -92,6 +95,45 @@ ElasticGraph.define_schema do |schema|
9295
end
9396
```
9497

98+
### Stable Field Numbers
99+
100+
`schema_artifacts:dump` automatically reads and writes `proto_field_numbers.yaml`
101+
in the schema artifacts directory. Existing numbers stay fixed even if field order
102+
changes, and new fields get the next available numbers. Field numbers follow protobuf's
103+
rules: they must be between 1 and 536,870,911, and the protobuf-reserved 19000-19999
104+
range is never allocated and is rejected in mappings.
105+
106+
`schema.proto` always uses the public GraphQL field names. When a field uses a
107+
different `name_in_index`, the sidecar YAML stores that override privately:
108+
109+
```yaml
110+
messages:
111+
Widget:
112+
fields:
113+
id: 1
114+
display_name:
115+
field_number: 2
116+
name_in_index: displayName
117+
```
118+
119+
If a field is renamed with `field.renamed_from`, `elasticgraph-proto_ingestion` reuses the
120+
existing field number under the new public field name.
121+
122+
### Stable Enum Value Numbers
123+
124+
Enum value numbers are pinned the same way, in an `enums` section of the sidecar. Existing
125+
values keep their numbers when other values are added or removed, new values get the next
126+
available numbers, and removed values keep their numbers reserved so they are never reused
127+
(number `0` is always the generated `*_UNSPECIFIED` value):
128+
129+
```yaml
130+
enums:
131+
WidgetColor:
132+
values:
133+
RED: 1
134+
BLUE: 2
135+
```
136+
95137
## Type Mappings
96138

97139
The generated `schema.proto` uses these built-in scalar mappings:

elasticgraph-proto_ingestion/lib/elastic_graph/proto_ingestion.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,8 @@ module ElasticGraph
1111
module ProtoIngestion
1212
# The name of the generated Protocol Buffers schema file.
1313
PROTO_SCHEMA_FILE = "schema.proto"
14+
15+
# The name of the generated proto field-number mapping file.
16+
PROTO_FIELD_NUMBERS_FILE = "proto_field_numbers.yaml"
1417
end
1518
end

elasticgraph-proto_ingestion/lib/elastic_graph/proto_ingestion/schema_definition/api_extension.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,18 @@ def proto_schema_artifacts(package_name: "elasticgraph")
7474
nil
7575
end
7676

77+
# Configures proto field-number mappings directly from a hash.
78+
# Useful for tests and advanced use cases where mappings are sourced outside artifacts.
79+
# When artifacts are dumped, mappings from the existing `proto_field_numbers.yaml` artifact
80+
# are loaded automatically; this method does not need to be called in that case.
81+
#
82+
# @param proto_field_number_mappings [Hash]
83+
# @return [void]
84+
def configure_proto_field_number_mappings(proto_field_number_mappings)
85+
proto_ingestion_state.field_number_mappings = proto_field_number_mappings
86+
nil
87+
end
88+
7789
private
7890

7991
# Returns this gem's state container. Centralizes the Steep cast that's needed because

elasticgraph-proto_ingestion/lib/elastic_graph/proto_ingestion/schema_definition/proto_ingestion_state.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ module SchemaDefinition
1414
# @private
1515
class ProtoIngestionState
1616
# @dynamic package_name, package_name=
17-
attr_accessor :package_name
17+
# @dynamic field_number_mappings, field_number_mappings=
18+
attr_accessor :package_name, :field_number_mappings
1819

1920
def initialize
2021
@package_name = "elasticgraph"
22+
@field_number_mappings = {}
2123
end
2224
end
2325
end

elasticgraph-proto_ingestion/lib/elastic_graph/proto_ingestion/schema_definition/results_extension.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,15 @@ def proto_schema
2020
@proto_schema ||= protobuf_schema_generator.to_proto
2121
end
2222

23+
# Returns proto field-number mappings suitable for artifact storage.
24+
#
25+
# @return [Hash]
26+
def proto_field_number_mappings
27+
# Ensure generation has occurred before reading mappings from the generator.
28+
proto_schema
29+
protobuf_schema_generator.field_number_mappings_for_artifact
30+
end
31+
2332
private
2433

2534
def protobuf_schema_generator
@@ -37,7 +46,8 @@ def protobuf_schema_generator
3746

3847
Schema.new(
3948
state: extension_state,
40-
package_name: ingestion_state.package_name
49+
package_name: ingestion_state.package_name,
50+
proto_field_number_mappings: ingestion_state.field_number_mappings
4151
)
4252
end
4353
end

0 commit comments

Comments
 (0)