DanielLeens commented on PR #11569:
URL: https://github.com/apache/seatunnel/pull/11569#issuecomment-5466087061
Note: this PR is authored by me (DanielLeens), so GitHub blocks a
self-review submission. Posting this as a plain PR comment instead of a formal
review, per project convention for self-authored PRs (same as my previous
rounds on this thread). Since `git diff` between my last comment and the
current head is not empty this time, this is a genuine incremental round, not a
repeat.
New activity since my last comment (`a9257e75bf1a`, then Issue 1 posted
against it): three new commits from @davidzollo — `5ec277d8be97`
("[Docs][Connector-V2] Clarify XA recovery cleanup risk"), `c5f4ec0a0d90`
("[Docs][Connector-V2] Clarify XA recovery concurrency"), and `d2bc272f3641`
("[Chore][Connector-V2] Refresh CI", current head) — plus a new reply from
@davidzollo on @dybyte's Q1 thread. I re-verified each of these directly
against source rather than taking the commit messages or the reply at face
value.
# What Problem Does This PR Solve?
Unchanged from prior rounds: before this patch, a permanent JDBC XA commit
failure could be silently swallowed (`throwIfAnyFailed("commit")` was disabled,
and `wrapException()` threw `TransientXaException` pre-wrapped inside
`JdbcConnectorException`, making the retryable-error branch unreachable). This
PR restores failure propagation, fixes retryable/permanent XA error
classification, bounds retries within one invocation, and reconciles restored
checkpoint XIDs against a live `xaFacade.recover()` scan instead of inferring
success from an absent XID alone.
# 1. Code Change Review
## 1.1 Core Logic Analysis
I pulled the three new commits as raw diffs from the GitHub API and
cross-checked them against the current head's full source for
`JdbcSinkAggregatedCommitter.java`, `XaGroupOpsImpl.java`,
`XaFacadeImplAutoLoad.java`, and `GroupXaOperationResult.java` rather than
trusting the commit subjects:
- **`5ec277d8be97`** is exactly the one-line migration-guide addition I
recommended as the "Best improvement" for my own Issue 1
(absence-from-recovery-scan cannot distinguish "committed by us" from "rolled
back externally"; only the gap/fail-closed case was previously disclosed). Both
`docs/en` and `docs/zh` `incompatible-changes.md` now state plainly that XA
recovery cannot tell a SeaTunnel-committed XID from one rolled back or removed
by an external actor, and that operators should not run external cleanup
against SeaTunnel-owned prepared branches while a job may still be restored,
without coordinating with recovery. This closes Issue 1 as I scoped it — a
documentation fix, not a code fix, since the underlying gap is
information-theoretic to XA itself, not an implementation defect.
- **`c5f4ec0a0d90`** tightens the `restoreCommit()` Javadoc to name the
external actor explicitly ("a DBA or RM cleanup process"), state plainly that
this is *not* a second Zeta committer, and say outright that the per-batch
recovery-scan refresh "narrows, but cannot eliminate, the gap before the
following XA commit call." I checked this against @davidzollo's reply on
@dybyte's Q1 thread ("Addressed in c5f4ec0a0d") and it is accurate — the new
Javadoc text matches exactly what his inline reply and my own earlier reply in
that same thread already committed to putting in writing. This is a
comment-only change; no executable logic moved.
- **`d2bc272f3641`** (current head) — verified via `gh api
repos/apache/seatunnel/commits/<sha>` that this commit has zero changed files.
It is a genuine empty CI-retrigger commit, not a disguised code change.
I independently re-verified the load-bearing invariants against the current
head's actual source rather than re-trusting my own prior rounds:
- `GroupXaOperationResult.hasNoFailures()` returns `false` if *either*
`failure` (permanent) or `transientFailure` (transient) is present;
`throwIfAnyFailed` only throws on the permanent `failure` field. Combined with
`XaGroupOpsImpl.commit()`'s loop guard `i.hasNext() && (result.hasNoFailures()
|| allowOutOfOrderCommits)` (both production call sites pass
`allowOutOfOrderCommits=false`), the loop stops at the first failure of any
kind and every un-iterated XID is appended to `forRetry` via the same iterator,
preserving order. This is the exact ordering invariant
`JdbcSinkAggregatedCommitter.findFirstRecoveredIndex`/`replayRecoveredCheckpoint`
(lines 207-239) depends on, and it holds in the source I read directly.
- `XaFacadeImplAutoLoad.wrapException` returns (does not throw) a bare
`TransientXaException` for `TRANSIENT_ERR_CODES = {XA_RETRY, XAER_RMFAIL}` and
a `JdbcConnectorException` for everything else, including `XAER_NOTA` and
`XA_RBTRANSIENT`; every one of `execute()`'s two throw sites and both
`Command.fromRunnable*` closures does `throw wrapException(...)`, so
`XaGroupOpsImpl.commit()`'s `catch (XaFacade.TransientXaException e)` is
genuinely reachable. This confirms the core claim of the PR (the pre-fix bug:
the exception used to arrive pre-wrapped inside `JdbcConnectorException`, so
that catch clause was dead code).
Both of these match the six prior rounds' conclusions; I re-derived them
from the current head's source rather than assuming they still hold, since
re-verifying invariants after every commit — even a docs-only one — is the
point of doing another pass at all.
## 1.2 Compatibility Impact
Unchanged: partially incompatible, and now more completely disclosed.
`5ec277d8be97` extends the existing migration-guide breaking-change entry (both
`en` and `zh`) to also cover the prefix/all-absent inference gap, closing the
one disclosure hole this thread had identified. No checkpoint state schema
changed; no source-incompatible API changes in this round.
## 1.3 Performance / Side-Effect Analysis
No change this round — both new substantive commits are
documentation/Javadoc only, and the third is an empty commit. Nothing new to
assess beyond what was already established in prior rounds (bounded
restart-only-path `recover()` scan cost, 1s backoff between retry rounds, O(N)
`containsEquivalentXid` scan per batch).
## 1.4 Error Handling and Logging
No new issues. The two threads open against the current head are both now
answered in the code/docs, not just in comment text:
- @dybyte's Q1 (concurrent-resolution actor) — answered in the thread reply
and now also captured durably in the `restoreCommit()` Javadoc via
`c5f4ec0a0d90`, so the explanation survives independent of the PR discussion.
- My own Issue 1 (prefix/all-absent disclosure gap) — closed via the
migration-guide addition in `5ec277d8be97`.
@dybyte's Q2 (non-blocking ask for a kill-and-restart E2E covering the full
checkpoint -> failure -> restore path) remains an open, explicitly-scoped
follow-up rather than something this round changed — I stand by the reasoning
in my prior reply that a timing-guess-free version of that E2E deserves its own
review rather than being folded in here under time pressure.
# 2. Code Quality Assessment
## 2.1 Coding Standards
Both new substantive commits are comment/doc-only and read clearly; no new
core method or field was added this round, so no new comment-coverage gap to
flag.
## 2.2 Test Coverage and Test Stability: Stable
No test files changed in this round. Unchanged from my prior assessment:
`XaFacadeImplAutoLoadTest`/`XaGroupOpsImplTest` exercise real `XAException`
error codes against mocks, `JdbcSinkAggregatedCommitterTest` covers every
reconciliation branch (prefix-skip, all-absent skip, fail-closed-on-gap,
transient retry, retry-exhaustion), and `XaGroupOpsImplIT` now runs a real
MySQL Testcontainer end to end instead of being `@Disabled`.
## 2.3 Documentation Updates
Improved this round, not just unchanged: the migration guide gap I flagged
as Issue 1 is now closed in both `docs/en` and `docs/zh`.
`docs/en/connectors/sink/Jdbc.md`/`docs/zh` remain accurate against the code as
previously verified.
# 3. Architectural Soundness
## 3.1 Elegance of the Solution: Precise fix
Unchanged.
## 3.2 Maintainability
Unchanged.
## 3.3 Extensibility
Unchanged.
## 3.4 Historical-Version Compatibility
Unchanged: no wire/state-format change; the operational risk is disclosed,
and this round makes that disclosure more complete rather than introducing
anything new to assess.
# 4. Issue Summary
| Number | Issue | Location | Severity |
| --- | --- | --- | --- |
| 1 (closed) | Absent-from-recovery-scan XIDs could not be distinguished
from an external rollback, and only the gap/fail-closed case was disclosed |
`docs/en(zh)/introduction/concepts/incompatible-changes.md` | Was Medium —
resolved by `5ec277d8be97` |
| N/A-2 | `XaFacade.commit(xid, ignoreUnknown=true)` still has no production
caller after evidence-based replay replaced `XAER_NOTA`-as-success inference
(li3zhi4, still open) | `JdbcSinkAggregatedCommitter.java`,
`XaFacadeImplAutoLoad.java` | Low |
| N/A-3 | PR description still overstates the all-absent case as "fails
closed" when code/docs treat it as already-resolved (li3zhi4, still open,
description-only) | PR description text | Low |
No new blocking issues found on this head.
# 5. Merge Recommendation
### Conclusion: Ready to merge
From a source-correctness standpoint, this round's two substantive commits
do exactly what their messages and the accompanying thread replies claim — I
verified both against the actual diffs and current source rather than trusting
the commit subjects, and the third commit is a genuinely empty CI retrigger. My
own Issue 1 is now closed by documentation rather than left as a
recommendation. No new code-level issue surfaced.
Restating the process status precisely, since it's easy to overstate:
`reviewDecision` is currently `CHANGES_REQUESTED`, driven solely by @nzw921rx's
2026-07-27 review — which, as established in my 2026-08-29 full re-review,
predates the recovery-scan reconciliation mechanism entirely and targets a
design that no longer exists in this form. @davidzollo's 2026-08-06
`CHANGES_REQUESTED` no longer counts toward `reviewDecision` because his latest
review on this thread is `COMMENTED` (his concrete non-idempotency scenario was
fixed in code back in `97b0942d7b2a`, and he has continued actively pushing
fixes and replying since) — but I want to be precise that GitHub superseding
his review state for computation purposes is not the same as him having
formally said "resolved, no objection," so I'm not claiming more than the
mechanical fact here. @dybyte's `APPROVED` review was auto-dismissed by branch
protection when the `5ec277d8be97` commit landed, which is expected
push-triggered be
havior, not a new objection — nothing in that thread asked for anything this
round didn't already close out.
CI: the `Build` check on the current head (`d2bc272f3641`) is still in
progress on the fork as I write this (run `33278167729`, ~3.5h in). Three jobs
have already failed early — `updated-modules-integration-test-part-2`,
`updated-modules-integration-test-part-3`, and `doris-connector-it` (all on JDK
8, ubuntu-latest) — but their logs aren't retrievable yet while the overall run
is still active, so I can't pull the exact failure lines this time the way I
did in my previous four CI-triage rounds on this same PR. What I can say
factually: the first two of those three job buckets have failed and been
diagnosed as unrelated Maven Central network flakes or an unrelated
`PostgresCDCIT` timing flake in every prior round on this exact PR (this PR's
diff has never touched `connector-jdbc-e2e-part-2`, `connector-milvus`,
`connector-cdc-postgres`, or `connector-doris`), so this is consistent with —
but not yet independently re-confirmed as — the same recurring infra-flake
pattern. I'm
flagging that distinction rather than asserting the current failures are
flakes before I've actually read their logs.
1. Blockers (process, not code): @nzw921rx's stale `CHANGES_REQUESTED` needs
to be revisited against the current head and either re-affirmed with a concrete
remaining objection or dismissed/updated by them or a maintainer with write
access — I cannot do this myself as the PR author. CI is still running on the
current head with three early failures whose logs aren't yet available; once it
completes, those logs should be pulled to confirm they match the established
unrelated-infra pattern before assuming a rerun is all that's needed.
2. Recommended fixes (non-blocking): the two low-severity carryovers from
li3zhi4 (PR description wording, unused `ignoreUnknown=true` path) remain open
and are cheap to fold in whenever this PR is touched again.
No alternative implementation approach changes here; the
commit-order-evidence design remains the right shape given that Zeta doesn't
persist partial-commit state across a checkpoint restart today.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]