DanielLeens commented on PR #11557:
URL: https://github.com/apache/seatunnel/pull/11557#issuecomment-5379334489

   # What Problem Does This PR Solve?
   - User pain point: in Zeta today, Source concurrency ownership is fragmented 
across every connector — the collector loop calls `SourceReader#pollNext` under 
a connector-owned checkpoint lock, and split assignment / reader registration 
travels through Hazelcast operation threads with ad-hoc synchronization that 
each connector author has to get right on their own.
   - Fix approach: move Reader and Split Enumerator concurrency ownership into 
two engine-owned event loops (a Reader runtime and a Coordinator runtime) 
behind a disabled-by-default, exact-allowlist "managed" lane, while the 
existing legacy lane and the public `SourceReader`/`SplitEnumerator` SPI stay 
completely untouched for every connector that has not opted in.
   - One-sentence summary: this is STIP-31 phase 0, an explicitly gated Draft 
with its own five-item graduation checklist (still 0/5 at this head); the 
design is sound and its compatibility posture for non-adopting connectors is 
genuinely strong, but at the current head it is not mergeable — it carries a 
real, unresolved git conflict against `dev`, and a concurrency stall I 
re-traced end-to-end (queue → admit → drain) is confirmed still present.
   
   # 1. Code Change Review
   
   ## 1.1 Core Logic Analysis
   
   **Housekeeping first, honestly.** This is my tenth pass over this PR (nine 
prior self-reviews plus one comment from @nzw921rx). The head (`e62beaaadf98`) 
has **not moved since 2026-08-14**, and I confirmed live against GitHub just 
now that there is no new commit, no new review, and no new comment/reply of any 
kind since my own last review at 2026-08-21T14:12:53Z — `gh pr view`'s 
`updatedAt` timestamp is literally just that review comment, not a code push. 
So this round is not a from-scratch re-derivation of the standing 45-issue 
backlog (16 High / 3 Med-High / 17 Medium / 9 Low from the 2026-08-16 round) — 
nothing has happened that would invalidate or require re-deriving it. What I 
did instead was independently re-verify, from first principles in the actual 
source at this exact head (not by trusting my own prior write-ups), the two 
most consequential blockers, and I traced one of them one layer deeper than any 
previous round did.
   
   **Re-verified #1: the `dev` conflict is real, not a stale GitHub flag.**
   
   `git merge-tree $(git merge-base dev refs/pr-review/11557) 
refs/pr-review/11557 dev`, run fresh against the current mirror, produces an 
actual `<<<<<<<`/`>>>>>>>` conflict body (not just an auto-resolved "changed in 
both") in:
   
   
`seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/execution/TaskExecutionContext.java`
   
   ```java
   +<<<<<<< .our
       /** Returns the immutable engine deployment identity for this task 
attempt. */
       public long getExecutionId() {
           return executionId;
   +=======
   +    public ClassLoaderService getClassLoaderService() {
   +        return taskExecutionService.getClassLoaderService();
   +>>>>>>> .their
       }
   ```
   
   This PR's `getExecutionId()` accessor and a `getClassLoaderService()` 
accessor that has independently landed on `dev` since insert at the same 
location, and neither side closes its own brace — a textbook adjacent-insertion 
conflict. `gh pr view` confirms `mergeStateStatus: DIRTY`, `mergeable: 
CONFLICTING` live, matching this. Not mergeable as-is regardless of code-review 
outcome.
   
   **Re-verified #2: the managed coordinator's `PREPARE_CLOSE` stall — traced 
the full admit→queue→drain chain, not just the symptom.**
   
   Previous rounds identified that `runOneTurn()` stops being called once the 
state machine leaves `RUNNING`. I went one step further this round and traced 
*why* that's fatal rather than just harmless idling. In 
`SourceSplitEnumeratorTask.java`:
   
   ```java
   case RUNNING:
       ...
       } else if (isManagedCoordinatorRuntime()) {
           if (!managedCoordinatorRuntime.runOneTurn()) {
               Thread.sleep(managedSourceRuntimeConfig.getIdleWaitMillis());
           }
       }
       break;
   case PREPARE_CLOSE:
       if (closeCalled) {
           currState = CLOSED;
       } else {
           Thread.sleep(100);
       }
       break;
   ```
   
   `runOneTurn()` is called from exactly two places in the whole codebase — 
this `RUNNING` branch, and a bootstrap/restore call — never from 
`PREPARE_CLOSE`. The question is whether anything else can set `closeCalled = 
true` for the managed lane without `runOneTurn()`. I traced it: 
`notifyCheckpointComplete`/`notifyCheckpointAborted` (the only two call sites 
in this class that ever invoke `closeCall()` on the legacy path) **both return 
early for the managed lane** before reaching their own `closeCall()` line, 
delegating instead to 
`managedCoordinatorRuntime.admitCheckpointComplete(checkpointId)` / 
`admitCheckpointAborted(...)`. Those two methods, in 
`ManagedSourceCoordinatorRuntime.java`, do **not** execute the callback — they 
only call `requireControlAdmission(() -> applyCheckpointComplete(checkpointId), 
...)`, which enqueues a `ControlEvent`. 
`applyCheckpointComplete`/`applyCheckpointAborted` — the only methods that ever 
call `task.managedCheckpointCallbackFinished(checkpoi
 ntId)`, which is the *only* path that sets `closeCalled = true` for the 
managed lane — are private methods with zero direct callers; they are only 
reachable by whatever dequeues and executes `ControlEvent`s, which per 
`runOneTurn()`'s own doc comment ("Executes one connector callback, scheduler 
result, or admitted Reader command") is exactly what `runOneTurn()` does.
   
   So the full chain is: checkpoint-complete notification arrives → gets 
enqueued → is only ever drained by `runOneTurn()` → `runOneTurn()` is only ever 
called from `RUNNING` → state has already moved to `PREPARE_CLOSE` → the event 
that would flip `closeCalled` to `true` is queued forever → `PREPARE_CLOSE` 
polls `closeCalled` every 100ms, forever. This is not a theoretical race 
window; it is a real deadlock in the managed lane's own bounded-job / 
savepoint-driven close sequence, confirmed by direct call-graph tracing at the 
current head, independent of and more precise than my own prior write-up of the 
same issue.
   
   **Runtime path (managed lane only — bypassed entirely for every connector 
not on the exact allowlist):**
   ```text
   Zeta job submission -> PhysicalPlanGenerator selects legacy vs managed lane 
(re-derived live on
      every JobMaster#init, including restore)
     -> managed lane: SourceSplitEnumeratorTask drives 
ManagedSourceCoordinatorRuntime.runOneTurn()
          only while state == RUNNING
     -> checkpoint-complete/aborted notifications admit a ControlEvent into the 
coordinator's queue,
          drained only by runOneTurn() -- never drained once PREPARE_CLOSE is 
entered (deadlock, above)
     -> ManagedSourceReaderRuntime: bounded mailbox, admission control, 
checkpoint-aware split ownership
     -> checkpoint: explicit Reader/coordinator checkpoint serializers with 
capability digest and
          connector state version
   ```
   Because the feature is fail-closed and disabled by default (empty allowlist 
+ global kill switch), the normal execution path for every currently-shipping 
connector never reaches any of this — this bounds the blast radius to "not a 
production risk today" while still being a real correctness gap in code that 
intends to graduate out of Draft.
   
   **Responding to @nzw921rx's design comment (Step 0.5, unresolved thread, 
2026-08-06 — no reply from me on record):** you raised whether an exposed 
feature switch is the right long-term shape, versus connectors simply failing 
fast if they don't support a configured feature, worrying an exposed switch 
could let users silently weaken semantics. Looking at the actual mechanism 
again: the switch here is an *exact allowlist* plus a *global kill switch*, 
both defaulting to empty/off — it is not a per-job opt-out that a user can flip 
to silently degrade an already-adopted connector's guarantees; it gates whether 
a connector's engine-side runtime has been migrated at all. I think that 
structurally addresses your concern: it's closer to a migration flag than a 
semantics-weakening user knob, and the code already supports your preferred end 
state — as connectors are added to the allowlist over time, the switch's 
practical effect shrinks toward zero, exactly as you describe. I'd frame it
  as "keep the switch, but the goal is to make it irrelevant by adoption, not 
