Skip to content

Bug: Multiple exemplars per data point overwrite each other in OTel metrics flattening #1662

@coderabbitai

Description

@coderabbitai

Summary

Tracked from PR #1661 (comment: #1661 (comment)).

Reported by @parmesant.

Current Behavior

In src/otel/metrics.rs, the insert_exemplars helper (introduced in PR #1661 to replace the old flatten_exemplar) iterates over the Vec<Exemplar> for a data point and writes each exemplar's fields directly into the same parent map, using static key names:

  • exemplar_time_unix_nano
  • exemplar_span_id
  • exemplar_trace_id
  • exemplar_value

Because every exemplar in the slice writes to the same keys, each iteration overwrites the previous one. After the loop only the last exemplar is retained in the flattened record. All prior exemplars are silently dropped.

This affects Sum, Histogram, and ExponentialHistogram metric types — all of which carry exemplars as Vec<Exemplar>.

The previous implementation (flatten_exemplar) returned a Vec<Map<String, Value>> (one Map per exemplar), which at least preserved each exemplar in a separate structure, though the call sites then merged only the first map.

Ideal Behavior

Exemplars for a data point should be preserved in full. Possible approaches:

  1. Array column – Serialize the exemplars as a Value::Array of Value::Object entries and store them under a single column (e.g., exemplars). Each element would contain exemplar_time_unix_nano, exemplar_span_id, exemplar_trace_id, exemplar_value, and any filtered attributes. This is the most semantically faithful representation and aligns with how the OTel spec treats exemplars (a list per data point).

  2. Indexed flat columns – Emit exemplar_0_time_unix_nano, exemplar_1_time_unix_nano, … etc. This avoids nested types but creates a variable, unbounded number of columns.

  3. Emit one row per exemplar – Fan out exemplar data so each exemplar becomes its own row joined to its parent data point's fields. This is queryable without nested-type support but multiplies row count.

Option 1 (array column) is recommended as it matches the OTel data model, keeps schema stable, and allows query-layer unnesting when needed.

References

  • PR Ingestion optimization #1661: Ingestion optimization
  • OTel Metrics proto spec: exemplars are repeated Exemplar on NumberDataPoint, HistogramDataPoint, and ExponentialHistogramDataPoint.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions