Skip to content

[core][flink][spark][python][vector] Support vector indexes on DV tables - #8930

Merged
JingsongLi merged 5 commits into
apache:masterfrom
XiaoHongbo-Hope:feature/lumina-index-support-dv-tables
Aug 4, 2026
Merged

[core][flink][spark][python][vector] Support vector indexes on DV tables#8930
JingsongLi merged 5 commits into
apache:masterfrom
XiaoHongbo-Hope:feature/lumina-index-support-dv-tables

Conversation

@XiaoHongbo-Hope

@XiaoHongbo-Hope XiaoHongbo-Hope commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Purpose

Allow building vector global indexes on deletion-vector (DV) enabled tables. Generic global-index building previously rejected every index type on DV tables, even though the vector read paths can filter DV-deleted rows and safely build over the physical-row superset.

Changes

  • Add an opt-in GlobalIndexerFactory.supportsDeletionVectors() capability, defaulting to false.
  • Enable the capability for Lumina and all current paimon-vector native index types: ivf-flat, ivf-pq, ivf-sq, ivf-rq, and diskann.
  • Relax the Flink builder guard only for opted-in factories and thread the selected index type into the builder.
  • Pin the Spark vector reader to the snapshot used during planning.
  • Carry the planned snapshot through PyPaimon vector plans and use it for DV live-row filtering, scalar-index filtering, and raw fallback reads.
  • Refresh global-index deletions from the latest snapshot on every materialized-deletion compaction commit attempt. This removes indexes committed after compaction preparation and repeats the refresh after optimistic-commit conflicts.

Related merged fixes

This PR completes snapshot pinning for Spark and PyPaimon and closes the remaining preparation-to-commit race.

Tests

  • Lumina, its legacy identifier, and all native vector index types are allowed on DV-enabled tables.
  • Non-opt-in index types remain rejected on DV-enabled tables and continue to work without DV.
  • Spark and PyPaimon readers retain and propagate the planned snapshot through live-row, scalar-index, and raw-read paths.
  • A global index committed after materialized-compaction preparation is deleted when that compaction commits.

@XiaoHongbo-Hope
XiaoHongbo-Hope force-pushed the feature/lumina-index-support-dv-tables branch 3 times, most recently from ef31d32 to 5b03c02 Compare July 30, 2026 13:59
@XiaoHongbo-Hope
XiaoHongbo-Hope marked this pull request as draft July 30, 2026 14:08
@XiaoHongbo-Hope
XiaoHongbo-Hope force-pushed the feature/lumina-index-support-dv-tables branch from 5b03c02 to 5cb2e47 Compare July 30, 2026 14:13
@XiaoHongbo-Hope XiaoHongbo-Hope changed the title [core][flink] Support building Lumina global index on deletion-vector tables [core][flink][lumina] Allow building Lumina global index on DV-enabled tables Jul 30, 2026
@XiaoHongbo-Hope
XiaoHongbo-Hope marked this pull request as ready for review August 1, 2026 09:52
@JingsongLi

Copy link
Copy Markdown
Contributor

Thanks for working on this. I think this PR needs two changes before merge:

  1. Please complete snapshot pinning for every reader before advertising deletion-vector support. supportsDeletionVectors() is a factory-wide capability, but snapshot pinning is currently incomplete. The core and Flink readers assign planSnapshot = plan.snapshot(), while SparkDataEvolutionVectorRead#read overrides the method without doing so. PyPaimon's VectorSearchScanPlan also only carries splits, and its live-row filter creates a new scan against the latest snapshot. If materialized-deletion compaction commits between planning and reading, an old index plan can be combined with the new row-id/DV state, producing missing or incorrect top-K results. Please thread the planned snapshot through Spark and PyPaimon as well, including the live-row/scalar filters, and add regression coverage for this race.

  2. This PR should also support paimon-vector. paimon-vector is the more important/current native vector-index implementation; Lumina is only the legacy index. NativeVectorGlobalIndexReader already honors includeRowIds for both single and batch search, so NativeVectorGlobalIndexerFactory should opt in to deletion-vector support after the snapshot-pinning issue above is fixed, with tests covering the native index types on DV tables. Limiting this change to Lumina leaves the primary implementation unsupported and makes the feature incomplete.

I consider both items part of this PR rather than follow-ups. The FileStoreCommitImpl refresh still makes sense because it closes the preparation-to-commit/retry race.