to remove it up front."
   
   ## 1.2 Compatibility Impact
   
   **Fully compatible in its current, disabled-by-default state.** 
`SourceReader.pollNext(...)`, `Collector.getCheckpointLock()`, and existing 
`SplitEnumerator` methods remain source- and binary-compatible; no connector is 
forced onto the managed lane; the kill switch and exact allowlist mean the 
legacy lane's behavior for every existing connector is byte-for-byte unchanged. 
Re-verified this claim directly rather than taking it on trust — this is the 
one property of the PR I'd call unconditionally strong.
   
   ## 1.3 Performance / Side-Effect Analysis
   Not independently re-benchmarked this round — no soak/performance evidence 
is attached to the PR, which the PR's own graduation checklist requires before 
Draft exit. The bounded-queue, reserved-control-capacity, 
reused-execution-thread design is architecturally sound on paper but still 
unproven without that evidence.
   
   ## 1.4 Error Handling and Logging
   
   **Issue A (High) — Unresolved git conflict against `dev` blocks a clean 
rebase/merge**
   - **Location**: 
`seatunnel-engine/seatunnel-engine-server/src/main/java/org/apache/seatunnel/engine/server/execution/TaskExecutionContext.java`
 (this PR's `getExecutionId()` vs. `dev`'s `getClassLoaderService()`).
   - **Problem**: Confirmed via a fresh `git merge-tree` against current `dev` 
