fleetd #608: make anAlreadyCollectedTicketProducesNoNudge deterministic #611
Reference in New Issue
Block a user
Delete Branch "worker/fleetd-608-flaky-nudge-test-d0c2d1-3"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
fleetd #608:
MessageServiceTest.anAlreadyCollectedTicketProducesNoNudgewas flaky — it passed in isolation but failed on a full-suite run (expected: <false> but was: <true>at theagent.promptassertion), because it bet a 300ms backoff was wide enough for the test to collect the ticket beforeReplyPushLoop's scheduled tick fired. Under load that bet lost.Fix
ManualScheduler(src/test/java/dev/ltms/fleet/msg/ManualScheduler.java): aScheduledExecutorServicefake that records whatReplyPushLoopschedules and only runs it when the test callsrunDueTasks(). It implements only the two methodsReplyPushLoopactually calls (schedule(Runnable, long, TimeUnit)andshutdownNow()); everything else throwsUnsupportedOperationExceptionby design.wireWithManualScheduler(...)/ManualPushWiringalongside the existingwireWithPushLoop(...)inMessageServiceTest— the real-scheduler overloads are untouched, since other tests in the file rely on a real timer on purpose.anAlreadyCollectedTicketProducesNoNudgeto: send → wait for the ticket's terminal notification (via the existingsetAfterFinishAsyncTaskCompleteHookForTesthook, so it doesn't raceCompletableFuture.complete()'s own publish-then-run-dependents gap, fleetd #399) → collect the ticket → run the one pending tick explicitly withrunDueTasks()→ assert no nudge. NoThread.sleep, no backoff dependency.Verified
anAlreadyCollectedTicketProducesNoNudgewith backoff set to1(the most hostile value) still passes, in isolation.ReplyPushLoop.ticketCollectedto a no-op (so a collected ticket is never removed frompendingTickets) turns the test red with the same assertion message the original flake reported (a ticket the lead already polled must never be nudged ==> expected: <false> but was: <true>); reverted afterwards (cleangit diffonReplyPushLoop.java).mvn -o clean installruns fromfleetd/: all threeTests run: 1841, Failures: 0, Errors: 0, Skipped: 0,BUILD SUCCESS.Out of scope (reported, not fixed, per the ticket)
The ticket asked me to report — not fix — whether the file's other large
Thread.sleepbarriers have the same shape (betting on wall-clock ordering between a scheduled tick and a test action, so they can go red on correct code under load):Thread.sleep(100),severalAsyncTicketsFinishingTogetherProduceOneCoalescedNudge): same shape — it's part of the same "both tickets must land inside the 300ms backoff" bet as the fixed defect.Thread.sleep(100), same test): same shape — the second half of that same window.Thread.sleep(200), same test, the post-awaitNudgesettle check): different risk direction — it can only mask a bug (false green) under load, not go red on correct code.Thread.sleep(300),answeringAQuestionStopsFurtherNudgesAboutIt): not the same shape — the question is already closed (state settled) before the sleep starts, so there's nothing left to race.Thread.sleep(300),aPrunedTicketIsReclaimedFromThePushLoopNotLeakedForever): not the same shape — it followsawaitNudge, which already deterministically observed the nudge; the trailing sleep and assertion hold regardless of timing since no second ticket exists yet.