kszucs opened a new pull request, #1469:
URL: https://github.com/apache/arrow-go/pull/1469

   ### Rationale for this change
   
   Content-defined chunking (CDC) makes the Parquet writer cut data pages at 
boundaries derived from the data instead of from a byte count. When a table is 
edited (rows inserted, deleted or updated) and written again, most data pages 
come out byte-identical to the previous version, so content-addressable storage 
such as Hugging Face Xet only has to store the pages that changed.
   
   Arrow C++/PyArrow (apache/arrow#45360) and arrow-rs (apache/arrow-rs#9450) 
already support it. This PR ports the same algorithm to arrow-go.
   
   ### What changes are included in this PR?
   
   - **Writer option:** `parquet.CDCOptions`, `parquet.DefaultCDCOptions()` 
(256 KiB min, 1 MiB max, norm level 0), `parquet.WithContentDefinedChunking` 
and `WriterProperties.ContentDefinedChunking()`. CDC is off by default.
   - **Chunker:** a new internal package, `parquet/internal/cdc`, ports the C++ 
`ContentDefinedChunker`. It keeps:
     - the gearhash rolling hash over (def level, rep level, value);
     - the same 8 gear tables and mask formula;
     - a cut after 8 mask matches, one per gear table, or at the max size.
   
     Nested columns are cut only at record boundaries. Fixed-width values are 
hashed as their little-endian bytes, so boundaries don't depend on the host's 
byte order. The gear tables are checked in as `gearhash_table.go`, generated by 
`gearhash_gen.go` through `go generate`.
   - **pqarrow integration:** `pqarrow.FileWriter` keeps one chunker per leaf 
column for the life of the file. Page boundaries therefore don't depend on how 
rows are split across row groups or across `Write`, `WriteBuffered`, 
`WriteTable` and `WriteColumnChunked`/`WriteColumnData` calls.
     - Each leaf write is sliced per chunk, zero-copy, and the current page is 
closed between chunks.
     - Invalid options make `NewFileWriter` return an error before anything is 
written to the sink.
   - **`file` package:** `ColumnChunkWriter` gains `FlushCurrentPage() error`, 
which every column writer already implements. `FlushCurrentPage` no longer 
writes an empty data page when nothing is buffered.
   - **Dictionary writes:** slices of one dictionary array share their 
dictionary data, so `pqarrow` now skips the dictionary comparison for them.
   
   Behaviour that differs from Arrow C++:
   - **State across row groups:** chunker state carries over across row groups. 
C++ does the same since GH-51684 (Arrow 27.0.0); earlier versions restart at 
every row group.
   - **Nested columns:** a boundary at the very start of a write call closes 
the page left open by the previous call. C++ drops that boundary, which makes 
nested pages depend on how the data was split across write calls.
   - **Dictionary fallback:** falling back from dictionary to plain encoding 
doesn't start a new page, because Go's fallback re-encodes the buffered page.
   - **Writer scope:** only `pqarrow.FileWriter` chunks. 
`pqarrow.WriteArrowToColumn` returns `arrow.ErrNotImplemented` when CDC is 
enabled, and the low-level `file` column writers ignore the option. C++ throws 
from `WriteBatch`.
   
   Page boundaries are not compared against files written by PyArrow or 
arrow-rs. The algorithm and gear tables are the same, but there are no 
cross-implementation fixtures.
   
   Of the ~5.3k added lines, about 2.1k are the generated gear table and about 
2.5k are tests.
   
   ### Are these changes tested?
   
   Yes.
   
   - **`parquet/internal/cdc`:**
     - The mask and option-validation tables and the gear-table values are 
taken from the C++ tests.
     - Chunk invariants are checked for required, nullable and nested columns, 
along with the forced cut at the max size and determinism.
     - Chunking a column in one call gives the same boundaries as chunking it 
across two calls, including when a boundary falls exactly at the split.
     - Type dispatch: dictionaries chunk like their indices, extensions like 
their storage, all binary-like types alike, and unsupported types return an 
error.
     - Hashing a fixed-width value word by word matches hashing its 
little-endian bytes.
     - `TestChunksStableBoundaries` pins exact boundaries, so a hashing change 
can't silently break deduplication between versions.
     - Invariant checks run in builds with `-tags assert`.
   - **`parquet/pqarrow`:** a port of the C++ CDC test suite, including its 
data generator and the `PagesFollowContent` page assertion.
     - **Single row group:** inserts, deletes, updates, prepend, append, an 
empty table and array offsets. The type matrix covers dictionary on and off and 
data page V1/V2.
     - **Multiple row groups:** edits across row groups.
     - **Independence:** pages don't depend on row-group boundaries, write 
calls, Arrow chunking, `WriteBuffered`, column-by-column writes or 
dictionary-typed input.
     - **Error paths:** invalid options, write errors, missing levels and 
`WriteArrowToColumn`.
     - Under `-race` the type matrix shrinks to the subset C++ runs under 
sanitizers.
   - **`parquet/file`:** a `FlushCurrentPage` call with nothing buffered writes 
no page.
   - **Benchmarks:** `BenchmarkWriteContentDefinedChunking` covers int64 and 
string columns with CDC off and on. `BenchmarkChunks` covers the chunker on its 
own.
   
   Local runs:
   - `go test -tags assert` and `-tags assert,noasm` over `./parquet/...`;
   - `-race` on the `cdc`, `pqarrow` and `file` packages;
   - a `-tags pqarrow_read_only` build;
   - `go vet` of the changed packages for 386 and s390x;
   - golangci-lint over `parquet`.
   
   ### Are there any user-facing changes?
   
   Yes. CDC is a new, experimental writer option:
   
   ```go
   props := parquet.NewWriterProperties(
        parquet.WithContentDefinedChunking(parquet.DefaultCDCOptions()),
   )
   fw, err := pqarrow.NewFileWriter(schema, w, props, 
pqarrow.DefaultWriterProps())
   ```
   
   **This PR includes breaking changes to public APIs:**
   - `file.ColumnChunkWriter` has a new method, `FlushCurrentPage() error`, so 
implementations outside this module must add it.
   - Calling `FlushCurrentPage` with nothing buffered no longer writes an empty 
data page.
   - `pqarrow.WriteArrowToColumn` returns an error when the column writer's 
properties enable CDC.
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to