mirror of
https://github.com/lukaszraczylo/traefikoidc.git
synced 2026-06-05 22:44:17 +00:00
fix: remove write-lock convoy in getLocal + fix mutateState CAS bug
UniversalCache.getLocal(): when a cached token expires, the RLock fast path (line 385-398) previously fell through to c.mu.Lock() (write lock). Under Yaegi, the write-lock holder takes 10-100ms for LRU manipulation, and Go's RWMutex writer-priority blocks ALL new RLock callers. A single expired-token event turned every concurrent request from read-parallel into write-serialized — the convoy that produced the 737-goroutine pileup at 0x400275a608 (pprof captured at /tmp/traefik-spike-1779663149). Fix: return (nil, false) immediately on expiry for Token/JWK/Session cache types. The periodic cleanup goroutine handles eviction. Write lock is never taken on the read path for these cache types. refreshAttemptTracker.mutateState(): the CAS loop used t.state.CompareAndSwap(t.state.Load(), next) — a second Load that can see a different value from a concurrent writer, silently overwriting their update. Fixed to CompareAndSwap(cur, next) using the snapshot we computed the mutation from.
This commit is contained in:
@@ -498,7 +498,7 @@ func (t *refreshAttemptTracker) mutateState(mutate func(cur *attemptState) *atte
|
||||
if next == nil {
|
||||
return cur
|
||||
}
|
||||
if t.state.CompareAndSwap(t.state.Load(), next) {
|
||||
if t.state.CompareAndSwap(cur, next) {
|
||||
return next
|
||||
}
|
||||
}
|
||||
|
||||
+10
-2
@@ -396,8 +396,16 @@ func (c *UniversalCache) getLocal(key string) (interface{}, bool) {
|
||||
return value, true
|
||||
}
|
||||
c.mu.RUnlock()
|
||||
// Expired — fall through to the write-locked slow path below to
|
||||
// remove the entry under exclusive access.
|
||||
// Expired — return miss immediately. The periodic cleanup goroutine
|
||||
// will evict the stale entry. NEVER fall through to the write-locked
|
||||
// slow path for Token/JWK/Session caches: under Yaegi the write Lock
|
||||
// at line 403 costs 10-100ms per acquisition, and Go's RWMutex
|
||||
// writer-priority semantics block ALL new RLock callers while a Lock
|
||||
// is pending. A single expired-token event turns every concurrent
|
||||
// request from read-parallel into write-serialized — the exact
|
||||
// convoy that produced the 737-goroutine pileup at 0x400275a608.
|
||||
atomic.AddInt64(&c.misses, 1)
|
||||
return nil, false
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
|
||||
Reference in New Issue
Block a user