Repository navigation
Conversation
… keep the cleanup action counters consistent offer() put the action into the queue first and counted it as accepted afterwards (OpenIdentityPlatform#359). poll() drains the queue without the monitor that offer() holds, so in between it could perform the action and count it as performed: for that moment performed + errors could exceed the accepted count. offer() now checks the remaining capacity of the queue, counts the action as accepted and only then adds it. Only offer() adds to the queue, and it holds the monitor while it does, so the checked capacity is still there when the action is added. Both counters only ever grow. Two paths took accepted actions out of the queue without counting them (OpenIdentityPlatform#367), so the performed and error counts never caught up with the accepted count again: - poll() caught only PersistitException around each action. A RuntimeException left poll(), was neither counted nor posted to the AlertMonitor, and the rest of the drained work list was lost. It is now handled like a PersistitException. - clear() discarded the queued actions. It now counts them, and CleanupManagerMXBean reports the count as getDiscardedCount(). Tests: - testActionAcceptedBeforePerformed parks offer() at a new CLEANUP_OFFER_A sequence point once the action is in the queue, runs poll() on another thread and checks that the action already counts as accepted when it is performed. - testRuntimeExceptionCountedAsError checks that an action failing with a RuntimeException counts as an error and the next action is performed. - testClearCountsDiscardedActions checks that the accepted count equals the performed, error and discarded counts after clear(). - testOverflow and the tests above use a CleanupManager that is never started, so only the test changes its counters; testOverflow checks the exact accepted and refused counts. Fixes OpenIdentityPlatform#359 Fixes OpenIdentityPlatform#367
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #359
Fixes #367
Problem
CleanupManager.offer()put the action into the queue first and counted it as accepted afterwards.poll()drains the queue without the monitor thatoffer()holds, so in between it could perform the action and count it as performed. For that momentgetPerformedCount() + getErrorCount()could exceedgetAcceptedCount(), or equal it while an accepted action was still pending. The counter-based waits in the tests rely on that not happening.poll()caught onlyPersistitExceptionaround each action. ARuntimeExceptionleftpoll()(IOTaskRunnable.run()only logs it), was neither counted as an error nor posted to theAlertMonitor, and the rest of the drained work list (up to 2,000 actions) was lost.clear()discarded the queued actions without counting them.Fix
offer(): checks theremainingCapacity()of the queue, counts the action as accepted and only then adds it. Onlyoffer()adds to the queue, and it holds the monitor while it does, so the checked capacity is still there when the action is added. Counting first and taking the count back on refusal would make the accepted count briefly too high and let it go down; with the capacity check both the accepted and the refused count stay exact and only ever grow.poll(): aRuntimeExceptionfrom an action is handled like aPersistitException: the action counts as an error, the alert is posted, and the rest of the work list is performed.clear(): drains the queue into a list and counts the discarded actions;CleanupManagerMXBeanreports them as the newgetDiscardedCount().drainTo()hands each action either toclear()or to a concurrentpoll(), never to both.CLEANUP_OFFER_A/B/Csequencer locations andCLEANUP_OFFER_SCHEDULEfor the test of the race;offer()passesCLEANUP_OFFER_Aonce the action is in the queue.Tests
testActionAcceptedBeforePerformed: parks the test'soffer()atCLEANUP_OFFER_Aonce the action is in the queue (aConditionlimits the location to the test's thread), runspoll()on another thread and checks that the action already counts as accepted when it is performed.testRuntimeExceptionCountedAsError: an action failing with aRuntimeExceptioncounts as an error, the action after it is performed, andgetLastException()returns the exception.testClearCountsDiscardedActions: after apoll()and aclear(), the accepted count equals the performed, error and discarded counts.testOverflow: checks the exact accepted and refused counts instead of>= 0.CleanupManagerthat is never started: noCLEANUP_MANAGERthread polls it and no Persistit thread offers to it, so only the test changes its counters.testActionAcceptedBeforePerformed,expected:<1> but was:<0>;testOverflow,expected:<100000> but was:<200000>;PersistitExceptioncaught:testRuntimeExceptionCountedAsErrorfails with theIllegalStateExceptionout ofpoll();clear():testClearCountsDiscardedActions,expected:<7> but was:<0>.persistit/coresuite on JDK 27 at a4b1baa: 603 tests, 0 failures, 0 errors, 5 skipped.Note
waitForCleanupManager()in #360 waits until the performed and error counts add up to the accepted count. No test callsclear()before such a wait, so #360 needs no change for the new discarded count.