this round — a real, unresolved textual conflict, matching GitHub's live 
`mergeable: CONFLICTING` / `mergeStateStatus: DIRTY`.
   - **Potential risk**: The PR cannot be merged as-is; a naive merge commit 
(rather than a deliberate rebase) risks silently dropping one side's accessor.
   - **Best improvement**: Rebase onto current `dev`, keep both 
`getExecutionId()` and `getClassLoaderService()`.
   - **Severity**: High.
   - **Raised by another reviewer**: No — carryover from my own 08-21 round, 
independently re-confirmed this round with fresh tooling output.
   
   **Issue B (High) — Managed coordinator never drains queued 
checkpoint-complete/aborted control events once `PREPARE_CLOSE` is entered**
   - **Location**: `SourceSplitEnumeratorTask.java` (`RUNNING` branch calls 
`runOneTurn()`; `PREPARE_CLOSE` branch only polls `closeCalled`); 
`ManagedSourceCoordinatorRuntime.java` 
(`admitCheckpointComplete`/`admitCheckpointAborted` only enqueue via 
`requireControlAdmission`; `applyCheckpointComplete`/`applyCheckpointAborted` — 
the only path to `task.managedCheckpointCallbackFinished(...)` → `closeCall()` 
— are unreachable except by `runOneTurn()`'s internal drain).
   - **Problem**: Traced the complete admit→queue→drain chain this round 
(deeper than the prior write-up, which asserted the symptom without tracing why 
`closeCalled` can never flip). Confirmed at the current, unchanged head.
   - **Potential risk**: Real deadlock — a managed-lane job whose close 
sequence depends on a checkpoint-complete/aborted callback (bounded job 
completion, savepoint-driven close) can stall indefinitely in `PREPARE_CLOSE`, 
sleeping 100ms forever.
   - **Best improvement**: Keep draining the coordinator's control-event queue 
while in `PREPARE_CLOSE` (either keep calling `runOneTurn()` or add an explicit 
drain-remaining-control-events step) until the coordinator itself reports 
nothing left to process.
   - **Severity**: High.
   - **Raised by another reviewer**: No — carryover from my own prior round; 
independently re-verified and the causal chain traced one layer deeper this 
round.
   
   **Issue C (High, self-declared blocker) — PR's own Draft graduation 
checklist is 0/5 satisfied**
   - **Location**: PR description, "Draft graduation gates" section.
   - **Problem**: All five gates (clean CI at current head, deterministic 
failover/fault-injection campaigns, managed-connector conformance coverage, 
Kafka/Iceberg/blocking-control/high-split-count performance results, maintainer 
review) remain unchecked, consistent with this round's findings (CI not clean, 
Issue B open, real git conflict).
   - **Potential risk**: None beyond what's already disclosed by the author.
   - **Best improvement**: N/A — this is the PR's own stated bar, not yet met.
   - **Severity**: High (self-declared).
   
   No independent re-audit of logging coverage across the ~1,920-line 
