laichunpongben opened a new issue, #3286:
URL: https://github.com/apache/iceberg-python/issues/3286

   ## Summary
   
   `Table.add_files(file_paths, check_duplicate_files=True)` currently 
materializes every DataFile in the current snapshot via `inspect.data_files()` 
(full pyarrow Table including `readable_metrics`, per-column lower/upper 
bounds, partition records) and filters on `file_path` in memory. Per-call cost 
is O(snapshot data-file count); across an incremental backfill where each call 
adds K files, cumulative cost is O(N²).
   
   Related but not the same:
   - #2132 — same problem shape, closed as a docs-only fix (#2249) recommending 
`check_duplicate_files=False`. The algorithm was not changed.
   - #2133 — parallelized the per-manifest fan-out and switched 
`inspect.files()` → `inspect.data_files()` (skips delete manifests). 
Constant-factor improvement only; per-call complexity unchanged.
   
   This issue tracks the underlying algorithmic gap.
   
   ## Repro
   
   Backfill a fresh table day-by-day, one `add_files` call per day, on the 
order of tens of thousands of parquet paths per call. Per-call wall-clock for 
the dup-check phase grows linearly with the cumulative file count of prior 
commits. After ~15 commits (~600k existing files), dup-check dominates — each 
new call takes ~10–15 minutes versus seconds during early commits.
   
   Setting `check_duplicate_files=False` eliminates the cost but also loses the 
idempotency guarantee that re-running a partial-failure resume is safe.
   
   ## Suggested fix
   
   Push the `file_paths` set into the manifest scan instead of materializing 
all DataFiles up front:
   
   1. Get `ManifestFile` references from the current snapshot's manifest list.
   2. Stream `ManifestEntry`s and short-circuit on `entry.data_file.file_path 
in candidate_set` (Python `set` containment is O(1)).
   3. Skip decoding partition records / per-column stats — they're not needed 
for path-equality.
   
   This makes dup-check streaming and roughly O(file_paths × 
pages_to_skim_until_match), independent of total snapshot size for negative 
cases and bounded by matching manifest size for positive cases.
   
   Optional follow-up: store `file_path` lower/upper bounds in `ManifestFile` 
so most manifests can be pruned without opening — requires an Iceberg spec 
extension.
   
   ## Java reference
   
   Spark's `add_files` action / `SnapshotProducer` does not pre-scan all data 
files for duplicate detection. Duplicate prevention there is predicate-based 
against the new paths only. The proposed pyiceberg fix would bring parity.
   
   ## Environment
   
   - pyiceberg 0.11.x (and HEAD as of this writing — 
`pyiceberg/table/__init__.py:add_files`)
   - `file_paths` is dispatched to 
`pyarrow.compute.field("file_path").isin(file_paths)` after a full 
materialization
   
   ## Willing to contribute
   
   Happy to send a PR if maintainers agree on the manifest-scan approach. 
Wanted to align on direction first since #2132 was closed as docs.
   


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


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to