KnightChess opened a new issue, #19357:
URL: https://github.com/apache/hudi/issues/19357

   ### Bug Description
   
   **What happened:**
   When a user stopped a Flink job (not a cluster restart) and the job had 
already reached the CANCELLED terminal state, they immediately started a new 
job. The new job started up normally, but the next day partial data loss was 
discovered.
   
   Root cause investigation: when the job entered the CANCELLED state, 
StreamWriteOperatorCoordinator.close() had not yet finished executing (HDFS was 
experiencing slow requests at the time, and close() took more than 10 minutes 
to complete). Before the old close() returned, the new job had already been 
launched and had created a new StreamWriteOperatorCoordinator. This resulted in 
two StreamWriteOperatorCoordinator instances coexisting and both committing 
instants to the same Hudi table. The instant/checkpoint states of the two 
coordinators overwrote or conflicted with each other, ultimately causing some 
already-committed data to be dropped.
   
   Key timeline:
   ```shell
   t0: User stops the job
   t1: Job state on the JobManager -> CANCELLED (terminal)
   t2: User starts a new job -> new Coordinator #2 is created and starts working
   ...
   t1 + 10min: close() of the old Coordinator #1 finally finishes (HDFS slow 
requests)
           -> #1 and #2 have coexisted for ~10 minutes, and both have been
              writing/committing to the same table during that window
   ```
   
   **What you expected:**
   A normal stop → start cycle should not cause data loss. Any of the following 
would be acceptable:
   1. When a new job starts, it should detect that the old 
StreamWriteOperatorCoordinator has not finished close() yet, and block/delay 
startup until the old coordinator has fully released its resources.
   2. StreamWriteOperatorCoordinator should enforce mutual exclusion / leasing 
(e.g., a distributed lock or fencing token keyed on the table path) to 
guarantee that at any moment only one coordinator commits instants for a given 
table. A late-arriving coordinator should detect the conflict and fail fast; a 
coordinator that has lost its lease should stop committing.
   3. The job state should not be allowed to transition to a terminal state 
(CANCELLED) until close() has fully returned; the job must not reach a terminal 
state while the coordinator is still doing asynchronous cleanup in the 
background.
   4. Optimize the runtime of StreamWriteOperatorCoordinator.close(). 
Conceptually, close() should be a lightweight teardown operation — it should 
not perform heavy or long-blocking work (e.g., synchronous HDFS calls that can 
take 10+ minutes on a slow NameNode). Heavy work should either be moved out of 
the shutdown path, made asynchronous with a bounded timeout, or split so that 
the critical section that must run under shutdown is minimal. This both reduces 
the window in which two coordinators can coexist and prevents close() from 
being blocked indefinitely by external storage issues.
   
   **Root cause of the data loss**
   <img width="2920" height="926" alt="Image" 
src="https://github.com/user-attachments/assets/aad6b0af-67fe-48b9-b3b8-f5dcf62eb7d0";
 />
   
   The data loss is caused by the following interleaving between the new 
coordinator and the still-alive old coordinator:
   
   1. 22:31:38 — The new coordinator commits instant 20260721223123851 and then 
enters its scheduling phase. It does not immediately generate the next instant.
   2. 22:31:43 — During this scheduling gap, the old coordinator (whose close() 
is still running due to slow HDFS) generates its own instant 20260721223143462.
   3. Because the TaskManager fetches whatever instant is currently available 
via lastPending, it picks up the old coordinator's instant 20260721223143462 
and starts flushing data against it. The corresponding WriteResultEvents are 
sent back and buffered on the old coordinator.
   4. 22:34 — The new coordinator finally generates its next instant 
20260721223443220. From this point on, the TaskManager flushes new data against 
20260721223443220 and sends the resulting WriteResultEvents to the new 
coordinator.
   5. When the new coordinator receives these WriteResultEvents from the 
TaskManager, it directly overwrites the events already in its own event buffer 
for that subtask. The writes that had been flushed against the old 
coordinator's instant 20260721223143462 are therefore silently dropped from the 
new coordinator's buffer, never make it into any commit metadata, and end up as 
lost data.
   
   In short: within the new coordinator's scheduling gap, the TaskManager was 
tricked into flushing to an instant owned by the old coordinator; the resulting 
WriteResultEvents were then clobbered when the new coordinator's own instant 
came online, causing that batch of writes to be lost.
   
   
   
   ### Environment
   
   **Hudi version:** 0.13.1 
   **Query engine:** Flink
   **Relevant configs:** mor-avro
   
   
   ### Logs and Stack Trace
   
   The following two marker-file log lines come from the same writer subtask 
(Sink: bucket_write(t: (15/40)#0) within the same Flink checkpoint window, but 
they correspond to two different Hudi instants:
   ```shell
   2026-07-21 22:32:27.316 WARN  [Sink: bucket_write(t: (15/40)#0]
     org.apache.hudi.table.marker.DirectWriteMarkers - Marker Path=
     hdfs://xxxhudi/.hoodie/.temp/20260721223143462/dt=2026-07-21/
     
00000007-51bc-4193-8f56-3c9331d97cf4_14-40-0_20260721121340693.parquet.marker.APPEND
     already exists, cancel creation
   
   2026-07-21 22:36:29.215 INFO  [Sink: bucket_write(t: (15/40)#0]
     org.apache.hudi.table.marker.DirectWriteMarkers - [direct] Created marker 
file
     hdfs://xxxhudi/.hoodie/.temp/20260721223443220/dt=2026-07-21/
     
00000007-51bc-4193-8f56-3c9331d97cf4_14-40-0_20260721121340693.parquet.marker.APPEND
     in 213 ms
   ```


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