Skip to content

[SPARK-57635][SQL] Make Declarative Pipelines run-termination reason deterministic - #56698

Closed
LuciferYang wants to merge 2 commits into
apache:masterfrom
LuciferYang:sdp-deterministic-termination-reason
Closed

[SPARK-57635][SQL] Make Declarative Pipelines run-termination reason deterministic#56698
LuciferYang wants to merge 2 commits into
apache:masterfrom
LuciferYang:sdp-deterministic-termination-reason

Conversation

@LuciferYang

Copy link
Copy Markdown
Contributor

What changes were proposed in this pull request?

TriggeredGraphExecution.getRunTerminationReason decided which failed flow's reason to report by calling collectFirst over failureTracker, a ConcurrentHashMap whose iteration order is unspecified. When more than one flow exhausts its retries (a non-retryable StopFlowExecution), the flow whose reason gets surfaced therefore varied from run to run.

This extracts a small pure helper, chooseRunTerminationReason, that considers only the stopped flows and picks the earliest one by (lastFailTimestamp, flowName), so the reported reason is stable across otherwise-identical runs. getRunTerminationReason now calls it and falls back to UnexpectedRunFailure(). The previous code also computed graphForExecution.flow(...) and lastException only to discard them; those are dropped.

Why are the changes needed?

Two runs that fail the same way could report different termination reasons (different flow name and cause), which is confusing in logs and events and makes the outcome non-reproducible.

Does this PR introduce any user-facing change?

No. The reported reason was already one of the failing flows; it is now chosen deterministically.

How was this patch tested?

Added unit tests for chooseRunTerminationReason in TriggeredGraphExecutionSuite: the earliest failure wins regardless of iteration order, ties are broken by flow name, and retryable failures are ignored. They fail against the previous order-dependent selection and pass with this change.

Was this patch authored or co-authored using generative AI tooling?

Generated-by: Claude Code (Claude Opus 4.8)

…deterministic

getRunTerminationReason picked a non-retryable flow failure via collectFirst over
failureTracker, a ConcurrentHashMap with unspecified iteration order, so which
flow's reason was surfaced varied across otherwise-identical runs. Choose the
earliest failure (ties broken by flow name) via a small pure helper, and add unit
tests for it. Also drops the dead graphForExecution.flow/lastException lookups.
…rcise

The three tests call chooseRunTerminationReason directly, so name them after it
rather than getRunTerminationReason.
@LuciferYang
LuciferYang requested a review from uros-b June 29, 2026 06:15

@dongjoon-hyun dongjoon-hyun left a comment

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.

+1, LGTM.

LuciferYang added a commit that referenced this pull request Jul 27, 2026
…deterministic

### What changes were proposed in this pull request?
`TriggeredGraphExecution.getRunTerminationReason` decided which failed flow's reason to report by calling `collectFirst` over `failureTracker`, a `ConcurrentHashMap` whose iteration order is unspecified. When more than one flow exhausts its retries (a non-retryable `StopFlowExecution`), the flow whose reason gets surfaced therefore varied from run to run.

This extracts a small pure helper, `chooseRunTerminationReason`, that considers only the stopped flows and picks the earliest one by `(lastFailTimestamp, flowName)`, so the reported reason is stable across otherwise-identical runs. `getRunTerminationReason` now calls it and falls back to `UnexpectedRunFailure()`. The previous code also computed `graphForExecution.flow(...)` and `lastException` only to discard them; those are dropped.

### Why are the changes needed?
Two runs that fail the same way could report different termination reasons (different flow name and cause), which is confusing in logs and events and makes the outcome non-reproducible.

### Does this PR introduce _any_ user-facing change?
No. The reported reason was already one of the failing flows; it is now chosen deterministically.

### How was this patch tested?
Added unit tests for `chooseRunTerminationReason` in `TriggeredGraphExecutionSuite`: the earliest failure wins regardless of iteration order, ties are broken by flow name, and retryable failures are ignored. They fail against the previous order-dependent selection and pass with this change.

### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Claude Opus 4.8)

Closes #56698 from LuciferYang/sdp-deterministic-termination-reason.

