loustler commented on issue #11679:
URL: https://github.com/apache/seatunnel/issues/11679#issuecomment-5229110726

   Thanks both. @SEZ9 asked for the logs, so I pulled the full job logs for 
both legs and went through them properly. Two things came out of that: **I have 
to correct two statements in my own issue description**, and the corrected 
picture is a good deal sharper than what I originally reported — it points at a 
specific method.
   
   Everything below is from run `31100290272` on 
`claude/dev-baseline-paimon-s3` @ `80fe7dc77` (`origin/dev` + a three-line 
comment, added only so the `changes` filter would schedule 
`paimon-connector-it`). Log excerpts are folded; open the one you want.
   
   ---
   
   ## First, two corrections to the issue description
   
   Steps 3 and 4 of my "Observed sequence" describe @DanielLeens's trace of run 
`31077218606` (the #11648 head), which I paraphrased when filing this without 
re-deriving them from my own baseline run. On my baseline run neither holds:
   
   | I wrote | Actually, on `dev` @ `80fe7dc77`, JDK 8 leg |
   |---|---|
   | "`SubPlan.handleCheckpointError()` moves the pipeline `RUNNING -> 
CANCELING`. The pipeline is resubmitted and fails identically, repeatedly." | 
There is **no** `CANCELING` transition anywhere, and no repeated resubmission. 
`handleCheckpointError` logs `checkpoint have error, cancel the pipeline` — and 
the pipeline never logs anything again. |
   | "the coordinator's own periodic status dump keeps reporting 
`pendingJobCount / … = 0` … every ~10s from this point onward, for the next ~2 
hours" | The dump does **not** keep reporting. `CoordinatorService`'s 10-second 
print stops permanently 6 seconds after the error. The last all-zero `Job info 
detail` I quoted is from **13:11:05 — before the failure at 13:11:09**, so it 
was never evidence of anything. |
   
   Both were my error, not the reviewer's — his trace was accurate for the run 
he was looking at. Sorry for putting them in the issue as if they were baseline 
observations. The corrected sequence follows, and it is a stronger case, not a 
weaker one.
   
   ---
   
   ## What actually happens
   
   ```
   13:11:09     NoPrivilegeException: User paimon doesn't have privilege SELECT
                   on table seatunnel_namespace11.st_test
   13:11:09,896  WARN  SubPlan - Pipeline [(1/1)] checkpoint have error, cancel 
the pipeline
   13:11:09,916  INFO  TaskExecutionService - task 1000300000000 error with 
exception
   13:11:09,918  INFO  LoggingEventHandler - 
ReaderCloseEvent(jobId=1137734595555360769)
                   <- the last mention of this job anywhere in the log
   
   13:11:15,786  INFO  CoordinatorService - Thread Pool Status
                   <- the last periodic print, ever
   
   13:11:20,210  WARN  SlowOperationDetector - Slow operation detected:
                   SubPlan.updatePipelineState(SubPlan.java:345)
                   SubPlan.handleCheckpointError(SubPlan.java:644)
                   JobMaster.handleCheckpointError(JobMaster.java:770)
                   
CheckpointManager.handleCheckpointError(CheckpointManager.java:219)
                   ... CheckpointErrorReportOperation.runInternal
                   ... com.hazelcast...OperationThread.executeRun
   
   13:12:02,527  INFO  CoordinatorService - clean job log, jobId: 
1137734545932550145
                   <- the last CoordinatorService line of any kind
   
   13:10:45 → 15:22:22   Create/Release classloader for job 1137734545932550145 
(the job that
                         already FINISHED), every 5 s, 3,120 cycles, for 2 h 11 
m
   
   15:22:25  ##[error]The operation was canceled.
   15:22:25  Terminate orphan process: pid (14667) (java) / (15448) (sh) / 
(15450) (java)
   ```
   
   After 13:12:03 the window contains **one** line that is not classloader 
churn, out of ~6,270. No ERROR or WARN of any kind after 13:11:21. The engine 
does not crash, does not retry, does not report — it simply stops.
   
   Note the last two lines: the runner has to kill an orphaned `sh` and two 
`java` processes. That is the `seatunnel.sh` client the test was blocked on, 
still alive after 2 hours.
   
   <details>
   <summary><b>Log — the failure and the cancel decision 
(13:11:09)</b></summary>
   
   The container's STDOUT and STDERR are relayed by separate consumers, so 
don't read anything into the sub-second interleaving between the STDERR stack 
and the STDOUT engine lines.
   
   ```
   [1137734595555360769] 13:11:09,901 STDERR: 
org.apache.seatunnel.common.utils.SeaTunnelException:
       org.apache.paimon.privilege.NoPrivilegeException: User paimon doesn't 
have privilege SELECT
       on table seatunnel_namespace11.st_test
        at 
org.apache.paimon.privilege.PrivilegeCheckerImpl.assertCanSelect(PrivilegeCheckerImpl.java:44)
        at 
org.apache.paimon.privilege.PrivilegedFileStoreTable.newScan(PrivilegedFileStoreTable.java:202)
        ...
        at 
org.apache.seatunnel.engine.server.task.SourceSeaTunnelTask.init(SourceSeaTunnelTask.java:76)
        at 
org.apache.seatunnel.engine.server.TaskExecutionService$BlockingWorker.run(TaskExecutionService.java:1077)
   
   [1137734595555360769] 13:11:09,896 WARN  SubPlan - Job 
paimon_to_paimon_with_s3_with_privilege.conf
       (1137734595555360769), Pipeline: [(1/1)] checkpoint have error, cancel 
the pipeline
   
   [1137734595555360769] 13:11:09,916 INFO  TaskExecutionService - task 
1000300000000 error with exception
   [] 13:11:09,918 INFO  LoggingEventHandler - log event: 
ReaderCloseEvent(createdTime=1786021869917,
       jobId=1137734595555360769, eventType=LIFECYCLE_READER_CLOSE)
   ```
   
   `checkpoint have error, cancel the pipeline` is the first line of 
`SubPlan.handleCheckpointError()`. Nothing after it — no `turned from state ... 
to CANCELING`, no `will end with state`, no `state process is stop` — is ever 
logged for this pipeline.
   </details>
   
   <details>
   <summary><b>Log — the Hazelcast operation thread stuck in 
<code>SubPlan.updatePipelineState</code> (13:11:20)</b></summary>
   
   ```
   [] 13:11:20,210 WARN 
com.hazelcast.spi.impl.operationexecutor.slowoperationdetector.SlowOperationDetector
       - [localhost]:5801 [seatunnel] [5.1] Slow operation detected: ...
        
org.apache.seatunnel.engine.server.dag.physical.SubPlan.updatePipelineState(SubPlan.java:345)
        
org.apache.seatunnel.engine.server.dag.physical.SubPlan.handleCheckpointError(SubPlan.java:644)
        
org.apache.seatunnel.engine.server.master.JobMaster.lambda$handleCheckpointError$9(JobMaster.java:773)
        java.util.ArrayList.forEach(ArrayList.java:1259)
        
org.apache.seatunnel.engine.server.master.JobMaster.handleCheckpointError(JobMaster.java:770)
        
org.apache.seatunnel.engine.server.checkpoint.CheckpointManager.handleCheckpointError(CheckpointManager.java:219)
        
org.apache.seatunnel.engine.server.checkpoint.CheckpointCoordinator.handleCoordinatorError(CheckpointCoordinator.java:352)
        
org.apache.seatunnel.engine.server.checkpoint.CheckpointCoordinator.handleCoordinatorError(CheckpointCoordinator.java:338)
        
org.apache.seatunnel.engine.server.checkpoint.CheckpointCoordinator.reportCheckpointErrorFromTask(CheckpointCoordinator.java:519)
        
org.apache.seatunnel.engine.server.checkpoint.CheckpointManager.reportCheckpointErrorFromTask(CheckpointManager.java:227)
        
org.apache.seatunnel.engine.server.checkpoint.operation.CheckpointErrorReportOperation.runInternal(CheckpointErrorReportOperation.java:48)
        
org.apache.seatunnel.engine.server.task.operation.TracingOperation.run(TracingOperation.java:42)
        
com.hazelcast.spi.impl.operationservice.Operation.call(Operation.java:189)
        
com.hazelcast.spi.impl.operationservice.impl.OperationRunnerImpl.call(OperationRunnerImpl.java:273)
        
com.hazelcast.spi.impl.operationexecutor.impl.OperationThread.executeRun(OperationThread.java:123)
        
com.hazelcast.internal.util.executor.HazelcastManagedThread.run(HazelcastManagedThread.java:102)
   ```
   
   One caveat I want to state rather than gloss: Hazelcast's 
`SlowOperationDetector` logs a given stack **once** and then aggregates 
repeats, so a single entry does not by itself prove the thread stayed there. 
What corroborates it is independent — `CoordinatorService`'s 10-second periodic 
print stops 5 seconds *before* this warning and never resumes.
   </details>
   
   <details>
   <summary><b>Log — the coordinator's periodic print stops, and what runs for 
the next two hours</b></summary>
   
   `CoordinatorService` prints on a 10-second cadence. Within the 
`PaimonWithS3IT` window it prints exactly four times and then stops:
   
   ```
   13:10:45,790  CoordinatorService Thread Pool Status
   13:10:55,789  CoordinatorService Thread Pool Status
   13:10:55,816  Job info detail        (all counts 0)
   13:11:05,788  CoordinatorService Thread Pool Status
   13:11:05,794  Job info detail        (all counts 0)   <- LAST, and this is 
BEFORE the 13:11:09 failure
   13:11:15,788  CoordinatorService Thread Pool Status   <- LAST periodic print 
of any kind
   13:12:02,527  clean job log, jobId: 1137734545932550145   <- LAST 
CoordinatorService line at all
   ```
   
   From then until the runner kills the job at 15:22, the only thing the engine 
emits is this, every 5 seconds:
   
   ```
   13:xx:xx  DefaultClassLoaderService - Create classloader for job 
1137734545932550145
               with jars 
[file:/tmp/seatunnel/connectors/connector-fake-3.0.0-SNAPSHOT-2.12.15.jar]
   13:xx:xx  DefaultClassLoaderService - Create classloader for job 
1137734545932550145
               with jars 
[file:/tmp/seatunnel/connectors/connector-paimon-3.0.0-SNAPSHOT-2.12.15.jar]
   13:xx:xx  DefaultClassLoaderService - Release classloader for job 
1137734545932550145 ...
   13:xx:xx  DefaultClassLoaderService - Release classloader for job 
1137734545932550145 ...
   ```
   
   Totals in the window:
   
   | job | Create | Release |
   |---|---|---|
   | `1137734545932550145` (job 1 — already `FINISHED`) | 3,120 | 3,120 |
   | `1137734595555360769` (job 2 — the rejected one) | 2 | **1** |
   
   Two things worth noting. The churn is on the job that **finished 
successfully**, not the failed one. And job 2 creates two classloaders and 
releases one — a leak consistent with its pipeline never completing teardown.
   </details>
   
   <details>
   <summary><b>Log — job-level state transitions, job 1 vs job 2</b></summary>
   
   ```
   Job fake_to_paimon_with_s3_with_privilege.conf (1137734545932550145)
       CREATED   -> PENDING
       PENDING   -> SCHEDULED
       SCHEDULED -> RUNNING
       RUNNING   -> FINISHED          <- complete
   
   Job paimon_to_paimon_with_s3_with_privilege.conf (1137734595555360769)
       CREATED   -> PENDING
       PENDING   -> SCHEDULED
                                      <- and that is all. Never RUNNING. Never 
terminal.
   ```
   
   Job 2 never reaches job-level `RUNNING`. Its *tasks* did deploy and run 
(`DEPLOYING -> RUNNING` at task-group level, three task groups on slots 4/5/6) 
— the privilege error arrives while the job itself is still `SCHEDULED`.
   </details>
   
   <details>
   <summary><b>Control — the same test, the same commit, on the JDK 11 leg (job 
<code>92614470554</code>)</b></summary>
   
   Same commit, same config file, passing leg — `Tests run: 4, Failures: 0, 
Errors: 0, Skipped: 0, Time elapsed: 118.481 s`.
   
   | | JDK 8 leg (hung) | JDK 11 leg (passed) |
   |---|---|---|
   | `checkpoint have error, cancel the pipeline` | 1 | **8** |
   | `Slow operation detected` | **1** | 0 |
   | `SubPlan.updatePipelineState` in a stack | **1** | 0 |
   | `CoordinatorService` periodic prints | 4, then stops | 14, continuous |
   | `paimon_to_paimon_with_s3_with_privilege.conf` job states | 
`CREATED→PENDING→SCHEDULED`, stop | `RUNNING→FAILING→FAILED` (×2) |
   | classloader Create / Release | 3,122 / 3,121 | 42 / 42 |
   
   This is the part I find most useful. The cancel path is not rare on the good 
leg — it runs **eight** times and completes every time, driving the job to 
`FAILING` and then `FAILED`. So `handleCheckpointError` is not broken in 
general. The difference is *when* the error arrives: on the passing leg the job 
had already reached `RUNNING`; on the hung leg it was still `SCHEDULED`.
   </details>
   
   <details>
   <summary><b>Control — this was not a slow or overloaded runner</b></summary>
   
   On the same hung JDK 8 leg, the sibling test class ran to completion 
normally before `PaimonWithS3IT` started:
   
   ```
   12:31:35  [INFO] Running org.apache.seatunnel.e2e.connector.paimon.PaimonIT
   12:51:41  [INFO] Tests run: 26, Failures: 0, Errors: 0, Skipped: 0,
                    Time elapsed: 1,206.934 s - in ...PaimonIT
   13:10:34  [INFO] Running 
org.apache.seatunnel.e2e.connector.paimon.PaimonWithS3IT
   15:22:25  ##[error]The operation was canceled.
   ```
   </details>
   
   ---
   
   ## @SEZ9 — on your (a)/(b) split
   
   Your (b) is the one the evidence lands on, and your (a) needs amending for 
this run:
   
   > (a) `SubPlan.handleCheckpointError` repeatedly resubmitting a pipeline 
that fails deterministically
   
   On my baseline run there is no repeated resubmission — 
`handleCheckpointError` is entered **once** and does not come back out. So a 
retry cap wouldn't help here; the pipeline never gets as far as retrying.
   
   > (b) the job vanishing from coordinator counters without ever transitioning 
to FAILED/CANCELED, so the client-side job-completion future never completes
   
   This is exactly it, and the logs now name the frame. `handleCheckpointError` 
(`SubPlan.java:644`) does:
   
   ```java
   public void handleCheckpointError() {
       log.warn(... "checkpoint have error, cancel the pipeline");
       if (!getPipelineState().isEndState()) {
           updatePipelineState(PipelineStatus.CANCELING);   // SubPlan.java:345
       }
   }
   ```
   
   and `updatePipelineState` is **`synchronized`** and performs Hazelcast 
`IMap` operations (`updateStateTimestamps`, `runningJobStateIMap.get/set`) 
inside `RetryUtils.retryWithException`, then calls `stateProcess()` — also 
`synchronized`. All of this is running on a Hazelcast **operation thread** 
(`CheckpointErrorReportOperation` → `TracingOperation` → `OperationRunnerImpl` 
→ `OperationThread`).
   
   The `turned from state ... to CANCELING` log line is emitted *after* the 
retry block, and it never appears — so control never got past the IMap work. A 
Hazelcast operation thread blocking on distributed map operations while holding 
a monitor is a shape that can starve the very pool those operations need.
   
   I want to flag the limit of that reasoning rather than overstate it: 
`Constant.OPERATION_RETRY_TIME = 30` and `OPERATION_RETRY_SLEEP = 2000`, so the 
retry loop is bounded at roughly 60 seconds — it cannot by itself account for a 
two-hour stall, and no `log.error` from the surrounding `catch` ever appears. 
So something downstream of, or concurrent with, that retry is where it actually 
parks. I don't know the engine well enough to say which, and I'd rather hand 
you a located frame than a guessed cause.
   
   Agreed on your fix direction, and agreed that a client-side timeout should 
not be the primary fix. One clarification so it isn't misread when it appears: 
I'm opening a separate PR that bounds `executeJob` **in the E2E test harness**, 
at @DanielLeens's request on #11648. That is CI blast-radius containment — a 
recurrence costs seconds instead of a 180-minute runner slot — not a proposed 
fix for this issue, and it touches no engine or client code.
   
   ## @SEZ9 — on the logs
   
   Both full job logs are downloaded and I can pull any window you name:
   
   - job `92614470569` — JDK 8, 31,752,702 bytes, hung
   - job `92614470554` — JDK 11, 31,308,272 bytes, passed
   
   What I still do **not** have is a thread dump of the blocked client, which 
is what would settle whether the future is dropped or the terminal state is 
never published. The hang is non-deterministic and I could not reproduce it 
locally: SeaTunnel pins `testcontainer.version` at 1.17.6, whose docker-java 
negotiates Docker API 1.32, and the daemon available to me requires 1.40+. 
Bumping the pin works but perturbs container startup timing — precisely the 
variable a timing race turns on — so I stopped rather than chase a reproduction 
whose negative result would mean nothing.
   
   One practical warning if you pull these yourself: `gh api .../logs` 
**truncates inconsistently** (I got 27.8 MB, 18.9 MB and 29.7 MB for the same 
job on three attempts), and it refuses the download entirely without 
`--allow-escape-sequences`. Check the tail reaches the post-job `Cleaning up 
orphan processes` block before concluding a line is absent — two of my earlier 
readings of this incident were wrong for exactly that reason, and so were the 
two corrections at the top of this comment.
   
   ## @abdessalems — yes, please, and here is where to start
   
   > I can reproduce the problem and look into the client-side 
`waitForJobComplete()` / terminal-state handling.
   
   That is exactly the half I'm not taking — I said in the issue I'm not 
submitting the engine fix, since the suspected area is Zeta's cancel/restore 
and client status-polling logic and I don't know it well enough to change 
safely. If you want it, it's yours.
   
   Three things that should save you time:
   
   1. **The server-side frame is located.** `SubPlan.updatePipelineState` 
(`SubPlan.java:345`), reached from `handleCheckpointError` (`SubPlan.java:644`) 
on a Hazelcast operation thread. If the pipeline never leaves `SCHEDULED`, no 
terminal `JobResult` is ever published — so before digging into the client, it 
is worth confirming whether the client is dropping a result or never being sent 
one. The evidence above points at the latter.
   2. **It is not deterministic, and the trigger looks like a race on job 
state.** Same commit, two legs: on the passing leg the job reached `RUNNING` 
before the privilege error and went `RUNNING → FAILING → FAILED`; on the hung 
leg the error arrived while the job was still `SCHEDULED`. Don't read a single 
green run as "not reproducing" — expect several attempts.
   3. **If you reproduce it, grab a thread dump before anything is torn down** 
— `jstack` on both the `seatunnel.sh` client and the engine process inside the 
container. That single artifact is what none of us has.
   
   Happy to help with reproduction, to test a candidate fix, and to pull any 
window out of the two runs above on request.
   


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