Skip to content

Commit d3ff605

Browse files
committed
Make the indexing schema version optional and fix review defects
The pipeline now treats `schema_version` as optional, so an ingestion format with no versions (such as protobuf) can omit it. Each ingestion adapter decides what a missing version means. The JSON adapter uses the latest available JSON schema version, and still validates the event against that version, so a malformed event still fails. This removes the breaking changes the previous revision introduced: - `Converters.upsert_event_for` accepts `__schema_version`, the legacy `__json_schema_version`, or neither. An existing generated project keeps working with no edit to `shared_factories.rb`. - The JSON adapter claims and reads the legacy `json_schema_version` envelope key, so a direct caller of `Indexer#processor.process` needs no edit. - The latency log and the warehouse dump log emit `schema_version` and also the deprecated alias `json_schema_version`, so existing dashboards keep working. - The version selection log keeps its JSON-specific field names to match its JSON-specific message type `ElasticGraphMissingJSONSchemaVersion`. It also fixes four defects found in review: - `elasticgraph-indexer/README.md` and `indexing_event_decoder.rb` promised a default to the latest version that the code no longer had. Both texts are now format neutral, and the JSON gem documents its own behaviour. - A duplicate "Indexing Event Decoder" section in the JSON gem README named a class that does not exist. Removed. - `IndexerExtension#ingestion_adapters` memoized into the shared name `@ingestion_adapters` while calling `super`, which assigned the same name. - `select_schema_version` sorted the available versions on every event. The warehouse dumper uses the fixed S3 key segment `unversioned` in place of `v<version>` for a version-less format, so the segment count stays the same.
1 parent f90f1aa commit d3ff605

19 files changed

Lines changed: 303 additions & 80 deletions

File tree

