Don't log caller cancellation as a distributed lock error (#608) - #626
Open
qjustfeelitp wants to merge 1 commit into
Open
Conversation
…ures#608) When the caller's own CancellationToken (eg: HttpContext.RequestAborted) is canceled while the distributed lock accessor is waiting to acquire the lock, Medallion.Threading surfaces an OperationCanceledException driven by that token. FusionCache caught it in the generic exception handler and logged it as "[DL] acquiring the DISTRIBUTED LOCK has thrown an exception" at the configured error level, even though nothing actually went wrong with the distributed locker. The factory path already rethrows caller cancellation before the generic catch; the distributed lock accessor was the only place that didn't. Add the same guard to the sync and async accessors so caller cancellation flows to the caller instead of being logged as a locker error. A genuine locker fault still falls through to the existing handler unchanged. Adds sync + async regression tests using a fake locker that reproduces the Medallion behaviour (cancels the caller token, then throws OCE driven by it): they assert the cancellation surfaces to the caller and the "[DL] ... has thrown an exception" message is not logged. Fixes ZiggyCreatures#608
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.
Summary
Fixes #608.
When the caller's own
CancellationToken(e.g.HttpContext.RequestAborted) is canceled while the distributed lock accessor is waiting to acquire the lock,Medallion.Threadingsurfaces anOperationCanceledException/TaskCanceledExceptiondriven by that token. FusionCache caught it in the genericcatch (Exception)handler ofDistributedLockerAccessor.AcquireLock/AcquireLockAsyncand logged it as:at the configured
DistributedLockerErrorsLogLevel(Error by default) — even though nothing actually went wrong with the distributed locker. It was simply the caller walking away.Root cause
Medallion.Threading'sBusyWaitHelperdistinguishes the two outcomes by design (DistributedLock.Core/Internal/BusyWaitHelper.cs):null(the lock could not be taken withintimeout);OperationCanceledExceptiondriven by the caller's token.So an
OperationCanceledExceptionwhose cause is the caller's own token is not a locker failure — it is normal caller cancellation. The factory path in FusionCache already treats caller cancellation this way (it rethrows theOperationCanceledExceptionbefore reaching the generic catch). The distributed lock accessor was the one place that didn't, so the same logical event was logged as an error on one path and handled cleanly on the other.Fix
Add the same guard the factory path uses to both the sync and async distributed lock accessors: when the exception is an
OperationCanceledExceptionand the caller's token is the one that was canceled, rethrow it so the cancellation flows to the caller instead of being logged as a distributed locker error. A genuine locker fault (or a cancellation not tied to the caller's token) still falls through to the existingcatch (Exception)handler and is logged/handled exactly as before.Tests
Adds
DistributedLockerTestswith a sync and an async test. Each installs a fakeIFusionCacheDistributedLockerthat reproduces the Medallion behaviour — it cancels the caller's token while "waiting" and then throws anOperationCanceledExceptiondriven by that token — and asserts that:OperationCanceledException, and[DL] acquiring the DISTRIBUTED LOCK has thrown an exceptionmessage is not logged.Verified as genuine regression tests (A/B against this branch):
[DL]error logged?Notes on #608
The original report attributed the flood of errors to a shared
CacheKeyPrefixacross instances. That is a red herring (as already noted in the thread): the errors are simply many concurrent requests whose callers get canceled while parked on the distributed lock during heavy contention (in the reporter's case, 50+ app instances against one shared cache under a UI-test suite). This change removes the misleading error logging at its source without touching keys or prefixes.