[core] Add manifest sidecars for partition, row-id and bucket pruning - #9743
[core] Add manifest sidecars for partition, row-id and bucket pruning#9743leaves12138 wants to merge 13 commits into
Conversation
Add optional bounded block indexes and Java/PyPaimon pruning. Publish explicit index references in manifest metadata and preserve them through serialization, rewrites, commit cleanup, snapshot retention, and orphan collection.
Use bounded 1 MiB read requests in Java and Python to avoid object-store request amplification. Merge adjacent selected blocks into spans and buffer Java reads independently of the Avro consumer read size. Add regression tests for request counts, skipped gaps, short reads, size budgets, stream closure and truncated inputs.
|
I suggest using a single manifest index sidecar organized by Avro block. Partition information would support partition predicate pushdown during planning. Since both partition and row-id information describe the same blocks, they can live in the same block record and share its physical location. A possible layout is: The partition dictionary is shared across the file and can reuse the existing manifest partition encoding, preserving full tuples, types and nulls. Each block only stores dictionary IDs. The block ID is implicit in its position; The two indexes should remain independently usable within each block:
During planning, evaluate the partition predicate against the dictionary once, then check each block's partition IDs and row-id intervals. For conjunctive filters, intersect their candidate block sets. Read the selected blocks and retain the existing entry filtering and ADD/DELETE merge, since block-level matches do not guarantee that the same entry satisfies both predicates. This layout assumes reading the whole sidecar, as the current implementation does. A partition-only query would also read the row-id index bytes. I would start with this simpler layout and consider separate physical sections if measurements show that selective index reads materially improve planning time. |
Forward selected_blocks through the append-only reader test wrapper. Fix the manifest target size and assert explicit retained and expired manifest sets so snapshot and tag retention coverage does not depend on randomized file sizes.
|
Here is a refined version of the block-oriented layout, keeping the file-level partition dictionary and making each block's two payloads independently extensible. The dictionary stores each complete partition tuple once, using the existing manifest partition serialization. This preserves tuple values and nulls; the scan's existing The encoding bytes identify how to decode the corresponding payload, with separate ID namespaces for partition and row-id payloads. They replace the availability flags:
The container's integers and the encoding-1 payload integers use fixed-width big-endian representation; partition bytes retain their existing serialization. Encoding bytes are interpreted as unsigned IDs. Each payload length counts only its payload bytes, excluding the encoding and length fields. Other nonzero encoding IDs are reserved for future representations. If a reader does not recognize one, it skips exactly that payload length and treats that dimension as unavailable, while still being able to use the other dimension. Lengths must be bounded and validated. The outer For example, There are several important correctness and budget rules:
For conjunctive partition and row-id filters, select each block using: Only an empty candidate block set permits skipping the manifest. Selected blocks still pass through the existing entry filtering and ADD/DELETE merge. This keeps one sidecar and one record per block. It still assumes a bounded whole-sidecar read: payload lengths allow skipping decoding and unknown encodings, but do not by themselves save storage I/O. Index size, block selectivity and planning latency should determine whether selective physical reads are worthwhile later. |
Merge current master and adopt its manifest extra-files metadata instead of a dedicated index-file-name field. Discover row-id indexes through explicit suffixed references and preserve other extra files across reads and cleanup. Verify Java/Python compatibility, mixed extra-file references, retention and failed-commit cleanup. Java core: 151 tests passed. Python: 106 passed, 4 skipped. Random interval and byte checks: 18000 queries passed.
Propagate PyArrow cancellations and inspect chained and suppressed failures before falling back to full manifests. Preserve Java interruption state and fatal failures, guard against exception cycles, and cover stream open/read/close behavior with regression tests.
3508a27 to
914897f
Compare
|
We also need to consider adding |
OK, I will add |
Add a shared partition dictionary and independently framed partition and row-id payloads per block. Preserve complete directories while degrading optional coverage, support partition-only planning and unknown payload encodings, and retain version-2 reads and cancellation handling.
Record complete bucket and total-bucket pairs per block, use existing bucket filters for point lookups, and retain independent partition and row-id coverage. Preserve v2/v3 reads and treat missing, invalid or over-budget bucket coverage conservatively.
Reuse the first row-id interval in Java and Python selectors while preserving complete validation and fallback for malformed sidecars. Add boundary, empty-query, corruption and decode-count regression tests.
Purpose
Manifest-list statistics can retain large manifests even when only a few Avro blocks match partition, row-ID or bucket filters. Add one optional manifest sidecar consumed by Java and PyPaimon before normal entry filtering and ADD/DELETE reconciliation.
The container has a file-level dictionary of complete serialized partition tuples and a complete physical block directory. Each block records its offset, length and entry count, followed by independently usable partition, row-ID and bucket payloads. Entry ordinals are derived from preceding block counts.
(bucket, totalBuckets)pairs. Complete pairs preserve rescale semantics; missing, invalid, negative/synthetic or over-budget bucket metadata disables only this payload. No mutual-exclusion constraint is imposed on bucket and row-ID metadata.ManifestBucketFilterchecks and leaves arbitrary partition-dependent callbacks to entry filtering. PyPaimon reuses the existing total-aware early bucket filter._EXTRA_FILES, published only after close, and retained or collected with the owning manifest through rolling/rewrite, failed commits, snapshot/tag/changelog retention and orphan cleanup. Selected body spans are coalesced. Java selections covering all blocks reuse the full-manifest cache; partial selections bypass it. PyPaimon explain scans disable sidecar pruning to preserve complete entry counters.Sidecars use the
.avro.sidecarsuffix. Bothmanifest.sidecar.read/writeswitches default to false. Partition-only and bucket-only queries can use the same index.manifest.sidecar.max-partitions,manifest.sidecar.max-partition-bytes, andmanifest.sidecar.max-bucket-pairsbound the added metadata. Readers consume the complete bounded sidecar; no runtime metrics or immutable-manifest backfill are added.Tests
Java 8 with normal Maven checks: 100 tests passed.
Python: 82 tests passed.
Tests cover identical Java/Python fixtures, unavailable bucket payloads, mixed total-bucket counts, bucket-only scans, partition-only scans without row IDs, raw-copy regeneration, malformed payloads and independent fallback, unknown encodings, complete directories under tight budgets, randomized no-false-negative checks, row-ID boundaries, tuple nulls, full-manifest cache reuse, partial-read cache isolation, and explain counters with partition and row-ID filters. Apache RAT, flake8 on the index/test modules, and
git diff --checkalso passed.