Joy-2000 opened a new issue, #19587:
URL: https://github.com/apache/hudi/issues/19587

   ### Describe the problem you faced
   
   ## Background
   
   In our production environment, we mainly run the following types of Flink 
streaming write workloads:
   
   - COW tables with append writes
   - MOR tables with Bucket Index and upsert writes
   
   A single Hudi table may be concurrently accessed by one or more write, 
table-service, and backfill jobs, including:
   
   - Flink streaming jobs writing to the latest date partition, potentially 
with asyn table services enabled
   - Spark jobs scheduling and/or executing clustering, compaction, log 
compaction, or cleaning
   - Multiple Spark jobs backfilling historical date partitions in parallel
   
   Hudi currently uses its transaction mechanism—NBCC/OCC combined with 
locking—to support these multi-writer scenarios. Among other things, this 
mechanism ensures that:
   
   - Completion times on the active timeline increase monotonically
   - Metadata Table updates are serialized
   
   ## Problem
   
   The current transaction boundary appears to cover several relatively 
expensive operations.
   
   For Flink streaming writes in particular, table-service scheduling, metadata 
commits, and instant-time generation share the same lock. When the underlying 
filesystem, such as HDFS, becomes busy or unstable, these operations may hold 
the lock for an extended period.
   
   This can significantly affect the stability of streaming jobs. For example, 
lock contention may block the sink from obtaining a new instant, resulting in 
prolonged backpressure, data accumulation, and delayed data availability.
   
   We would like to discuss whether the scope between 
`txnManager.beginStateChange` and `txnManager.endStateChange` can be reduced 
under NBCC.
   
   The following operations are possible candidates for moving partially or 
entirely outside the transaction boundary. 
   
   ## 1. Table-service scheduling
   
   `scheduleTableServiceInternal` currently performs the entire scheduling 
process for clustering, compaction, and log compaction inside a transaction.
   
   The scheduling process may involve several expensive operations, including:
   
   - Loading the timeline
   - Building the filesystem view
   - Building the table-service plan
   
   The execution time of these operations can vary significantly, especially 
when the filesystem is under load.
   
   ### Proposal
   
   Keep only the operations that require serialization inside the lock, such as 
`createTable` and `createNewInstantTime`, and move plan building and other 
expensive scheduling operations outside the lock.
   
   ### Potential risk
   
   Without locking the entire scheduling process, multiple table-service jobs 
may schedule overlapping work concurrently. If this is not detected or rejected 
later, it could result in duplicate processing or data correctness issues (In 
our production environment, we use a separate scheduling lock to prevent table 
services from being scheduled concurrently). 
   
   ### Expected benefit
   
   Reduce the duration for which the transaction lock is held during 
table-service scheduling.
   
   ## 2. Request-time generation in `startCommit`
   
   The generation of both request times and completion times currently requires 
acquiring the lock.
   
   Under the current timeline model, locking completion-time generation is 
necessary to preserve monotonically increasing completion times. However, it 
may be worth discussing whether the locking requirement for request-time 
generation can be relaxed.
   
   Compaction request times are an exception because they participate in 
FileSlice boundaries and therefore still require strict serialization.
   
   ### Proposal
   
   Investigate whether request-time generation for regular writes and other 
applicable actions can be moved outside the transaction lock, while retaining 
locking for:
   
   - Completion-time generation
   - Compaction request-time generation
   - Any action whose request time affects FileSlice boundaries or timeline 
ordering semantics
   
   ### Potential risk
   
   Concurrent writers might generate the same request time. Atomic file 
creation by the filesystem currently ensures that only one writer can 
successfully create a requested-instant file with a given name. However, the 
losing writer would still need to detect the collision, generate a new request 
time, and retry.
   
   Therefore, moving request-time generation outside the lock may require an 
explicit collision-detection and retry mechanism.
   
   ### Expected benefit
   
   When a Flink sink obtains its first or next instant, it would no longer be 
blocked solely because another operation is holding the transaction lock. This 
could make data consumption and checkpoint processing smoother.
   
   ## 3. `finalizeWrite` during commit
   
   `commitStats`, `completeClustering`, `completeCompaction`, and 
`completeLogCompaction` perform several operations while completing a write or 
table service. These operations include, but are not limited to:
   
   - `resolveWriteConflict`
   - `finalizeWrite`
   - `writeTableMetadata`
   - `saveAsComplete`
   
   `finalizeWrite` removes speculative or invalid files based on the markers 
for the current instant. When a write touches a large number of partitions or 
files, `DirectWriteMarkers` may issue a large number of filesystem `list` 
operations.
   
   As a result, `finalizeWrite` may hold the transaction lock for a long time 
when the filesystem is busy.
   
   ### Proposal
   
   Move `finalizeWrite` outside the transaction boundary, while keeping 
conflict resolution, Metadata Table updates, and timeline completion inside the 
transaction where serialization is required.
   
   ### Potential risk
   
   We currently do not see an obvious correctness dependency that requires 
`finalizeWrite` itself to run under the transaction lock. However, this needs 
to be verified for all marker implementations and failure-recovery paths.
   
   In particular, we should confirm that moving it outside the transaction does 
not introduce races with rollback, cleaning, or another writer operating on the 
same files.
   
   ### Expected benefit
   
   Reduce the lock duration caused by filesystem operations during 
`finalizeWrite`, especially for large writes or when HDFS is under heavy load.
   
   ## Questions for discussion
   
   1. Which operations inside the current NBCC transaction boundary strictly 
require serialization?
   2. Which action types can request time generation safely occur outside the 
lock?
   3. Are there other expensive filesystem or timeline operations that can be 
moved outside the transaction boundary?
   4. Would it make sense to introduce metrics for transaction lock wait time 
and lock hold time so that lock contention can be observed more directly?
   
   Our understanding and the ideas proposed above may be incomplete or 
incorrect. We would greatly appreciate any corrections, feedback, or 
suggestions from the community.
   
   ### To Reproduce
   
   none
   
   ### Expected behavior
   
   The overall goal is to preserve the existing NBCC correctness guarantees 
while reducing lock contention and improving the stability of Flink streaming 
writes in multi-writer environments.
   
   ### Environment Description
   
   Hudi:1.1.1
   Flink:1.20
   Spark:3.4
   
   ### Additional context
   
   _No response_
   
   ### Stacktrace
   
   ```shell
   
   ```


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