Skip to content

Flink: ConvertEqualityDeletes: Fix index consistency after a failure or restore - #18674

Merged
mxm merged 10 commits into
apache:mainfrom
mxm:resolve-equality-deletes.index-recovery
Nov 3, 2026
Merged

mxm merged 10 commits into
apache:mainfrom
mxm:resolve-equality-deletes.index-recovery

Conversation

@mxm

@mxm mxm commented Oct 28, 2026

Copy link
Copy Markdown
Contributor

Summary

To avoid rebuilding the index for every trigger, the planner has logic to reuse the
PK index between triggers. This PR fixes cases where the index can be incomplete, even
though the planner would consider the index fresh.

These cases are:

  1. An index update fails on a trigger with nothing to convert. There are no retries,
    because only triggers that convert a staging snapshot are planned again.
  2. A job restores from a checkpoint taken before a conversion commit. The index
    does not have the rows of that commit, and commits by other writers before it
    are missed.
  3. A rebuild fails half way in the planner and is still marked as done.
  4. A conversion fails while the target branch has no snapshot yet. The retry
    resolves its equality deletes against the rows the failed attempt indexed.

Constraints

All of these issues stem from two constraints of ConvertEqualityDeletes:

A. Conversion commits are made independently of the Flink checkpointing interval
B. Any failures in the conversion process must not fail the Flink job

Changes in this PR

Now the planner checks the target branch before it uses the index. The index is
used only if the head of the target branch is the snapshot the index was built
at, or the conversion commit of the last planned staging snapshot directly on
top of it. In every other case the planner rebuilds the index.

A rebuild forgets the indexed snapshot first and records the new one only after
it has emitted all read commands. A rebuild that fails half way is therefore
repeated, also after a restore. While the target branch has no snapshot, every
trigger starts a new index generation, a counter that goes up with each
rebuild. The workers drop the rows indexed so far when the generation changes.

If an index update fails in a reader or worker, the committer keeps the failed
generation in its checkpointed state and does not commit a conversion on it.
The next trigger plans the same staging snapshot again on a rebuilt index. On a
trigger with nothing to convert, this wastes one conversion unless the index is
rebuilt in between. We accept that, because we do not want to fail the job.


Developed with AI assistance. Every change was fully reviewed.

…or restore

The PK index workers keep the row positions of the target branch, and the
planner reuses that index between triggers. In some edge cases, the index can
be incomplete, but the planner still uses it.

These cases are:

- An index update fails on a trigger with nothing to convert. There are no retries,
  because only triggers that convert a staging snapshot are planned again.
- A job restores from a checkpoint taken before a conversion commit. The index
  does not have the rows of that commit, and commits by other writers before it
  are missed.
- A rebuild fails half way in the planner and is still marked as done.
- A conversion fails while the target branch has no snapshot yet. The retry
  resolves its equality deletes against the rows the failed attempt indexed.

Fix:

Now the planner checks the target branch before it uses the index. The index is
used only if the head of the target branch is the snapshot the index was built
at, or the conversion commit of the last planned staging snapshot directly on
top of it. In every other case the planner rebuilds the index.

A rebuild forgets the indexed snapshot first and records the new one only after
it has emitted all read commands. A rebuild that fails half way is therefore
repeated, also after a restore. While the target branch has no snapshot, every
trigger starts a new index generation, a counter that goes up with each
rebuild. The workers drop the rows indexed so far when the generation changes.

If an index update fails in a reader or worker, the committer keeps the failed
generation in its checkpointed state and does not commit a conversion on it.
The next trigger plans the same staging snapshot again on a rebuilt index. On a
trigger with nothing to convert, this wastes one conversion unless the index is
rebuilt in between. We accept that, because we do not want to fail the job.

@Guosmilesmile Guosmilesmile 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.

LGTM


if (planResult.indexGeneration() == failedIndexGeneration) {
deleteUncommittedDVs();
throw new IllegalStateException(

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.

Do we really want to handle this as an error?
Maybe better handled as a noop?
Or even better add a metrics?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Agreed. I opted for a mere warn message.

We already have metrics for errors and the error will be logged where it occurs.

Comment thread docs/docs/flink-maintenance.md Resolved
Comment thread docs/docs/flink-maintenance.md Resolved
Comment thread docs/docs/flink-maintenance.md Resolved
@mxm
mxm merged commit 866bca5 into apache:main Nov 3, 2026
28 checks passed
@mxm
mxm deleted the resolve-equality-deletes.index-recovery branch November 3, 2026 15:28
@mxm

mxm commented Nov 3, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the reviews @Guosmilesmile and @pvary! Here is the backport: #18777

mxm added a commit that referenced this pull request Nov 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants