Make Redis dispose-vs-connect guard reach the seam deterministically (#1332) - #1392
Conversation
Adversarial Code ReviewScope: single changed file CRITICAL
HIGH
MEDIUM
LOW
Analysis notes (verified correct, no action)
Bot Comments Addressed
Summary0 CRITICAL / 0 HIGH / 0 MEDIUM / 0 LOW. Not merge-blocking. Test-only change; 20/20 targeted runs green, full Taskdeck.Api.Tests 2060/2060 pass, negative proof confirmed against pre-#1189 semantics. |
There was a problem hiding this comment.
Code Review
This pull request refactors the Dispose_IsNotSerialized_BehindAnInFlightConnect test in RedisCacheServiceTests.cs by replacing Task.Run with a dedicated background thread. This ensures the connecting worker reaches the connection seam deterministically, resolving test flakiness caused by thread pool saturation under heavy load. There are no review comments, so I have no additional feedback to provide.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
Adversarial Review — Round 2 (consolidated)Two independent adversarial reviews were run against this PR with distinct lenses:
Attack surface traced (all refuted against
|
Adversarial Review — Fixes Applied (Round 2)
Fix detail: all four events converted to All findings addressed. CI status: PENDING (fresh push, checks re-running). |
Summary
RedisCacheServiceTests.Dispose_IsNotSerialized_BehindAnInFlightConnectflaked under full-API-suite load on Windows withExpected connectEntered.Wait(TimeSpan.FromSeconds(5)) to be True ... but found False. The connecting actor was launched withTask.Run(...), which queues a thread-pool work item. Under a saturated pool the injection throttle delayed running that item, so the worker never reachedOnBeforeConnectbefore the wait window elapsed — a scheduling artifact, not a real regression.This is a test-only change. No production
RedisCacheServicebehavior is touched (the production file has an empty diff).Mechanism (deterministic, not a bigger timeout)
Replaced
Task.Run(...)with a dedicated, named backgroundThread(redis-connector,IsBackground = true) that is created and scheduled by the OS immediately, independent of thread-pool saturation. Coordination is unchanged and explicit:GetConnectioninvokesOnBeforeConnectsynchronously on the connecting thread before anyawait, so the existingconnectEntered/releaseConnecthandshake is a real rendezvous.Joined (bounded, 5s) instead of awaited, and its result/exception are captured on fields so the post-dispose degrade assertions still hold.The 5s wait was not increased; determinism comes from the dedicated OS thread, not from a wider margin. The
disposeElapsed < 500msconcurrency assertion is preserved verbatim.Negative proof (still fails against pre-#1189 semantics)
Temporarily reverted the production
GetConnectionto hold_connectionLockacross the blocking connect (the pre-#1189 lock-around-connect bug), rebuilt, and ran the test:With the fixed production code the dedicated connecting thread releases
_connectionLockbefore the connect, soDispose()acquires it in microseconds. The revert was restored before committing (production diff is empty); the new mechanism did not weaken the guard.Verification
dotnet build backend/Taskdeck.sln -c Release— clean (0 errors).--filter "FullyQualifiedName~RedisCacheServiceTests"(8/8 tests each run).Taskdeck.Api.Tests— 2060 passed, 0 failed, 0 skipped.Closes #1332