Skip to content

fix(upsert): bound file match filter to avoid segfault#3420

Open
abnobdoss wants to merge 1 commit into
apache:mainfrom
abnobdoss:fix/upsert-file-match-filter
Open

fix(upsert): bound file match filter to avoid segfault#3420
abnobdoss wants to merge 1 commit into
apache:mainfrom
abnobdoss:fix/upsert-file-match-filter

Conversation

@abnobdoss

@abnobdoss abnobdoss commented May 27, 2026

Copy link
Copy Markdown
Contributor

Rationale for this change

Upserts with large composite (multi-column) keys segfault during scan planning. The initial scan builds one And(EqualTo, EqualTo) per unique source key and Ors them into a deep tree, which PyArrow's C++ engine recurses over in GetFragmentsCanonicalize, overflowing the C stack (SIGSEGV) at tens of thousands of keys.

This PR uses a bounded create_file_match_filter for the initial file-pruning scan: per-column min/max bounds (plus IsNull where needed), so predicate size scales with the number of join columns, not source rows. The exact create_match_filter is still used downstream for overwrite and insert filtering, so row-level correctness is unchanged.

The bounds are loose by design. With composite keys they can pull in false positives, but exact matching drops those before anything is written, so the only cost is scanning a few extra files in exchange for upserts that no longer crash. A follow-up could tighten this, for example per-prefix bounds or a grouped In, as long as the predicate size stays bounded.

Benchmarks

Timings cover expression construction, schema binding, PyArrow conversion, and applying the resulting Arrow filter to an in-memory table. They do not measure end-to-end upsert time, which also depends on table layout, file pruning, IO, and downstream exact matching. Benchmark script.

Before, exact match-filter path:

Unique source keys 1 join column 2 join columns 3 join columns
100 1.5 ms 7.3 ms 11 ms
1k 4.7 ms 87 ms 147 ms
10k 33 ms SIGSEGV SIGSEGV
100k 650 ms SIGSEGV SIGSEGV
1m 6.59 s >30 s >30 s

After, bounded file-match filter path:

Unique source keys 1 join column 2 join columns 3 join columns
100 1.0 ms 1.1 ms 1.2 ms
1k 1.0 ms 1.1 ms 1.2 ms
10k 1.1 ms 1.1 ms 1.3 ms
100k 1.3 ms 1.6 ms 1.9 ms
1m 3.7 ms 6.8 ms 9.0 ms

Related

Are these changes tested?

Yes. Added coverage for bounded-filter construction, the false-positive culling path, and a large composite-key regression test. The regression test runs the upsert in a subprocess, so a runtime that would segfault reports a pytest failure instead of killing the worker: the base branch exits 139 under Python 3.10, and this branch exits 0.

Ran:

  • uv run python -m pytest tests/table/test_upsert.py -k "large_composite_key_initial_scan or file_match_filter or create_match_filter"
  • uv run python -m pytest tests/table/test_upsert.py
  • commit hooks

Are there any user-facing changes?

No API changes. Large composite-key upserts no longer crash during initial scan planning, and may scan a conservative superset of candidate files before exact row matching.

@github-actions

Copy link
Copy Markdown

This pull request has been marked as stale due to 30 days of inactivity. It will be closed in 1 week if no further activity occurs. If you think that's incorrect or this pull request requires a review, please simply write any comment. If closed, you can revive the PR at any time and @mention a reviewer or discuss it on the dev@iceberg.apache.org list. Thank you for your contributions.

@github-actions github-actions Bot added the stale label Jun 27, 2026
@abnobdoss

abnobdoss commented Jun 27, 2026

Copy link
Copy Markdown
Contributor Author

Still active - happy to address any feedback.

@github-actions github-actions Bot removed the stale label Jun 28, 2026
@ericyuanhui

Copy link
Copy Markdown

Are there any updates on this? Has the issue been resolved? I noticed there are two PRs, but neither has been merged yet. Could someone provide some clarification on the current status?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants