mirror of
https://github.com/lukaszraczylo/graphql-monitoring-proxy.git
synced 2026-10-12 05:32:36 +00:00
fix(memory, connection): two data-race/counter defects
1) connection_pool.go: GetConnectionStats read lastRecoveryAttempt (a multi-word time.Time) without any lock while checkAndRecover wrote it under recoveryMutex -> data race on live admin API endpoints. Snapshot it under recoveryMutex (the writer's lock). Regression test TestGetConnectionStatsRace drives the read path concurrent with the locked write under -race. 2) cache/memory memory.go: Cache.Get expired path used an unconditional Delete + atomic decrement, so concurrent Gets on the same expired entry each decremented entryCount/memoryUsage -> permanent negative drift. Use LoadAndDelete-with-guard (as evictOldest/evictToFreeMemory already do) so only the goroutine that actually removes the entry decrements. Regression test TestMemoryCacheConcurrentGetOnExpiredEntry_CountersConsistent.
This commit is contained in:
Vendored
+8
-3
@@ -181,9 +181,14 @@ func (c *Cache) Get(key string) ([]byte, bool) {
|
||||
|
||||
cacheEntry := entry.(CacheEntry)
|
||||
if cacheEntry.ExpiresAt.Before(time.Now()) {
|
||||
c.entries.Delete(key)
|
||||
atomic.AddInt64(&c.entryCount, -1)
|
||||
atomic.AddInt64(&c.memoryUsage, -cacheEntry.MemorySize)
|
||||
// Guard the counter decrements with LoadAndDelete so concurrent Gets on
|
||||
// the same expired entry don't each decrement entryCount/memoryUsage
|
||||
// (the unconditional Delete here previously double-counted).
|
||||
if removed, ok := c.entries.LoadAndDelete(key); ok {
|
||||
removedEntry := removed.(CacheEntry)
|
||||
atomic.AddInt64(&c.entryCount, -1)
|
||||
atomic.AddInt64(&c.memoryUsage, -removedEntry.MemorySize)
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user