Skip to content

Commit 7239bab

Browse files
committed
Put legacy layout hint under protected metadata
1 parent 7b382dc commit 7239bab

1 file changed

Lines changed: 24 additions & 43 deletions

File tree

rfcs/2026-04-29-25329-trace-data-model.md

Lines changed: 24 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -893,8 +893,8 @@ During the migration, `TraceEvent` is an enum:
893893
pub enum TraceEvent {
894894
/// Pre-migration source output: an untyped `LogEvent` whose key layout is
895895
/// defined by the producing source. The producing source identifies its
896-
/// layout in `LogEvent.metadata().value()` under the reserved key
897-
/// `_vector.trace_legacy_layout` so the correct shim can be selected
896+
/// layout in `LogEvent.metadata().value()` under the reserved sub-key
897+
/// `vector.trace_legacy_layout` so the correct shim can be selected
898898
/// after fan-in, disk-buffer round-trips, or `vector` source/sink hops.
899899
Legacy(LogEvent),
900900
/// Post-migration typed container.
@@ -908,32 +908,11 @@ pub enum TraceEvent {
908908
}
909909
```
910910

911-
The shim dispatch needs a stable producer identifier that survives every transport step a
912-
`Legacy` event takes inside Vector: fan-in, disk buffers, and `vector` source/sink hops.
913-
`EventMetadata.source_type` is not viable on its own: the topology source pump in
914-
`src/topology/builder.rs` (the `run_source_output_pump` task) unconditionally calls
915-
`metadata.set_source_type(source_type)` on every event a source emits, so a `Legacy` trace
916-
deserialized by the `vector` source has its upstream `source_type` (`"opentelemetry"` or
917-
`"datadog_agent"`) rewritten to `"vector"` and the original layout identifier is lost. The
918-
overwrite is correct for the field's documented role (it identifies the source that produced
919-
the local emission), but it forecloses using `source_type` as the shim selector across a
920-
serialised hop.
921-
922-
The migration carries the layout hint in the existing
923-
`EventMetadata.value` arbitrary-metadata `Value` instead, under the reserved key
924-
`_vector.trace_legacy_layout`. The `opentelemetry` and `datadog_agent` sources each set this
925-
key to a static string identifying themselves on every `Legacy` trace they emit; the source
926-
pump does not touch `EventMetadata.value`, and the metadata `Value` is serialised end-to-end
927-
by the existing `Metadata`/`metadata_full` proto encoding, so the hint survives any number of
928-
intermediate hops including `vector` source/sink serialisation. The shim dispatch reads this
929-
key to select the correct `Legacy -> Typed` conversion. A `Legacy` event whose hint is absent
930-
or maps to no registered shim returns an error and emits a rate-limited `warn!` log rather
931-
than guessing the layout.
932-
933-
The reserved key disappears with the `Legacy` variant: once every trace source has migrated to
934-
native `Typed` output, the sources stop setting it, the shim dispatch is removed, and no
935-
production traffic carries it. No permanent type-system or wire-format additions are required;
936-
the convention lives only as long as the migration enum does.
911+
Trace event-producing sources each set the reserved sub-key `vector.trace_legacy_layout` in
912+
`EventMetadata.value` to a static string identifying themselves on every `Legacy` trace they emit.
913+
`to_typed()` reads this sub-key to select the corresponding `Legacy -> Typed` shim. A `Legacy` event
914+
whose hint is absent or maps to no registered shim returns an error and emits a rate-limited `warn!`
915+
log.
937916

938917
The end-state `struct TraceEvent { resource, scope, chunk, spans, metadata }` shown above is
939918
reached by deleting the `Legacy` arm once every component has migrated; the `Typed` arm's
@@ -957,7 +936,7 @@ Both accessor families coexist on `TraceEvent` and dispatch on the variant:
957936
zero-overhead post-migration signature. The panic is the loud failure that the intake-convert
958937
pattern below avoids.
959938
- Explicit `to_typed(&mut self)` rewrites a `Legacy` variant in place into `Typed` by reading the
960-
`_vector.trace_legacy_layout` hint from `EventMetadata.value` and invoking the corresponding
939+
`vector.trace_legacy_layout` hint from `EventMetadata.value` and invoking the corresponding
961940
source-specific shim. Already-`Typed` events are a no-op. There is no symmetric `to_legacy`.
962941

963942
Per-component shims are unidirectional (`Legacy -> Typed` only). The `datadog_agent` source
@@ -1125,16 +1104,18 @@ are deleted, leaving only the typed struct.
11251104
etc.) are removed from `TraceEvent` before the source steps; every remaining call site then
11261105
fails to compile, making the consumer migration a mechanical fix-the-build task rather than a
11271106
runtime-failure audit.
1128-
- Shim selection is keyed on a reserved key `_vector.trace_legacy_layout` carried in
1129-
`EventMetadata.value` and set by the producing trace source. The metadata `Value` is serialized
1130-
with every event record and passes through fan-in, disk buffers, and `vector` source/sink hops
1131-
unchanged (unlike `EventMetadata.source_type`, which the topology source pump rewrites on every
1132-
emission and so cannot serve as the selector across a serialised hop). Conversion is invoked
1133-
explicitly by `to_typed(&mut self)`; immutable typed accessors panic on `Legacy` rather than
1134-
converting on demand, because returning typed references through a `&self` accessor would
1135-
require either mutating `self` or returning owned/`Cow` shapes that would have to be torn out
1136-
again post-migration. The convention lives only for the duration of the migration and
1137-
disappears with the `Legacy` variant; no new struct field or wire-format extension is needed.
1107+
- Shim selection is keyed on a reserved sub-key `vector.trace_legacy_layout` in
1108+
`EventMetadata.value` set by the producing trace source. The `vector` metadata namespace is
1109+
read-only to VRL programs (configured by `compile_vrl`), so transforms between source and sink
1110+
cannot accidentally delete or overwrite the hint. The metadata `Value` is serialized with every
1111+
event record and passes through fan-in, disk buffers, and `vector` source/sink hops unchanged
1112+
(unlike `EventMetadata.source_type`, which the topology source pump rewrites on every emission
1113+
and so cannot serve as the selector across a serialised hop). Conversion is invoked explicitly
1114+
by `to_typed(&mut self)`; immutable typed accessors panic on `Legacy` rather than converting on
1115+
demand, because returning typed references through a `&self` accessor would require either
1116+
mutating `self` or returning owned/`Cow` shapes that would have to be torn out again
1117+
post-migration. The convention lives only for the duration of the migration and disappears with
1118+
the `Legacy` variant; no new struct field or wire-format extension is needed.
11381119

11391120
## Drawbacks
11401121

@@ -1442,14 +1423,14 @@ shims are unidirectional (`Legacy -> Typed` only) and a `Typed` event has no sou
14421423
on which to base a `Typed -> Legacy` conversion.
14431424

14441425
- [ ] Land the legacy-layout hint as a precursor: the `opentelemetry` and `datadog_agent` sources
1445-
each set `EventMetadata.value`'s reserved key `_vector.trace_legacy_layout` to a static string
1426+
each set `EventMetadata.value`'s reserved sub-key `vector.trace_legacy_layout` to a static string
14461427
identifying themselves on every trace event they emit. Purely additive -- no consumer reads the
14471428
key yet. See "Migration: coexistence of `LogEvent` and typed representations" for the rationale.
14481429
- [ ] Convert `TraceEvent` to the migration enum: `Legacy(LogEvent)` and `Typed { resource, scope,
14491430
chunk, spans, metadata }`, demonstrating the new structure and the supporting types. Add the
14501431
explicit `to_typed(&mut self)` converter that reads
1451-
`EventMetadata.value."_vector.trace_legacy_layout"` and invokes the corresponding `Legacy ->
1452-
Typed` shim. Typed accessors (`resource()`, `spans()`, ...) panic on `Legacy` with a diagnostic
1432+
`EventMetadata.value` sub-key `vector.trace_legacy_layout` and invokes the corresponding
1433+
`Legacy -> Typed` shim. Typed accessors (`resource()`, `spans()`, ...) panic on `Legacy` with a diagnostic
14531434
pointing at `to_typed()`. Every component still produces and consumes `Legacy`; nothing
14541435
functionally changes. A `to_typed()` call on an event whose layout hint is absent or unrecognized
14551436
returns an error and emits a rate-limited `warn!` log.
@@ -1466,7 +1447,7 @@ on which to base a `Typed -> Legacy` conversion.
14661447
`component_errors_total{error_type="unsupported_payload_version"}` increment and a
14671448
rate-limited error log instead of being silently translated.
14681449
- [ ] OTLP `Legacy -> Typed` shim and `Typed -> OTLP-wire` conversion in `lib/opentelemetry-proto`.
1469-
The shim dispatch infrastructure uses the `_vector.trace_legacy_layout` hint in
1450+
The shim dispatch infrastructure uses the `vector.trace_legacy_layout` hint in
14701451
`EventMetadata.value` to select the OTLP shim on demand. The `Typed -> wire` conversion is what
14711452
the eventual native source emission and any OTLP sink will use.
14721453
- [ ] Remove the legacy `tracerPayloads`-empty ingest branch from

0 commit comments

Comments
 (0)