coordinator runtime this round (standing Issue 16 from the 08-16 round: 
effectively zero logging, multiple managed operations collapse distinct failure 
causes to a generic outcome) — no code has changed since that finding, so I 
have no basis to believe it's changed either.
   
   # 2. Code Quality Assessment
   
   ## 2.1 Coding Standards
   No independent re-audit across all 95 files this round given the file count 
and the depth of the nine prior passes; no commit has landed since the last 
audit that would change the answer.
   
   ## 2.2 Test Coverage and Test Stability
   
   **Mandatory stability rating: High risk.** Two independent, still-current 
reasons: (1) the standing review's finding that no test covers any of the four 
legacy↔managed restore paths — the highest-risk compatibility surface in the PR 
— remains unaddressed; (2) I re-checked CI live at this exact head: fork run 
`31787886417` (`DanielLeens/seatunnel`) still shows `unit-test (11, 
windows-latest)` as `failure` (`RestApiSubmitJobStartWithSavePointTest`, 
`IllegalStateException: Node failed to start!`), with `engine-v2-it` and 
`unit-test (11, ubuntu-latest)` `cancelled` and the integration-test-part jobs 
`skipped` as a result of that gating failure. This Windows/Hazelcast-bootstrap 
symptom matches a previously-documented environmental flake pattern (different 
test class each occurrence, ~300s timeout, windows-latest only) rather than 
something tied to this PR's code, but the run has not been re-triggered since 
08-14 and CI is not currently green at this head either way.
   
   ## 2.3 Documentation Updates
   No independent re-verification this round. Standing finding: 
`docs/en/introduction/concepts/incompatible-changes.md` is not updated for this 
feature (arguably correctly, since the feature is fully backward compatible 
while disabled — but worth an explicit note once it graduates), and the 
managed-source-runtime doc's rollback section wording was flagged as ambiguous 
against the surrounding failover-stability claims; unchanged since flagged.
   
   # 3. Architectural Soundness
   
   ## 3.1 Elegance of the Solution — Long-term solution (design) / Temporary 
workaround (current implementation maturity)
   The two-tier design — an exact, versioned, opt-in capability allowlist with 
a global kill switch, legacy lane fully untouched — is the right shape for a 
change this invasive to Zeta's Source execution model, and directly addresses 
@nzw921rx's design concern as discussed in 1.1: it's a migration gate, not a 
semantics-weakening user knob. But "0/5 graduation gates, and a real deadlock I 
just re-traced end-to-end" means the *implementation* has not yet earned 
"long-term solution" independent of the design being right.
   
   ## 3.2 Maintainability
   The near-absence of logging in the ~1,920-line coordinator runtime (standing 
finding) is a real maintainability risk specifically because this code exists 
to take over concurrency reasoning that used to live, visibly, in connector 
implementations. If Issue B's deadlock fires in production, there would 
currently be nothing in the logs pointing at it.
   
   ## 3.3 Extensibility
   The `ManagedSourceReader` bridge and the Fake/Kafka/Iceberg pilots 