elasticgraph-indexer/README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ module MyCompany
9090
end
9191
```
9292

93-
Decoded event hashes do not need to provide a schema version. When a version is omitted, the latest
94-
available schema artifact version is used for validation and record preparation. Decoders may include
95-
`schema_version` to request a specific schema artifact version.
93+
A decoded event hash may carry a `schema_version` to request a specific schema artifact version. The
94+
key is optional, because an ingestion format may have no versions at all. Each ingestion adapter
95+
decides what a missing version means for its own format. `elasticgraph-json_ingestion` uses the latest
96+
available JSON schema version.

elasticgraph-indexer/lib/elastic_graph/indexer/indexing_event_decoder.rb

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ def initialize(config:, schema_artifacts:, logger:)
2222
end
2323

2424
# @param payload [String] a raw payload from the transport
25-
# @return [Array<Hash<String, Object>>] the decoded ElasticGraph indexing events. Events do not
26-
# need to include a schema version; when omitted, the latest available schema version is used.
25+
# @return [Array<Hash<String, Object>>] the decoded ElasticGraph indexing events. An event may
26+
# include a `schema_version`, but does not have to: an ingestion format with no versions
27+
# omits it. Each ingestion adapter decides what a missing version means for its own format.
2728
def decode(payload)
2829
# :nocov: -- must return an array to satisfy Steep type checking but never called
2930
[]

elasticgraph-indexer/lib/elastic_graph/indexer/ingestion_adapter.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ def handles_event?(event)
3636
# Validates the given event and resolves the record preparer appropriate for the event's
3737
# schema version.
3838
#
39+
# The event's `schema_version` is optional, because an ingestion format may have no versions
40+
# at all. Each adapter decides what a missing version means for its own format.
41+
#
3942
# @param event [Hash<String, Object>] an ElasticGraph indexing event
4043
# @return [ValidationResult] the result of validating the event
4144
def validate_event(event)

elasticgraph-indexer/lib/elastic_graph/indexer/processor.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,18 @@ def calculate_latency_metrics(successful_operations, noop_results)
120120

121121
result = successful_events.include?(event) ? "success" : "noop"
122122

123+
# The schema version is optional, since an ingestion format may have no versions at all.
124+
schema_version = event[SCHEMA_VERSION_KEY]
125+
123126
@logger.info({
124127
"message_type" => "ElasticGraphIndexingLatencies",
125128
"message_id" => event["message_id"],
126129
"event_type" => event.fetch("type"),
127130
"event_id" => EventID.from_event(event).to_s,
128-
SCHEMA_VERSION_KEY => event.fetch(SCHEMA_VERSION_KEY),
131+
SCHEMA_VERSION_KEY => schema_version,
132+
# Deprecated alias of `schema_version`, kept so that dashboards and monitors that watch
133+
# the old name keep working.
134+
JSON_SCHEMA_VERSION_KEY => schema_version,
129135
"latencies_in_ms_from" => latencies_in_ms_from,
130136
"slo_results" => slo_results,
131137
"result" => result

elasticgraph-indexer/lib/elastic_graph/indexer/test_support/converters.rb

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,27 @@ module ElasticGraph
1414
class Indexer
1515
module TestSupport
1616
module Converters
17+
# Attributes that describe the event rather than the record, so they never reach the record.
18+
# `__json_schema_version` is the legacy name of `__schema_version`; projects generated before
19+
# the schema version became ingestion-format-neutral still use it.
20+
EVENT_ONLY_ATTRIBUTES = ["__typename", "__version", "__schema_version", "__json_schema_version"]
21+
1722
# Helper method for testing and generating fake data to convert a factory record into an event
1823
def self.upsert_event_for(record)
19-
{
24+
event = {
2025
"op" => "upsert",
2126
"id" => record.fetch("id"),
2227
"type" => record.fetch("__typename"),
2328
"version" => record.fetch("__version"),
24-
"record" => record.except("__typename", "__version", "__schema_version"),
25-
SCHEMA_VERSION_KEY => record.fetch("__schema_version")
29+
"record" => record.except(*EVENT_ONLY_ATTRIBUTES)
2630
}
31+
32+
# The schema version is optional, so include it only when the factory supplies one.
33+
if (schema_version = record["__schema_version"] || record["__json_schema_version"])
34+
event[SCHEMA_VERSION_KEY] = schema_version
35+
end
36+
37+
event
2738
end
2839

2940
# Helper method to create an array of events given an array of records

elasticgraph-indexer/sig/elastic_graph/indexer/test_support/converters.rbs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ module ElasticGraph
22
class Indexer
33
module TestSupport
44
module Converters
5+
EVENT_ONLY_ATTRIBUTES: ::Array[::String]
6+
57
def self.upsert_event_for: (::Hash[::String, untyped]) -> ::Hash[::String, untyped]
68

79
def self.upsert_events_for_records: (

elasticgraph-indexer/spec/unit/elastic_graph/indexer/operation/factory_spec.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,10 @@ module Operation
127127
expect_failed_event_error(event, "missing_keys", "type", expect_no_ops: true)
128128
end
129129

130-
it "notifies an error on missing `#{SCHEMA_VERSION_KEY}`" do
130+
it "builds operations for an event that carries no `#{SCHEMA_VERSION_KEY}`, since the key is optional" do
131131
event = build_upsert_event(:component).except(SCHEMA_VERSION_KEY)
132132

133-
expect_failed_event_error(event, SCHEMA_VERSION_KEY)
133+
expect(build_expecting_success(event)).not_to be_empty
134134
end
135135

136136
it "notifies an error on wrong field types" do

elasticgraph-indexer/spec/unit/elastic_graph/indexer/processor_spec.rb

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,20 @@ class Indexer
135135

136136
expect(logged_jsons_of_type("ElasticGraphIndexingLatencies").first).to include(
137137
"event_id" => "Component:#{component.fetch("id")}@v#{component.fetch("version")}",
138-
"message_id" => "m1"
138+
"message_id" => "m1",
139+
SCHEMA_VERSION_KEY => component.fetch(SCHEMA_VERSION_KEY),
140+
# Deprecated alias, kept for existing dashboards and monitors.
141+
JSON_SCHEMA_VERSION_KEY => component.fetch(SCHEMA_VERSION_KEY)
142+
)
143+
end
144+
145+
it "logs a nil schema version for an event that carries none, since the key is optional" do
146+
component = upsert_event_with_latency_timestamps(:component, 36, 72).except(SCHEMA_VERSION_KEY)
147+
process([component])
148+
149+
expect(logged_jsons_of_type("ElasticGraphIndexingLatencies").first).to include(
150+
SCHEMA_VERSION_KEY => nil,
151+
JSON_SCHEMA_VERSION_KEY => nil
139152
)
140153
end
141154