Authored-by: YangJie <yangjie01@baidu.com>
Signed-off-by: yangjie01 <yangjie01@baidu.com>
(cherry picked from commit effd582)
Signed-off-by: yangjie01 <yangjie01@baidu.com>
LuciferYang added a commit that referenced this pull request Jul 27, 2026
…deterministic

### What changes were proposed in this pull request?
`TriggeredGraphExecution.getRunTerminationReason` decided which failed flow's reason to report by calling `collectFirst` over `failureTracker`, a `ConcurrentHashMap` whose iteration order is unspecified. When more than one flow exhausts its retries (a non-retryable `StopFlowExecution`), the flow whose reason gets surfaced therefore varied from run to run.

This extracts a small pure helper, `chooseRunTerminationReason`, that considers only the stopped flows and picks the earliest one by `(lastFailTimestamp, flowName)`, so the reported reason is stable across otherwise-identical runs. `getRunTerminationReason` now calls it and falls back to `UnexpectedRunFailure()`. The previous code also computed `graphForExecution.flow(...)` and `lastException` only to discard them; those are dropped.

### Why are the changes needed?
Two runs that fail the same way could report different termination reasons (different flow name and cause), which is confusing in logs and events and makes the outcome non-reproducible.

### Does this PR introduce _any_ user-facing change?
No. The reported reason was already one of the failing flows; it is now chosen deterministically.

### How was this patch tested?
Added unit tests for `chooseRunTerminationReason` in `TriggeredGraphExecutionSuite`: the earliest failure wins regardless of iteration order, ties are broken by flow name, and retryable failures are ignored. They fail against the previous order-dependent selection and pass with this change.

### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Claude Opus 4.8)

Closes #56698 from LuciferYang/sdp-deterministic-termination-reason.

Authored-by: YangJie <yangjie01@baidu.com>
Signed-off-by: yangjie01 <yangjie01@baidu.com>
(cherry picked from commit effd582)
Signed-off-by: yangjie01 <yangjie01@baidu.com>
@LuciferYang

Copy link
Copy Markdown
Contributor Author

Merge Summary:

Posted by merge_spark_pr.py

LuciferYang added a commit that referenced this pull request Jul 27, 2026
…deterministic

### What changes were proposed in this pull request?
`TriggeredGraphExecution.getRunTerminationReason` decided which failed flow's reason to report by calling `collectFirst` over `failureTracker`, a `ConcurrentHashMap` whose iteration order is unspecified. When more than one flow exhausts its retries (a non-retryable `StopFlowExecution`), the flow whose reason gets surfaced therefore varied from run to run.

This extracts a small pure helper, `chooseRunTerminationReason`, that considers only the stopped flows and picks the earliest one by `(lastFailTimestamp, flowName)`, so the reported reason is stable across otherwise-identical runs. `getRunTerminationReason` now calls it and falls back to `UnexpectedRunFailure()`. The previous code also computed `graphForExecution.flow(...)` and `lastException` only to discard them; those are dropped.

### Why are the changes needed?
Two runs that fail the same way could report different termination reasons (different flow name and cause), which is confusing in logs and events and makes the outcome non-reproducible.

### Does this PR introduce _any_ user-facing change?
No. The reported reason was already one of the failing flows; it is now chosen deterministically.

### How was this patch tested?
Added unit tests for `chooseRunTerminationReason` in `TriggeredGraphExecutionSuite`: the earliest failure wins regardless of iteration order, ties are broken by flow name, and retryable failures are ignored. They fail against the previous order-dependent selection and pass with this change.

### Was this patch authored or co-authored using generative AI tooling?
Generated-by: Claude Code (Claude Opus 4.8)

Closes #56698 from LuciferYang/sdp-deterministic-termination-reason.

Authored-by: YangJie <yangjie01@baidu.com>
Signed-off-by: yangjie01 <yangjie01@baidu.com>
(cherry picked from commit effd582)
Signed-off-by: yangjie01 <yangjie01@baidu.com>
@LuciferYang

Copy link
Copy Markdown
Contributor Author

Merge Summary:

Posted by merge_spark_pr.py

@LuciferYang

Copy link
Copy Markdown
Contributor Author

Thank you @dongjoon-hyun @uros-b

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