demonstrate a workable extension pattern; Iceberg's choice to adopt only the 
coordinator runtime while keeping its Reader on the legacy lane is a sensible 
incremental-adoption example.
   
   ## 3.4 Historical-Version Compatibility
   Fully compatible in the feature's current disabled-by-default state, as in 
1.2. Standing finding, not independently re-verified this round: `SourceConfig` 
gains five new serialized fields with no `serialVersionUID`, a rolling-upgrade 
risk specifically for the master→worker `TaskGroup` serialization path once/if 
the managed lane is enabled for any connector.
   
   # 4. Issue Summary
   
   | # | Issue | Location | Severity |
   |---|-------|----------|----------|
   | A | Unresolved git conflict against current `dev` blocks merge — 
reconfirmed fresh this round | `TaskExecutionContext.java` | High |
   | B | Managed coordinator never drains queued checkpoint-complete/aborted 
events once `PREPARE_CLOSE` is entered — full admit→queue→drain chain traced 
this round | `SourceSplitEnumeratorTask.java`, 
`ManagedSourceCoordinatorRuntime.java` | High |
   | C | PR's own Draft graduation checklist is 0/5 satisfied | PR description 
| High (self-declared) |
   | — | Standing backlog from the 2026-08-16 full-review round, unchanged 
head, still authoritative: 45 numbered issues (16 High / 3 Med-High / 17 Medium 
/ 9 Low), spanning coordinator restore-thread binding, lane re-derivation on 
every `JobMaster#init`, unbounded checkpoint-proof growth, unimplemented 
coordinator epoch-rebase, and near-zero coordinator logging | PR review thread, 
round dated 2026-08-16 | 16 High and below |
   | — | CI: `unit-test (11, windows-latest)` failure at current head, likely 
the documented Windows/Hazelcast bootstrap flake but not re-run to a 
confirmed-clean state | fork run 31787886417 | Needs re-run to confirm |
   
   # 5. Merge Recommendation
   
   ### Conclusion: Not recommended for merge
   
   **1. Blockers — must be fixed**
   1. **Issue A (High)** — Rebase onto current `dev` and resolve the real 
conflict in `TaskExecutionContext.java`.
   2. **Issue B (High)** — Fix the `PREPARE_CLOSE` control-event drain gap; 
I've now traced the exact mechanism (admit→queue→drain) so this should be a 
well-scoped fix rather than a search.
   3. **Issue C / standing backlog** — Work through the standing 16 
High-severity findings from the 2026-08-16 round systematically, in particular 
the fault-injection/failover campaigns and managed-connector conformance 
coverage the PR's own checklist calls for, before this leaves Draft.
   4. Get a fresh, fully green CI run at whatever head results from the above, 
and confirm the Windows unit-test failure doesn't reproduce (or independently 
confirm it's the known environmental flake).
   
   **2. Recommended fixes — non-blocking**
   Follow the standing review's own Medium/Low prioritization from the 
2026-08-16 round once the High items above are closed.
   
   **Overall assessment.** Nothing has changed at the code level since my last 
review three days ago, and I want to be upfront that most of this round's value 
is two things: independently reconfirming, with fresh tooling and a deeper 
call-chain trace, that the two most consequential blockers are real and 
unchanged — plus responding to @nzw921rx's open design question, which had gone 
unanswered. The direction of STIP-31 phase 0 is right, and its compatibility 
story for every connector not opting in is genuinely solid — I keep re-checking 
that claim rather than taking my own past word for it, and it keeps holding up. 
But this is still, correctly, a Draft: it has an actual unresolved conflict 
against `dev`, a concurrency deadlock I can now trace start to finish, and a 
graduation checklist the author's own PR description says is 0/5 done. As the 
author, my view here isn't an independent approval — a write-capable maintainer 
still needs to do the final pass, and per the PR's own sta
 ted gates, that should wait until the fault-injection, conformance, and 
performance evidence the description calls for is actually attached.
   


-- 
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]

Reply via email to