Skip to content

[python][ray] Incrementally commit update_by_row_id file groups#8826

Open
XiaoHongbo-Hope wants to merge 25 commits into
apache:masterfrom
XiaoHongbo-Hope:update_by_row_id_incremental_commit
Open

[python][ray] Incrementally commit update_by_row_id file groups#8826
XiaoHongbo-Hope wants to merge 25 commits into
apache:masterfrom
XiaoHongbo-Hope:update_by_row_id_incremental_commit

Conversation

@XiaoHongbo-Hope

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

Copy link
Copy Markdown
Contributor

Purpose

For large update_by_row_id jobs, a single final commit makes late failures expensive because all uncommitted file groups must be rewritten.

Add optional max_groups_per_commit windows while keeping the default single atomic commit. Incremental mode may retain earlier windows after a later failure.

The follow-up also bounds conflict planning by row range, keeps explicit REST rejections deterministic, and releases later uncommitted groups while draining failures.

Tests

  • Relevant Python regressions: 143 passed, 10 subtests.
  • Python 3.6 compatibility: 92 passed.
  • Flake8, py_compile, and diff checks passed.

@XiaoHongbo-Hope
XiaoHongbo-Hope marked this pull request as ready for review July 26, 2026 12:18
@XiaoHongbo-Hope
XiaoHongbo-Hope force-pushed the update_by_row_id_incremental_commit branch 2 times, most recently from 1187b47 to 9e0ef29 Compare July 26, 2026 12:43
@XiaoHongbo-Hope
XiaoHongbo-Hope force-pushed the update_by_row_id_incremental_commit branch from 9e0ef29 to 8612798 Compare July 26, 2026 12:52
for n in batch.column("n_updated").to_pylist():
iter_batches_kwargs = {"batch_format": "pyarrow"}
if on_group_result is not None:
# Yield each group immediately.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

batch_size=1 does not yield each group immediately; it only splits blocks after map_groups emits them. Ray buffers multiple group results within the same task, so if a later group fails, earlier completed groups never reach on_group_result and are not committed. This reproduces with num_partitions=1, three groups, and max_groups_per_commit=1: when the third group fails, the snapshot does not advance.

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.

batch_size=1 does not yield each group immediately; it only splits blocks after map_groups emits them. Ray buffers multiple group results within the same task, so if a later group fails, earlier completed groups never reach on_group_result and are not committed. This reproduces with num_partitions=1, three groups, and max_groups_per_commit=1: when the third group fails, the snapshot does not advance.

Fixed as of ac42026: _apply_group returns each group’s failure as data instead of
raising, so completed groups in the same task are still committed before the error is
surfaced.

@XiaoHongbo-Hope
XiaoHongbo-Hope marked this pull request as draft July 27, 2026 00:17
@XiaoHongbo-Hope
XiaoHongbo-Hope marked this pull request as ready for review July 27, 2026 08:13

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

Two findings from this review:

# No target file groups (e.g. an existing but empty snapshot). Match the
# 4-tuple contract so callers that unpack (msgs, num, row_ids, err) --
# including merge_into's NOT MATCHED insert path -- don't crash.
return [], 0, [], None

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.

Returning a 4-tuple fixes the unpacking crash, but it does not make an existing-but-empty snapshot follow the empty-target path. _build_datasets still passes target_empty=base_snapshot is None; for an empty snapshot, base_snapshot is non-null, so the NOT MATCHED insert path builds a left-anti join against a zero-block target. The new test fails on Python 3.10 and 3.11 with ArrowInvalid. Please also treat base_snapshot.total_record_count == 0 as empty, and skip the matched branches, so this regression passes the supported CI matrix.

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.

Returning a 4-tuple fixes the unpacking crash, but it does not make an existing-but-empty snapshot follow the empty-target path. _build_datasets still passes target_empty=base_snapshot is None; for an empty snapshot, base_snapshot is non-null, so the NOT MATCHED insert path builds a left-anti join against a zero-block target. The new test fails on Python 3.10 and 3.11 with ArrowInvalid. Please also treat base_snapshot.total_record_count == 0 as empty, and skip the matched branches, so this regression passes the supported CI matrix.

Fixed

exc_info=commit_exc,
)
clean_up_rejected_commit()
raise commit_exc

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.

Once this branch classifies the rejection as deterministic, the staged CommitMessage files are safe to abort. Rethrowing the raw exception loses that signal: TableCommit._commit only aborts on CommitConflictError, while merge_into and RayDataSink stop aborting after commit starts, so 400/403/404 rejections can leave data, changelog, and index files orphaned. Please propagate a safe-to-abort rejection type or marker to the layer that owns the messages, while preserving the original cause, and add a caller-level test showing that no manual abort() is required.

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.

Once this branch classifies the rejection as deterministic, the staged CommitMessage files are safe to abort. Rethrowing the raw exception loses that signal: TableCommit._commit only aborts on CommitConflictError, while merge_into and RayDataSink stop aborting after commit starts, so 400/403/404 rejections can leave data, changelog, and index files orphaned. Please propagate a safe-to-abort rejection type or marker to the layer that owns the messages, while preserving the original cause, and add a caller-level test showing that no manual abort() is required.

Fixed

@XiaoHongbo-Hope
XiaoHongbo-Hope marked this pull request as draft July 27, 2026 09:38
@XiaoHongbo-Hope
XiaoHongbo-Hope marked this pull request as ready for review July 27, 2026 09:46
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.

3 participants