elasticgraph-indexer/spec/unit/elastic_graph/indexer/test_support/converters_spec.rb

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,42 @@ module TestSupport
3333
SCHEMA_VERSION_KEY => 1
3434
)
3535
end
36+
37+
it "accepts the legacy `__json_schema_version` attribute, so factories from older projects keep working" do
38+
factory_record = {
39+
"id" => "1",
40+
"__version" => 1,
41+
"__typename" => "Widget",
42+
"__json_schema_version" => 3,
43+
"field1" => "value1"
44+
}
45+
46+
expect(TestSupport::Converters.upsert_event_for(factory_record)).to eq(
47+
"op" => "upsert",
48+
"id" => "1",
49+
"version" => 1,
50+
"type" => "Widget",
51+
"record" => {"id" => "1", "field1" => "value1"},
52+
SCHEMA_VERSION_KEY => 3
53+
)
54+
end
55+
56+
it "omits the schema version when the factory record supplies none, since the key is optional" do
57+
factory_record = {
58+
"id" => "1",
59+
"__version" => 1,
60+
"__typename" => "Widget",
61+
"field1" => "value1"
62+
}
63+
64+
expect(TestSupport::Converters.upsert_event_for(factory_record)).to eq(
65+
"op" => "upsert",
66+
"id" => "1",
67+
"version" => 1,
68+
"type" => "Widget",
69+
"record" => {"id" => "1", "field1" => "value1"}
70+
)
71+
end
3672
end
3773

3874
describe ".upsert_events_for_records" do

elasticgraph-json_ingestion/README.md

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,6 @@ events and validates JSON-ingestion-specific schema options. Generated ElasticGr
77
and enable it by default. Applications that wire schema-definition tasks manually enable it by adding
88
`ElasticGraph::JSONIngestion::SchemaDefinition::APIExtension` to their schema-definition extension modules.
99

10-
## Indexing Event Decoder
11-
12-
JSON ingestion payloads can include `json_schema_version` to request a specific JSON schema artifact
13-
version. Configure the JSON ingestion decoder when the indexer consumes those payloads so the JSON-specific
14-
field is mapped to the indexer's generic `schema_version` event field.
15-
16-
```yaml
17-
indexer:
18-
indexing_event_decoder:
19-
name: ElasticGraph::JSONIngestion::IndexingEventDecoder::JSONLines
20-
require_path: elastic_graph/json_ingestion/indexing_event_decoder
21-
```
22-
2310
## Schema Definition APIs
2411

2512
Use `schema.json_schema_version` to identify the current JSON schema artifact. Every change that affects
@@ -119,11 +106,22 @@ end
119106

120107
Beyond schema definition, this gem teaches `elasticgraph-indexer` how to ingest JSON events: it provides an
121108
ingestion adapter that validates each event against the JSON schema identified by the event's
122-
`json_schema_version` and prepares its record for indexing using that version's view of the schema.
109+
`schema_version` and prepares its record for indexing using that version's view of the schema.
123110

124111
No configuration is needed: defining your schema with this gem's `SchemaDefinition::APIExtension` registers
125112
an indexer extension in your schema artifacts' runtime metadata, which the indexer applies when it boots.
126113

114+
### Schema versions
115+
116+
The adapter resolves the version of each event as follows:
117+
118+
- The `schema_version` key selects the JSON schema version. When the exact version is unavailable, the
119+
adapter selects the closest available version and logs `ElasticGraphMissingJSONSchemaVersion`.
120+
- The legacy `json_schema_version` key still works, so a publisher or an in-process caller that predates
121+
the ingestion-format-neutral key needs no change.
122+
- An event that carries neither key gets the latest available JSON schema version. The adapter still
123+
validates the event against that version, so a malformed event still fails.
124+
127125
This gem also provides the `be_a_valid_elastic_graph_event` RSpec matcher (via
128126
`require "elastic_graph/json_ingestion/spec_support/event_matcher"`) for testing that publisher events
129127
conform to your schema.

0 commit comments

Comments
 (0)