Repository navigation
feat(ipc): Write dictionary deltas from ArrowIpcWriterWriteArrayViewWithDictionaries - #980
Draft
rustyconover wants to merge 12 commits into
Draft
rustyconover wants to merge 12 commits into
rustyconover wants to merge 12 commits into
Conversation
ArrowIpcEncoderSetMessageMetadata() was ignored by ArrowIpcEncoderEncodeSimpleDictionaryBatch(); encode it like the schema and record batch messages do. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The IPC Field for a dictionary-encoded column describes the dictionary value type, so its children must come from schema->dictionary rather than from the index schema. This replaces the ENOTSUP for dictionary values with children (e.g., struct dictionaries whose children are themselves dictionary-encoded). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Add ArrowIpcEncoderSetDictionaryReplacement() to declare the DICTIONARY_REPLACEMENT feature in an encoded Schema message. The writer enables it in stream mode, where a dictionary ID may be written more than once, and the reader no longer rejects streams that declare it (the decoder already replaces dictionaries). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
On main, a file could contain exactly one DictionaryBatch, so a schema with more than one dictionary-encoded field couldn't be written, and the footer schema couldn't resolve its dictionary IDs when finalizing the file. Record the dictionary encodings of the footer schema and track the dictionary IDs written to the file: a second batch for the same ID returns EINVAL because IPC files don't support dictionary replacement (deltas remain unsupported when writing files). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
When dictionary values contain dictionary-encoded children, a reader needs the nested dictionaries before it can decode the DictionaryBatch that references them. Keep assigning IDs in schema order but write each dictionary after the dictionaries nested in its values. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
ArrowIpcWriterWriteArrayStream() wrote every dictionary before every record batch. Skip a dictionary when it has the same length, offset, null count, and buffer addresses (recursively) as the corresponding dictionary of the previous array, which is the case when a producer shares one dictionary among the arrays of a stream (e.g., the nanoarrow IPC reader). Comparing addresses is only safe while both arrays are alive, so the writer keeps the previous array (if it has dictionaries) until the next one is written. ArrowIpcWriterWriteDictionaryBatch() only sees a borrowed view that it can't keep alive and always writes the dictionary. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Write the integration files back using ArrowIpcWriterWriteArrayStream(), which writes dictionaries (writing the arrays with ArrowIpcWriterWriteArrayView() alone does not), and enable the write roundtrip for the dictionary, nested dictionary, shared dictionary, and extension (whose dict-extension column is dictionary-encoded) files. Because the reader shares dictionaries among the batches it decodes, these roundtrips also exercise skipping unchanged dictionaries. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… a repeated file dictionary ID Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #980 +/- ##
==========================================
+ Coverage 78.44% 78.54% +0.09%
==========================================
Files 106 106
Lines 17050 17288 +238
Branches 2018 2097 +79
==========================================
+ Hits 13375 13578 +203
+ Misses 2432 2430 -2
- Partials 1243 1280 +37 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
…message-metadata # Conflicts: # src/nanoarrow/ipc/writer.c
…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
force-pushed
the
feat/ipc-writer-dictionary-deltas
branch
from
October 20, 2026 03:48
2569825 to
09d8978
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
nvalues, wherenis 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):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 (isDelta0, 1, 0). Read back with nanoarrow and Arrow C++.WriteArrayViewWithDictionariesNullableDelta:int64values[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.ctestpasses (Linux aarch64, Debug, 881 tests withNANOARROW_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