[fix][ml] Track all pending read callbacks for timeouts - #26081
[fix][ml] Track all pending read callbacks for timeouts#26081Technoboy- wants to merge 3 commits into
Conversation
|
I think the current priority-queue approach can still retain too much state after reads complete. The wrapper is added before A bucketed timeout structure may be a better fit here: group reads by timeout bucket, and keep an inner map from // bucketId -> (readOpCount -> callback wrapper)
private final ConcurrentLongHashMap<ConcurrentLongHashMap<ReadEntryCallbackWrapper>> readTimeoutBuckets =
ConcurrentLongHashMap.<ConcurrentLongHashMap<ReadEntryCallbackWrapper>>newBuilder().build();
static final class ReadEntryCallbackWrapper implements ReadEntryCallback, ReadEntriesCallback {
volatile ConcurrentLongHashMap<ReadEntryCallbackWrapper> timeoutBucket;
} |
@void-ptr974 That's correct that it's a trade off. However, one detail is that For the queue based solution, the eviction of all completed entries is simply about scanning the queue and adding the entries back to the queue until the end of the queue has been reached. Adding new entries during this operation would need to be blocked in the simplest solution. IIRC, the scanning performance is insanely fast with org.jctools.queues.MpscUnboundedArrayQueue. The maintenance and expiration would be handled on a single thread. When using For the MpscUnboundedArrayQueue solution, it would be useful to keep a counter of the queue size and the count of completed entries in the queue. There could be a threshold when the maintenance operation is run. Let's say when the queue holds more than 25% of completed entries, the operation could run. |
|
@lhotari Thanks, that makes sense. I agree that a shared MPSC-based tracker with a single maintenance/expiration owner sounds like a good direction, since it keeps timeout handling centralized and makes the ManagedLedger logic easier to maintain. |
|
Hi @Technoboy- — gentle ping on this one. There are still 3 comments from my review that haven't been picked up:
No rush if you are busy — I mostly want to make sure it is not blocked on something I said, or waiting on an answer from me. If any of it is unclear or you disagree, say so and I will take another look; if you would rather someone else carried it forward, that is fine too. |
updated. |
Motivation
When managed ledger read-entry timeout is enabled,
ManagedLedgerImplonly keeps the most recentReadEntryCallbackWrapperinlastReadCallback. If multiple reads are pending and an older read hangs, a newer read can overwrite that callback, so the older operation is no longer checked bycheckReadTimeout().That can leave a cursor read pending indefinitely and block follow-up cursor operations such as reset/mark-delete progress.
Modifications
lastReadCallbackwith a pending-read callback map keyed by read operation id.Verifying this change
./gradlew :managed-ledger:test --tests org.apache.bookkeeper.mledger.impl.ManagedLedgerTest.testManagedLedgerWithReadEntryTimeOut --tests org.apache.bookkeeper.mledger.impl.ManagedLedgerTest.testManagedLedgerWithConcurrentReadEntryTimeOut