perf(handler): eliminate read-lock upgrade in FileInfoCache.Get with approximate LRU

FileInfoCache.Get previously acquired TWO locks on every cache hit:
1. RLock → check existence + TTL
2. Release RLock → Lock → MoveToFront (LRU update) → Unlock

The MoveToFront on every hit forced a read-to-write lock upgrade,
creating contention under concurrent reads.

Apply approximate LRU: skip MoveToFront on Get (read) path entirely.
LRU position is only updated in Set (write) path. This is the same
pattern already used by internal/cache/file_cache.go.

Result: Get fast path reduced from 2 lock acquisitions to 1 RLock.
TTL-expired entries still use double-check locking for safe removal.
This commit is contained in:
xfy 2026-06-04 00:13:29 +08:00
parent 12caed5d4e
commit e44cfc7128

View File

@ -47,32 +47,20 @@ func (c *FileInfoCache) Get(filePath string) (os.FileInfo, bool) {
return nil, false
}
// 检查 TTL只读检查
if time.Since(entry.cachedAt) > fileInfoCacheTTL {
c.mu.RUnlock()
// 升级为写锁删除过期条目
c.mu.Lock()
// double-check可能已被其他请求删除或更新
if entry, ok := c.entries[filePath]; ok && time.Since(entry.cachedAt) > fileInfoCacheTTL {
c.lruList.Remove(entry.element)
if e, ok := c.entries[filePath]; ok && time.Since(e.cachedAt) > fileInfoCacheTTL {
c.lruList.Remove(e.element)
delete(c.entries, filePath)
}
c.mu.Unlock()
return nil, false
}
info := entry.info
c.mu.RUnlock()
// LRU 移动需要写锁
c.mu.Lock()
// double-check条目可能已被删除
if entry, ok := c.entries[filePath]; ok {
c.lruList.MoveToFront(entry.element)
c.mu.Unlock()
return entry.info, true
}
c.mu.Unlock()
return nil, false
return info, true
}
// Set 缓存 FileInfo