@XiaoHongbo-Hope
XiaoHongbo-Hope force-pushed the feature/lumina-index-support-dv-tables branch 2 times, most recently from 9ea4129 to 3b1a6ab Compare August 3, 2026 11:46
Allow deletion-vector tables to build vector global indexes whose readers can filter deleted row IDs. Opt in both Lumina and every paimon-vector native index factory while keeping other generic index types rejected.\n\nPin Spark and PyPaimon vector reads to the snapshot used for planning. Thread that snapshot through live-row filtering, scalar-index filtering, and raw fallback reads so materialized-deletion compaction cannot mix old index plans with new row-id or DV state.\n\nRefresh global-index deletions on every materialized-compaction commit attempt so indexes committed after preparation are still removed.
@XiaoHongbo-Hope
XiaoHongbo-Hope force-pushed the feature/lumina-index-support-dv-tables branch from 3b1a6ab to 6363ee8 Compare August 3, 2026 12:05
@XiaoHongbo-Hope XiaoHongbo-Hope changed the title [core][flink][lumina] Allow building Lumina global index on DV-enabled tables [core][flink][spark][python][vector] Support vector indexes on DV tables Aug 3, 2026
@XiaoHongbo-Hope

Copy link
Copy Markdown
Contributor Author

Thanks for working on this. I think this PR needs two changes before merge:

  1. Please complete snapshot pinning for every reader before advertising deletion-vector support. supportsDeletionVectors() is a factory-wide capability, but snapshot pinning is currently incomplete. The core and Flink readers assign planSnapshot = plan.snapshot(), while SparkDataEvolutionVectorRead#read overrides the method without doing so. PyPaimon's VectorSearchScanPlan also only carries splits, and its live-row filter creates a new scan against the latest snapshot. If materialized-deletion compaction commits between planning and reading, an old index plan can be combined with the new row-id/DV state, producing missing or incorrect top-K results. Please thread the planned snapshot through Spark and PyPaimon as well, including the live-row/scalar filters, and add regression coverage for this race.
  2. This PR should also support paimon-vector. paimon-vector is the more important/current native vector-index implementation; Lumina is only the legacy index. NativeVectorGlobalIndexReader already honors includeRowIds for both single and batch search, so NativeVectorGlobalIndexerFactory should opt in to deletion-vector support after the snapshot-pinning issue above is fixed, with tests covering the native index types on DV tables. Limiting this change to Lumina leaves the primary implementation unsupported and makes the feature incomplete.

I consider both items part of this PR rather than follow-ups. The FileStoreCommitImpl refresh still makes sense because it closes the preparation-to-commit/retry race.

Thanks for the detailed review. Both requested changes are addressed in 6363ee8:

/**
* Whether the read path filters DV-deleted rows, so building over all physical rows is safe.
*/
default boolean supportsDeletionVectors() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this, we have support all vector index.

@JingsongLi JingsongLi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found one PyPaimon snapshot-lifecycle correctness issue that should be addressed before merge.


def read_plan(self, plan):
# type: (VectorSearchScanPlan) -> GlobalIndexResult
self._plan_snapshot = plan.snapshot()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Storing the plan snapshot on the reader makes the result depend on previous calls. read() remains public and does not clear _plan_snapshot, so reusing a reader after read_plan(plan_at_S1) and then calling read(splits_planned_at_S2) makes the S2 live-row, scalar-index, and raw-fallback paths read from S1. The mutable field is also unsafe for concurrent reads, and the batch path has the same issue. After DV materialization this can return deleted rows or omit valid top-K results. Could we keep the reader stateless and pass the snapshot explicitly for each invocation (for example, _read(splits, snapshot), with read_plan forwarding plan.snapshot()), instead of caching it on self?

@JingsongLi JingsongLi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The latest update exposes one additional DV snapshot-consistency issue outside the vector readers.

+ "deleted rows to be indexed.",
table.name());

scanSnapshot = table.snapshotManager().latestSnapshot();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing the DV guard here enables every generic index type, including full-text and es-index, but those read paths are not snapshot-consistent yet. FullTextRead.read(Plan) discards the plan context, and DataEvolutionFullTextRead computes live rows from the latest snapshot. If materialized-DV compaction commits between planning at S1 and reading at S2, the S1 index row IDs are intersected with S2 live rows, which can produce missing or empty top-K results. Please keep non-vector indexes gated (or restore an explicit vector-capability check) until full-text plans carry their snapshot and both live-row and raw fallback reads are pinned to it. A plan -> materialized compaction -> read regression test would cover this race.

@JingsongLi JingsongLi Aug 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let‘s add support to full-text too.

@JingsongLi

Copy link
Copy Markdown
Contributor

+1

@JingsongLi
JingsongLi merged commit de71827 into apache:master Aug 4, 2026
18 checks passed
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