Skip to content

feat(ipc): Write dictionary deltas from ArrowIpcWriterWriteArrayViewWithDictionaries - #980

Draft
rustyconover wants to merge 2 commits into
apache:mainfrom
rustyconover:feat/ipc-writer-dictionary-deltas
Draft

rustyconover wants to merge 2 commits into
apache:mainfrom
rustyconover:feat/ipc-writer-dictionary-deltas

Conversation

@rustyconover

@rustyconover rustyconover commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #979. #928 and #968 have merged. Rebased onto the refreshed #979 branch; remains draft until #979 lands.

With #979, ArrowIpcWriterWriteArrayViewWithDictionaries() skips a dictionary that is unchanged since the last batch, but any change still replaces the whole dictionary. A common change is a dictionary that only grows: a producer encodes each batch against a dictionary that it appends new values to. Arrow streams can send just the appended values as a delta (DictionaryBatch.isDelta), which nanoarrow's reader supports since #955.

When a batch's dictionary is longer than the one last written for its ID and starts with the same values, the writer now writes only the appended values, as a delta. To check this, it copies the first n values, where n is the length of the saved copy, and compares that with the saved copy. On a match, it copies the appended values and writes them as a delta, then appends them to the prefix copy, which becomes the new saved copy. Anything else is a replacement, as before.

Deltas are only written to streams. In a file, a dictionary that grew is a replacement and returns ENOTSUP, matching #928, whose writer doesn't write deltas to files. A dictionary that is written again because a nested dictionary changed is written in full.

Cost: the same benchmark as #979, 1000 batches of 2048 rows, dictionary<int32, string>, written to a memory buffer (Release, Linux aarch64; best of 3):

#968 #979 this PR
10,000 values, growing by 16 a batch 205 ms, 458.3 MB 531 ms, 458.3 MB 346 ms, 9.2 MB
10,000 values, unchanged 126 ms, 258.5 MB 23 ms, 8.6 MB 23 ms, 8.6 MB

Tests (writer_test.cc):

  • WriteArrayViewWithDictionariesDeltas: dictionaries [foo, bar], [foo, bar, baz], [foo, bar, baz], [foo, qux] write a full dictionary, a delta, nothing, and a replacement (isDelta 0, 1, 0). Read back with nanoarrow and Arrow C++.
  • WriteArrayViewWithDictionariesNullableDelta: int64 values [1, null, 3] growing to [1, null, 3, null, 5], where the delta starts partway through a byte of the validity bitmap.
  • WriteArrayViewWithDictionariesNoDeltaInFile: in a file, a dictionary that grew returns ENOTSUP.

ctest passes (Linux aarch64, Debug, 881 tests with NANOARROW_BUILD_TESTS_WITH_ARROW), and 406 tests under ASan/UBSan with clang. The writer tests are clean under valgrind. The changed files are formatted with clang-format 16.0.6, the pre-commit version.

This PR's code and description were written by AI (Claude Code) and reviewed by a human maintainer.

🤖 Generated with Claude Code

Validation after rebasing (October 26): macOS arm64 Debug, 429 CTest cases, no failures; 16 compression cases skipped because compression is disabled in this build.

@codecov-commenter

codecov-commenter commented Oct 19, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 79.25532% with 39 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.52%. Comparing base (08d23f6) to head (6bc6f0c).

Files with missing lines Patch % Lines
src/nanoarrow/ipc/writer.c 79.25% 8 Missing and 31 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #980      +/-   ##
==========================================
- Coverage   78.52%   78.52%   -0.01%     
==========================================
  Files         106      106              
  Lines       17195    17381     +186     
  Branches     2064     2121      +57     
==========================================
+ Hits        13502    13648     +146     
- Misses       2431     2439       +8     
- Partials     1262     1294      +32     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rustyconover
rustyconover force-pushed the feat/ipc-writer-dictionary-deltas branch 2 times, most recently from 09d8978 to cf1f753 Compare October 20, 2026 22:34
rustyconover and others added 2 commits October 26, 2026 11:55
…wWithDictionaries

ArrowIpcWriterWriteArrayViewWithDictionaries() wrote every dictionary before
every RecordBatch: its input is borrowed, so it couldn't compare a dictionary
with the previous batch's by address as ArrowIpcWriterWriteArrayStream() does.
The writer now keeps a copy of each dictionary it writes and compares the next
batch's values with it, writing only dictionaries that changed (and those whose
values contain a nested dictionary that was written). In a file, batches whose
dictionaries are unchanged can now be written this way.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ithDictionaries

When a batch's dictionary starts with the values last written for its ID, the
writer now writes only the appended values, as a delta DictionaryBatch, instead
of replacing the whole dictionary. This only applies to streams: a file does not
support deltas.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@rustyconover
rustyconover force-pushed the feat/ipc-writer-dictionary-deltas branch from cf1f753 to 6bc6f0c Compare October 26, 2026 16:05

This branch has not been deployed

No deployments
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.

2 participants