Skip to content

Replace cache lockers with production-ready sp_getapplock and SETNX implementations#4

Merged
jzills merged 3 commits into
developfrom
fix/cache-locker-improvements
May 3, 2026
Merged

Replace cache lockers with production-ready sp_getapplock and SETNX implementations#4
jzills merged 3 commits into
developfrom
fix/cache-locker-improvements

Conversation

@jzills

@jzills jzills commented May 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • Replaces the non-atomic DistributedCacheLocker (check-then-set race condition) with backend-specific lockers: SqlServerCacheLocker (sp_getapplock) and RedisCacheLocker (SET NX PX + compare-and-delete Lua scripts)
  • Removes the unnecessary SemaphoreSlimLocker from the memory cache — IMemoryCache reference semantics and ConcurrentDictionary already guarantee thread safety
  • Fixes a bug in IMemoryCacheExtensions.SetKey where the namespace tracking entry was written under the wrong cache key (individual key instead of the namespace key), preventing expiration options from ever being applied to the tracking dict

Changes

  • SqlServerCacheLocker: single sp_getapplock call with native SQL Server blocking up to LockTimeout; released via sp_releaseapplock on a held SqlConnection
  • RedisCacheLocker: atomic SET NX PX acquire + compare-and-delete release, both single Lua scripts; polls every 100 ms up to LockTimeout
  • NullCacheLock.IsAcquired now initializes to true (was false, silently skipping every WaitForLockThenAsync callback when used with NullCacheLocker)
  • MemoryActionCacheNullCacheLock / NullCacheLocker; SqlServerActionCacheFactory now injects IOptions<SqlServerCacheOptions> to extract the connection string
  • Deleted DistributedCacheLocker, DistributedCacheLock, SemaphoreSlimLocker, SemaphoreSlimLock

Test plan

  • All 359 unit tests pass on net8.0 and net10.0
  • SqlServerCacheLocker requires a live SQL Server instance to test locking behaviour end-to-end (covered by integration tests against a real DB)

jzills added 3 commits May 2, 2026 21:17
… lockers

SQL Server now uses sp_getapplock/sp_releaseapplock for true atomic session-level
locking, eliminating the non-atomic check-then-set race condition in the old
DistributedCacheLocker. Redis now uses SET NX PX + compare-and-delete Lua scripts
for atomic distributed locking. Memory cache drops the unnecessary SemaphoreSlimLocker
since IMemoryCache reference semantics and ConcurrentDictionary already guarantee
thread safety. Also fixes a bug in IMemoryCacheExtensions.SetKey where the namespace
tracking entry was written to the wrong cache key (individual key instead of namespace).
@jzills jzills merged commit f0eb166 into develop May 3, 2026
4 checks passed
@jzills jzills deleted the fix/cache-locker-improvements branch May 3, 2026 02:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant