Skip to content

perf(arrow codec): drop serde_json::Value round-trip in Arrow encoding#25773

Open
benjamin-awd wants to merge 2 commits into
vectordotdev:masterfrom
benjamin-awd:perf/arrow-drop-json-roundtrip
Open

perf(arrow codec): drop serde_json::Value round-trip in Arrow encoding#25773
benjamin-awd wants to merge 2 commits into
vectordotdev:masterfrom
benjamin-awd:perf/arrow-drop-json-roundtrip

Conversation

@benjamin-awd

Copy link
Copy Markdown
Contributor

Summary

The Arrow codec used to build each record batch in two passes:

event -> serde_json::Value -> Arrow JSON decoder -> record batch

The first pass is unnecessary. Arrow's decoder accepts anything that implements Serialize, and LogEvent already does, so events can go straight into the decoder:

event -> Arrow JSON decoder -> record batch

Vector configuration

No configuration changes.

How did you test this PR?

Ran unit/integration tests for Arrow + Parquet

Change Type

  • Bug fix
  • New feature
  • Dependencies
  • Non-functional (chore, refactoring, docs)
  • Performance

Is this a breaking change?

Technically a breaking change, since non-finite floats (inf/-inf) are now passed through to the destination, whereas previously they were encoded as null.

This behavior is arguably more correct and unlikely to affect users.

  • Yes
  • No

Does this PR include user facing changes?

  • Yes. Please add a changelog fragment based on our guidelines.
  • No. A maintainer will apply the no-changelog label to this PR.

References

Notes

  • Please read our Vector contributor resources.
  • Do not hesitate to use @vectordotdev/vector to reach out to us regarding this PR.
  • Some CI checks run only after we manually approve them.
    • We recommend adding a pre-push hook, please see this template.
    • Alternatively, we recommend running the following locally before pushing to the remote branch:
      • make fmt
      • make check-clippy (if there are failures it's possible some of them can be fixed with make clippy-fix)
      • make test
  • After a review is requested, please avoid force pushes to help us review incrementally.
    • Feel free to push as many commits as you want. They will be squashed into one before merging.
    • For example, you can run git merge origin master and git push.
  • If this PR introduces changes Vector dependencies (modifies Cargo.lock), please
    run make build-licenses to regenerate the license inventory and commit the changes (if any). More details on the dd-rust-license-tool.

Arrow's JSON decoder accepts any `Serialize` type, so there is no need to
first materialize each event into a `serde_json::Value` tree. Serialize
`&LogEvent`s straight into the decoder, removing one tree allocation and one
`Serialize` walk per event on the encode hot path.

`build_record_batch` and `find_null_non_nullable_fields` now take
`&[&LogEvent]`. `vector_log_events_to_json_values` is retained only for the
Parquet auto-infer path (Arrow's JSON schema inference still needs it) and is
gated behind the `parquet` feature.

Behavior is preserved: all type coercion still runs through the unchanged Arrow
JSON decoder via the identical `Serialize` path.

A Criterion bench on a ~10 MB batch of wide/nested events shows encode time
drop ~30% (record-batch build 27.8ms -> 19.3ms, end-to-end IPC 29.3ms ->
20.4ms).
@benjamin-awd benjamin-awd requested a review from a team as a code owner July 8, 2026 06:05

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1dacc9a1ed

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +359 to +360
let json_values = match vector_log_events_to_json_values(&events) {
Ok(values) => values,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Infer Parquet schemas from the encoded values

In schema_mode = auto_infer, this still infers from serde_json::Values while the batch is now encoded from the original LogEvents. serde_json::to_value converts non-finite floats such as inf/-inf to null, so a batch where a field contains only those values is inferred as an Arrow Null column; the subsequent build_record_batch(..., &logs) then tries to decode the original float into that Null column and the parquet encode errors instead of producing the new pass-through behavior.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handled in 4fc638e

@benjamin-awd benjamin-awd force-pushed the perf/arrow-drop-json-roundtrip branch from f3c9e85 to 6803d82 Compare July 8, 2026 06:31
In AutoInfer mode the schema is inferred from serde_json::Values, which
coerce non-finite floats (inf/-inf) to null and therefore infer a Null
column. After dropping the JSON round-trip on the encode path, the record
batch was built from the original log events instead, feeding those raw
floats into the inferred Null column, which the decoder rejects.

Encode AutoInfer batches from the same JSON values used for inference so
the data and schema always agree. Strict/Relaxed keep the direct
log-event path.
@benjamin-awd benjamin-awd force-pushed the perf/arrow-drop-json-roundtrip branch from 6803d82 to 4fc638e Compare July 8, 2026 06:36

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4fc638ed43

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

self.schema = Arc::new(ParquetSchemaGenerator::try_normalize_schema(
&events, schema,
));
decode_rows_to_record_batch(Arc::clone(&self.schema), &json_values)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve non-finite floats in auto-inferred Parquet

For schema_mode = auto_infer, the batch is still encoded from serde_json::Values, so the new direct encoder never sees non-finite floats. If a Parquet batch contains inf/-inf, serde_json::to_value has already converted those values to null; with a finite value in the same batch the column can still infer as Float64 but the non-finite rows are written as null, and with only non-finite values the field becomes Null. Fresh evidence in this revision is the explicit decode_rows_to_record_batch(..., &json_values) call here after schema inference, so auto-infer still differs from relaxed/strict and from the pass-through behavior described for this change.

Useful? React with 👍 / 👎.

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.

Drop unnecessary round-trip in ClickHouse Arrow encoding

1 participant