Skip to content

fix(otel): preserve all exemplars per data point (#1662)#1720

Open
prabhaks wants to merge 1 commit into
parseablehq:mainfrom
prabhaks:fix/1662-exemplars-array-column
Open

fix(otel): preserve all exemplars per data point (#1662)#1720
prabhaks wants to merge 1 commit into
parseablehq:mainfrom
prabhaks:fix/1662-exemplars-array-column

Conversation

@prabhaks

@prabhaks prabhaks commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

What

Fixes #1662.

insert_exemplars in src/otel/metrics.rs wrote every exemplar of a data point to the same static columns (exemplar_time_unix_nano, exemplar_span_id, exemplar_trace_id, exemplar_value). Since the keys were shared, each exemplar overwrote the previous one and only the last survived. This affected Sum, Histogram and ExponentialHistogram (and Gauge, which shares the same NumberDataPoint path).

Approach

Following the recommended option 1 in the issue, all exemplars for a data point are now serialized into a single nested exemplars array column. Each element is an object with exemplar_time_unix_nano, exemplar_span_id, exemplar_trace_id, exemplar_value and any filtered attributes. This matches the OTel data model (exemplars are a repeated field per data point) and keeps the schema stable regardless of exemplar count.

Backward compatibility

Added a P_OTEL_FLATTEN_EXEMPLARS flag (default false). When set to true the previous flat exemplar_* columns are emitted (last exemplar wins, same as before) for anyone already querying those columns. Per the discussion on the issue, the default is the new array behavior.

Bonus fix

Previously exemplar attributes were written straight into the data point map, so they leaked into compute_series_hash and fragmented physical-series identity. exemplars is now part of OTEL_METRICS_KNOWN_FIELD_LIST, so exemplar contents no longer affect the series hash.

Query validation (PoC)

Before settling on the schema I ran a PoC against the same library versions used here (datafusion 53, arrow/arrow-json 58) to confirm the query layer handles the array-of-objects column. arrow-json infers it as List<Struct<...>>, and DataFusion handles reading the column, array_length, unnest, struct field access, and filtering on nested fields. Details are in the issue thread.

Testing

  • cargo test: 311 passed, 0 failed.
  • New unit tests cover: all exemplars preserved in the array, legacy flat mode keeps last, empty exemplars insert nothing, end to end through a Sum, exemplar contents do not affect the series hash, and the known-field list update.

Flag reference

P_OTEL_FLATTEN_EXEMPLARS env var / --otel-flatten-exemplars flag, default false (new nested exemplars array). Set to true for the legacy flat exemplar_* columns.

Summary by CodeRabbit

  • New Features
    • Added --otel-flatten-exemplars / P_OTEL_FLATTEN_EXEMPLARS to control how OTEL metric exemplars are represented when flattening.
    • Default preserves all exemplars in a nested exemplars structure; enabling uses legacy flat exemplar_* fields (“last wins”).
  • Bug Fixes
    • Metric series identity/hashing no longer changes based on exemplar contents.
  • Tests
    • Added regression coverage for nested vs legacy behavior, empty exemplars, end-to-end flattening, and series-hash invariance.

@coderabbitai

coderabbitai Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

OTEL metrics now support configurable exemplar storage: nested arrays preserve all exemplars by default, while P_OTEL_FLATTEN_EXEMPLARS enables legacy flattened columns. The mode flows through JSON and protobuf processing, with updated series identity handling and regression tests.

Changes

OTEL exemplar handling

Layer / File(s) Summary
Configuration and series identity contract
src/cli.rs, src/otel/metrics.rs
Adds the otel_flatten_exemplars option and registers exemplars as a known OTEL metric field.
Exemplar serialization and flattening
src/otel/metrics.rs
Adds JSON conversion helpers and applies nested-array or legacy flattened exemplar output across gauge, sum, histogram, and exponential-histogram records.
Pipeline wiring and validation
src/otel/metrics.rs
Propagates the option through JSON and protobuf processing and tests preservation, legacy overwrite behavior, empty exemplars, and series hashes.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant OTELClient
  participant flatten_otel_metrics
  participant process_resource_metrics
  participant flatten_metrics_record
  participant insert_exemplars

  OTELClient->>flatten_otel_metrics: OTEL metrics payload
  flatten_otel_metrics->>process_resource_metrics: configured exemplar mode
  process_resource_metrics->>flatten_metrics_record: metric record and mode
  flatten_metrics_record->>insert_exemplars: exemplar list and mode
  insert_exemplars-->>flatten_metrics_record: nested array or flattened columns
  flatten_metrics_record-->>process_resource_metrics: flattened records
Loading

Suggested reviewers: nikhilsinhaparseable

Poem

I’m a rabbit with metrics to spare,
Nesting exemplars with careful care.
Or flatten them wide, old-style in a row,
While every little trace gets a place to show.
Hop, hop—the data stays whole!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the core fix: preserving all OTEL exemplars per data point.
Description check ✅ Passed The description covers the issue, approach, compatibility flag, testing, and flag reference, with only minor template sections omitted.
Linked Issues check ✅ Passed The changes match #1662 by preserving exemplars in a nested array, supporting legacy flat mode, and covering affected metric types.
Out of Scope Changes check ✅ Passed No obvious unrelated code changes are present beyond the exemplar preservation fix and its related compatibility and hashing updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@prabhaks

Copy link
Copy Markdown
Contributor Author

Note on the failing coverage check: it fails at the clippy step because of pre-existing lints on main from the newer Rust/clippy 1.97, not because of this PR (none of the flagged files are touched here). I opened #1721 to fix those lints and unblock CI. Once #1721 is merged I will rebase this branch on top so its checks go green.

@nikhilsinhaparseable

Copy link
Copy Markdown
Contributor

@prabhaks thank you for raising the lint PR, i have verified and merged it, you can rebase this PR now

@nikhilsinhaparseable nikhilsinhaparseable self-requested a review July 11, 2026 04:57
@prabhaks prabhaks force-pushed the fix/1662-exemplars-array-column branch from 42b3bde to 1e9eb32 Compare July 11, 2026 05:45
@prabhaks

Copy link
Copy Markdown
Contributor Author

@prabhaks thank you for raising the lint PR, i have verified and merged it, you can rebase this PR now

Done, thank you for reviewing and merging my other PR!

@prabhaks prabhaks force-pushed the fix/1662-exemplars-array-column branch from 1e9eb32 to bf49759 Compare July 11, 2026 05:57
@prabhaks

Copy link
Copy Markdown
Contributor Author

@nikhilsinhaparseable @parmesant please take a look at this PR whenever you get a chance!

@prabhaks

Copy link
Copy Markdown
Contributor Author

@parmesant @nikhilsinhaparseable Any thoughts on this fix?

@parmesant

Copy link
Copy Markdown
Contributor

Hey @prabhaks
Got caught up with another issue today. I'll pick this one up tomorrow

@parmesant parmesant force-pushed the fix/1662-exemplars-array-column branch from 0134035 to 9685f44 Compare July 14, 2026 07:28
insert_exemplars wrote every exemplar of a data point to the same static
columns (exemplar_time_unix_nano, exemplar_span_id, exemplar_trace_id,
exemplar_value), so each exemplar overwrote the previous one and only the
last survived. This affected Sum, Histogram, ExponentialHistogram (and
Gauge) metric types.

Serialize all exemplars into a single nested `exemplars` array column
(array of objects), the OTel-faithful representation recommended in the
issue. Each element carries exemplar_time_unix_nano, exemplar_span_id,
exemplar_trace_id, exemplar_value, and any filtered attributes.

Add the P_OTEL_FLATTEN_EXEMPLARS flag (default false) to opt back into
the legacy flat columns for backward compatibility.

Also stops exemplar attributes from leaking into the physical-series
hash: `exemplars` is added to OTEL_METRICS_KNOWN_FIELD_LIST so exemplar
contents no longer fragment series identity.
@parmesant parmesant force-pushed the fix/1662-exemplars-array-column branch from 9685f44 to a8bbdbc Compare July 15, 2026 14:08
@parmesant

Copy link
Copy Markdown
Contributor

@prabhaks the PR looks good but there is a doubt that we have-

  • While ingesting data from astronomy shop, we could see that a new column called exemplars_exemplar_value_list got created which contained the array of values and another column exemplars_exemplar_value which is empty. Is that intended?

@prabhaks

Copy link
Copy Markdown
Contributor Author

@prabhaks the PR looks good but there is a doubt that we have-

  • While ingesting data from astronomy shop, we could see that a new column called exemplars_exemplar_value_list got created which contained the array of values and another column exemplars_exemplar_value which is empty. Is that intended?

Hi @parmesant - I am on vacation till July 22nd. Will get back to you after that!

@parmesant

Copy link
Copy Markdown
Contributor

Have a great vacation!

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.

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

3 participants