Skip to content

cli: add Apache Arrow IPC support to mcap convert - #1749

Draft
amacneil wants to merge 5 commits into
mainfrom
cursor/add-arrow-convert-support-7d91
Draft

cli: add Apache Arrow IPC support to mcap convert#1749
amacneil wants to merge 5 commits into
mainfrom
cursor/add-arrow-convert-support-7d91

Conversation

@amacneil

@amacneil amacneil commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds Apache Arrow support to the Rust CLI for the arrow message/schema encodings proposed in #1748, on both the write and read paths:

  • mcap convert reads an Arrow IPC file or stream (.arrow, .feather, .ipc, .arrows) and writes a single channel using the arrow message encoding, with the Arrow IPC Schema stored once in the Schema record (arrow schema encoding). Each input row becomes one message containing a bare encapsulated RecordBatch (matching pyarrow.RecordBatch.serialize()) — no stream framing, no embedded schema, no Arrow body compression. Dictionary-encoded columns are hydrated to their value type so every RecordBatch decodes independently.
  • mcap cat --json now decodes arrow messages: it reads each RecordBatch against the channel's Schema record and emits the rows. Because a message may hold multiple rows, the data field is a JSON array of row objects.

Timestamp handling

log_time/publish_time are derived from Arrow columns:

  • --log-time-field <NAME> selects the column; otherwise a column named log_time is used, falling back to the first timestamp/date column in schema order (Arrow field order is well-defined and preserved through IPC).
  • --publish-time-field <NAME>, then a column named publish_time, otherwise defaults to log_time.
  • Arrow Timestamp/Date32/Date64 columns are scaled from their intrinsic unit to nanoseconds. Integer columns use --timestamp-unit {s,ms,us,ns} (default ns).
  • Null, negative, or out-of-u64 times are hard errors.

New flags on convert

--topic (defaults to the input file stem), --schema-name, --log-time-field, --publish-time-field, --timestamp-unit, --rows-per-message (default 1, for opt-in row batching).

Dependency

Adds arrow (ipc, json, chrono-tz features). json powers cat --json via arrow-json's writer; chrono-tz is required so named timezones (e.g. timestamp[us, tz=UTC]) render correctly.

Testing

  • 14 new unit tests (13 for convert in arrow_ipc.rs, 1 for cat --json in cat.rs). The convert tests generate Arrow IPC fixtures dynamically (no committed binaries / LFS), covering unit scaling, auto-detection, named/explicit/default field resolution, dictionary hydration, row batching, stream vs file framing, multi-batch files, and error paths; each round-trips the emitted bytes back through an Arrow StreamReader.
  • Manual end-to-end: converted a timestamp[us, tz=UTC] + dictionary Arrow file and a real third-party Arrow file (samplefile.com), verified with mcap info/list, mcap cat --json, and a pyarrow round-trip decode.

Depends conceptually on #1748 (registry entry); this PR is the implementation.

Open in Web Open in Cursor 

Co-authored-by: Adrian Macneil <adrian@foxglove.dev>

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Solid converter, tests round-trip through a real StreamReader which is the right way to prove spec compliance. A few things to settle, mostly inline.

One risk that doesn't pin to a single line: Arrow's time handling fails hard mid-stream (null, negative, or out-of-u64 values are errors). Unlike the bag/db3 paths, a bad value deep in a large file aborts after the output .mcap has already been partially written and never finalized, leaving a truncated file at the output path. Worth confirming that's acceptable, or cleaning up the output on error.

Open question on sequencing: this rides on #1748 for the arrow encoding registration — see the docs comment. Draft is the right state until that lands.

Comment thread rust/cli/src/commands/convert/arrow_ipc.rs
Comment thread rust/cli/src/commands/convert.rs Outdated
Comment thread rust/cli/src/cli.rs
Comment thread rust/cli/src/commands/convert/arrow_ipc.rs Outdated

<!-- cspell: enable -->

Each input row becomes one MCAP message (an [Arrow IPC](https://arrow.apache.org/docs/format/Columnar.html#serialization-and-interprocess-communication-ipc) encapsulated `RecordBatch`) on a single channel using the [`arrow`](https://mcap.dev/spec/registry#arrow) message and schema encodings. Dictionary-encoded columns are hydrated to their value type, as required by the encoding.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This registry#arrow anchor — and the arrow message/schema encoding names this converter emits — depend on #1748, which isn't merged. Until the registry entry is published, this link 404s and we'd be shipping files with an unregistered encoding. Make sure this doesn't land ahead of #1748.

Co-authored-by: Adrian Macneil <adrian@foxglove.dev>
claude[bot]

This comment was marked as outdated.

cursoragent and others added 2 commits June 29, 2026 04:39
- Derive default topic from the original input, not the materialized
  remote temp path
- Drop the undocumented .arrows extension
- Only read timestamps from each message's leader row so nulls in
  non-leader rows of a batched group don't fail conversion
- Remove a partially written output file when conversion fails
- Warn when arrow-only flags are passed for a non-arrow input

Co-authored-by: Adrian Macneil <adrian@foxglove.dev>
Co-authored-by: Adrian Macneil <adrian@foxglove.dev>
claude[bot]

This comment was marked as outdated.

Co-authored-by: Adrian Macneil <adrian@foxglove.dev>

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

New commit closes last round's decoder-reuse coverage gap — arrow_json_reuses_cached_decoder_across_messages runs two messages through one JsonTranscoders and asserts the second decodes via the cached decoder. 👍

Still gated on #1748: files written with the arrow encoding (and the registry#arrow doc link) can't land until that registry entry is published. Open thread on cli.md tracks it — draft is the right state until then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants