[fix][broker] Prevent stale service unit callbacks from dropping active lookup and cleanup jobs - #26146
Conversation
db37eac to
ca16bea
Compare
…futures ServiceUnitStateChannel keeps deferred owner lookups and inactive broker cleanup jobs in maps keyed by service unit or broker. Completion callbacks removed entries by key, so a stale completion could remove a newer future installed after a retry. Guard getOwnerRequests and cleanupJobs removals with the captured future. Also guard broker-creation cleanup cancellation so a delayed health-check result cannot cancel a newer cleanup job. Add tests that reproduce stale get-owner and cleanup-job completion removing newer futures.
ca16bea to
1605aee
Compare
There was a problem hiding this comment.
The intention was to complete whatever earliest finished lookup asap and unblock all pending lookup requests.
The problem statement seems like the dedup logic didnt happen correctly in some cases.
These futures can be replaced when a lookup is retried, when an Owned event completes a waiting lookup, or when cleanup is rescheduled.
Can you explain the order here? How can the first lookup future be replaced? Shouldn't the later lookup be deduped?
Do we have any case that the lookup dedupe map does not get cleaned up because the completed future is not the one in the map? In this case, this PR will keep the original lookup request forever, which will be a bigger issue.
|
Thanks for the question. While With the previous unconditional This cannot retain I agree that the current test's direct |
At T4, what's the issue when it can return the ownership early? Isnt it better it doesn't need to wait til T5? Like I mentioned, the intention was to "early return any ownership". If F1 contains valid ownership, without exception, it can save time til T5? Also, |
|
Thanks for the questions. I think the key distinction is between the lifecycle you described and the lifecycle in the original implementation. My understanding of the intended lifecycle is:
Under that lifecycle, F1 still owns the map entry when its cleanup runs, so However, the original event handlers use a different lifecycle.
With the previous key-only removal, T4 changes the map from F2 to empty. It does not complete F2 or transfer the result held by F1 to F2. The handler at T5 therefore cannot find F2, and a deferred F2 reaches only its timeout fallback. If F1 contains a valid owner, callers already holding F1 return that owner when F1 completes at T2; the conditional removal does not make them wait until T5. If F2 can independently resolve an owner from the current The remove-before-complete behavior is also important for no-owner results. For example, Therefore, the gap is that the original cleanup assumes the map entry still belongs to F1, while the original event-handler behavior does not guarantee that assumption. |
|
Thank you for sharing the racing case. The shared scenario removed F1 twice at T2 and T4. I think the removal should only happen once. I agree with this safe-guarded removal direction. |
…ve lookup and cleanup jobs (apache#26146) (cherry picked from commit 32146ae)
…ve lookup and cleanup jobs (apache#26146) (cherry picked from commit 32146ae)
…ve lookup and cleanup jobs (apache#26146) (cherry picked from commit 32146ae)
…ve lookup and cleanup jobs (apache#26146) (cherry picked from commit 32146ae)
Motivation
ServiceUnitStateChannelImplstores pending owner lookups and inactive-broker cleanup jobs in maps keyed by service unit or broker. These futures can be replaced when a lookup is retried, when an Owned event completes a waiting lookup, or when cleanup is rescheduled.Some completion and cancellation paths removed entries by key only. A stale callback could remove or cancel a newer future for the same key, causing owner lookups to wait for timeout or delaying inactive-broker ownership cleanup.
Modifications
getOwnerRequestsremovals with the captured request future.cleanupJobsremovals with the captured cleanup future.Verifying this change
./gradlew :pulsar-broker:test --tests org.apache.pulsar.broker.loadbalance.extensions.channel.ServiceUnitStateChannelTest.testCompletedGetOwnerRequestDoesNotRemoveNewRequest --tests org.apache.pulsar.broker.loadbalance.extensions.channel.ServiceUnitStateChannelTest.testSkippedEventDoesNotRemoveNewGetOwnerRequest --tests org.apache.pulsar.broker.loadbalance.extensions.channel.ServiceUnitStateChannelTest.testCompletedCleanupJobDoesNotRemoveNewCleanupJob --tests org.apache.pulsar.broker.loadbalance.extensions.channel.ServiceUnitStateChannelTest.handleBrokerCreationEventDoesNotCancelNewCleanupJobTest./gradlew :pulsar-broker:checkstyleMain :pulsar-broker:checkstyleTestgit diff --check