-
Notifications
You must be signed in to change notification settings - Fork 216
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Enhancement] Reduce concurrent conflicts between block write operations and poll operations #1550
Comments
…and poll operations AutoMQ#1550
…and poll operations (AutoMQ#1550)
Currently, |
…and poll operations (AutoMQ#1550)
…and poll operations (AutoMQ#1550)
hey, I have updated the documentation. Please review the proposed handling scheme for |
Great job, the explanation is very detailed. Among the two approaches in
|
…and poll operations (AutoMQ#1550)
…and poll operations (AutoMQ#1550)
… and poll operations (AutoMQ#1550)
… and poll operations (AutoMQ#1550)
Background:
Currently, there exists intense concurrent competition between the write and poll operations of the block, which affects the write performance of the Write-Ahead Logging (WAL).
path: com.automq.stream.s3.wal.impl.block.SlidingWindowService
Current Status:
During the current block write process,
Conflict Points:
Solution:
Optimization Approach: The write and poll operations should be separated to minimize concurrent conflicts.
1. Lock Separation Optimization
To separate the conflicts between writing blocks and polling blocks, lock separation can be implemented. For the polling operation, a separate lock, pollBlockLock, can be set up.
2. Shared Resource Handling:
After implementing lock separation, the next challenge is managing shared resources.
pendingBlocks: Both writing and polling involve modifications to pendingBlocks. Therefore, pendingBlocks should be implemented as a thread-safe queue, such as LinkedBlockingQueue.
currentBlock: Currently, both writing and polling involve accessing the current block, leading to inevitable conflicts between the two processes.
To optimize this, a batching time can be introduced, which can be set to the current minWriteIntervalNanos. This way, during polling, a decision can be made based on time whether to include the currentBlock in the poll. If needed, an attempt to acquire blockLock is made, potentially causing a conflict; otherwise, no conflict arises.
writeBlocks: Currently, the primary role of
writeBlocks
is to update thestartOffset
ofWindowCoreData
. ForwriteBlocks
, it is crucial to ensure the orderliness of the internal blocks.The current
blockLock + pollBlockLock
mechanism ensures the ordering of blocks withinwriteBlocks
. As a preliminary solution, convertingwriteBlocks
into a blocking queue seems feasible.When
writeBlocks
is not empty, the ordering can indeed be guaranteed. However, whenwriteBlocks
is empty, how can we obtain the minimumstartOffset
currently written towriteBlock
(or, equivalently, the maximum offset of the already written blocks)?Previously, due to the global
blockLock
, whenwriteBlocks
was empty, we could simply retrieve the information fromcurrentBlock
.Currently, without the global lock, the preliminary solution involves acquiring
blockLock + pollBlockLock
whenwriteBlocks
is empty.However, it is evident that this will introduce concurrency issues between step 4 and step 1 and 2 whenever
writeBlocks
is empty.How can we optimize this situation?
There are two approaches: eventual consistency and strict consistency.
eventual consistency:
When wroteBlocks is empty, we can directly calculate the offset based on the offset of the currently wroteBlock:
offset = wroteBlocks.startOffset() + WALUtil.alignLargeByBlockSize(wroteBlocks.blockBatchSize()) to update the
position accordingly.
If the current block is indeed the one with the largest startOffset, then updating the offset in this way poses no
issue.
However, if that's not the case, for example, if block4 and block5 have already been written before, and now
block3 is being written (in an out-of-order manner), after block3 is written, writingBlock becomes empty, and at this
point, WindowCoreData's startOffset might be incorrectly updated to offset1. The correct update should be to
offset2.
When would the update to the latest offset occur to maintain consistency?
The update to the latest offset will occur when the next IO operation writes a new block, and the update is
performed again at that time. This ensures that the
startOffset
reflects the most recent and accurate state of thewritten blocks.
strict consistency
In the poll operation, keep track of the maximum offset that has been written to the block, denoted as
maxWriteOffset. When updating, if writeBlocks is empty, it indicates that the current block has been fully written.
Therefore, the offset can be set to maxWriteOffset.
3. Locking Optimization:
Consider changing the current blocking lock acquisition to a try-lock mechanism. If the write thread successfully acquires the lock, it proceeds with its operation. If the write thread fails to acquire the lock, it means that the poll thread is currently processing, and the write thread can simply return without further action.
The text was updated successfully, but these errors were encountered: