taoran92 opened a new issue, #10595:
URL: https://github.com/apache/paimon/issues/10595

   ## Search before asking
   
   - [x] I searched in the issues and found nothing similar.
   
   ## Motivation
   
   A customer's workload uses predicates such as `__paimon_bucket IN (0, 1)`
   to query selected physical buckets through Spark without enumerating
   business keys. This is useful for exploratory analysis, testing
   transformations on a subset, and investigating particular buckets; it
   does not require statistically representative sampling.
   
   In the baseline below, Spark returns correct results but scans unrelated
   buckets before applying the metadata filter. Translating this condition
   into a Core bucket filter would avoid those data-file reads. PR1 adds
   an optimization to the Core filtering path; Spark integration follows
   separately.
   
   ### Spark baseline reproduction
   
   The original local reproduction used Paimon 2.2-SNAPSHOT at commit
   `937b5e1d51a5d346634df56794c562150aa1a2b5`, Spark 3.5.9,
   Scala 2.12 and Java 11.
   
   With a disposable Paimon catalog/database and the Paimon Spark extensions
   enabled:
   
   ```sql
   CREATE TABLE bucket_filter_repro (id INT, payload STRING)
   USING paimon
   TBLPROPERTIES (
     'bucket' = '4',
     'bucket-key' = 'id',
     'file.format' = 'parquet'
   );
   
   INSERT INTO bucket_filter_repro
   SELECT CAST(id AS INT), concat('v', CAST(id AS STRING))
   FROM range(400);
   
   SELECT id, payload, __paimon_bucket
   FROM bucket_filter_repro;
   
   SELECT id, payload, __paimon_bucket
   FROM bucket_filter_repro WHERE __paimon_bucket = 1;
   
   SELECT id, payload, __paimon_bucket
   FROM bucket_filter_repro WHERE __paimon_bucket = 99;
   
   SELECT id, payload, __paimon_bucket
   FROM bucket_filter_repro WHERE id = 1;
   ```
   
   The fixture produced four data files:
   
   | Predicate | Planned buckets | Planned files | BatchScan output rows | 
Final result rows |
   | --- | --- | ---: | ---: | ---: |
   | None | 0, 1, 2, 3 | 4 | 400 | 400 |
   | `__paimon_bucket = 1` | 0, 1, 2, 3 | 4 | 400 | 95 |
   | `__paimon_bucket = 99` | 0, 1, 2, 3 | 4 | 400 | 0 |
   | `id = 1` | 2 | 1 | 100 | 1 |
   
   Planned files were inspected through scan input splits. Scan output rows
   were measured using BatchScan's `numOutputRows`. The `id = 1` query
   demonstrates existing bucket-key pruning.
   
   These are synthetic baseline results, not customer production measurements.
   Exact file counts depend on the write configuration.
   
   ## Solution
   
   ### PR1 — Core physical bucket predicates and manifest pruning
   
   Core already supports physical bucket filtering through
   `ReadBuilder.withBucketFilter(Filter<Integer>)`. Ordinary filters exclude
   data files using manifest entries or sidecar bucket indexes. Existing
   `withBucket(int)` and supported bucket-key selectors can also prune
   manifests using bucket-range statistics. PR1 extends the existing
   [manifest bucket pruning](https://github.com/apache/paimon/pull/9791)
   to structured physical bucket predicates.
   
   PR1 introduces an immutable, serializable `PhysicalBucketPredicate` through
   the same read API. It supports comparisons, membership, and set operations
   through `intersect()`, `union()`, and `negate()`. These operations preserve
   manifest-range pruning and serializability; the inherited `Filter.and()`
   does not.
   
   The planner compares the predicate with each manifest's `minBucket` and
   `maxBucket`, already recorded in the manifest list. It can reject disjoint
   manifests before opening their contents or sidecars. Retained manifests
   continue through existing sidecar and entry-level filtering. A sidecar is
   a separate companion index describing blocks within a manifest.
   
   The implementation preserves existing filters and constraints, supports
   historical physical bucket IDs independently of the current bucket count,
   and retains manifests with missing or invalid range statistics. No
   storage-format changes or mandatory manifest rewrites are required.
   
   ### Benefits
   
   PR1 reduces metadata reads for multi-bucket, range and composed physical
   predicates while selecting the same data files as an equivalent ordinary
   bucket filter. Disjoint manifests can be skipped before opening their
   contents or sidecars.
   
   The benefit depends on how many manifest ranges are disjoint from the
   requested buckets. Broadly overlapping or unavailable ranges limit it;
   snapshot and manifest-list access is still required.
   
   ### Follow-up — Spark integration
   
   PR2 will translate supported `__paimon_bucket` conditions into Core scan
   filters and preserve Spark SQL semantics, including NULL handling.
   PR1 alone does not change Spark SQL scan behavior; the Spark integration
   must be completed to deliver the overall feature.
   
   ## Validation
   
   A separate synthetic Core fixture contains four manifests, each with one
   bucket, one data file and two rows. A supplementary local probe selected
   buckets `IN (1, 3)` and measured these cold-cache input-stream open counts:
   
   | Bucket filter | Sidecar enabled | Manifest opens | Sidecar opens |
   | --- | --- | ---: | ---: |
   | Ordinary bucket filter | No | 4 | 0 |
   | Physical bucket predicate | No | 2 | 0 |
   | Ordinary bucket filter | Yes | 2 | 4 |
   | Physical bucket predicate | Yes | 2 | 2 |
   
   All four cases plan two data files and return the same four rows; the
   unfiltered scan plans four files. The same counts were verified for the
   range `1 <= bucket <= 2`. Both selections were checked on append and
   primary-key tables, covering 16 scan cases across filter forms and
   sidecar settings.
   
   These counters measure `FileIO.newInputStream` calls for manifests and
   sidecars, not total metadata I/O, bytes read or elapsed-time speedup.
   They validate Core pruning independently of Spark SQL pushdown.
   
   The reviewed PR1 commit (`8aed1ea5d`) also passed 34 targeted Core tests,
   covering predicate boundaries and serialization, filter compatibility,
   sidecars and caches, primary-key merge reads, deleted/replaced files,
   and historical bucket counts. The supplementary probe reused that test
   fixture and was run separately from the committed test suite.
   
   ## Are you willing to submit a PR?
   
   - [x] I'm willing to submit a PR!
   


-- 
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