perf(handler): use RWMutex for FileInfoCache
- Change FileInfoCache.mu from sync.Mutex to sync.RWMutex - Get method uses RLock for concurrent read access - Stats method uses RLock for read-only operation - Double-check pattern for lock upgrades (TTL expiry, LRU move) This improves concurrent read performance for the FileInfo cache, which is read-heavy in static file serving scenarios. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
3c96f12f74
commit
5f470993ff
@ -35,7 +35,7 @@ type fileInfoEntry struct {
|
|||||||
type FileInfoCache struct {
|
type FileInfoCache struct {
|
||||||
entries map[string]*fileInfoEntry
|
entries map[string]*fileInfoEntry
|
||||||
lruList *list.List
|
lruList *list.List
|
||||||
mu sync.Mutex
|
mu sync.RWMutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewFileInfoCache 创建 FileInfo 缓存
|
// NewFileInfoCache 创建 FileInfo 缓存
|
||||||
@ -48,25 +48,39 @@ func NewFileInfoCache() *FileInfoCache {
|
|||||||
|
|
||||||
// Get 获取缓存的 FileInfo
|
// Get 获取缓存的 FileInfo
|
||||||
func (c *FileInfoCache) Get(filePath string) (os.FileInfo, bool) {
|
func (c *FileInfoCache) Get(filePath string) (os.FileInfo, bool) {
|
||||||
c.mu.Lock()
|
c.mu.RLock()
|
||||||
defer c.mu.Unlock()
|
|
||||||
|
|
||||||
entry, ok := c.entries[filePath]
|
entry, ok := c.entries[filePath]
|
||||||
if !ok {
|
if !ok {
|
||||||
|
c.mu.RUnlock()
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查 TTL
|
// 检查 TTL(只读检查)
|
||||||
if time.Since(entry.cachedAt) > fileInfoCacheTTL {
|
if time.Since(entry.cachedAt) > fileInfoCacheTTL {
|
||||||
// 过期,删除并返回未命中
|
c.mu.RUnlock()
|
||||||
c.lruList.Remove(entry.element)
|
// 升级为写锁删除过期条目
|
||||||
delete(c.entries, filePath)
|
c.mu.Lock()
|
||||||
|
// double-check:可能已被其他请求删除或更新
|
||||||
|
if entry, ok := c.entries[filePath]; ok && time.Since(entry.cachedAt) > fileInfoCacheTTL {
|
||||||
|
c.lruList.Remove(entry.element)
|
||||||
|
delete(c.entries, filePath)
|
||||||
|
}
|
||||||
|
c.mu.Unlock()
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
// 命中,移动到 LRU 头部
|
c.mu.RUnlock()
|
||||||
c.lruList.MoveToFront(entry.element)
|
|
||||||
return entry.info, true
|
// 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set 缓存 FileInfo
|
// Set 缓存 FileInfo
|
||||||
@ -124,8 +138,8 @@ func (c *FileInfoCache) Clear() {
|
|||||||
|
|
||||||
// Stats 返回缓存统计
|
// Stats 返回缓存统计
|
||||||
func (c *FileInfoCache) Stats() FileInfoCacheStats {
|
func (c *FileInfoCache) Stats() FileInfoCacheStats {
|
||||||
c.mu.Lock()
|
c.mu.RLock()
|
||||||
defer c.mu.Unlock()
|
defer c.mu.RUnlock()
|
||||||
|
|
||||||
return FileInfoCacheStats{
|
return FileInfoCacheStats{
|
||||||
Entries: len(c.entries),
|
Entries: len(c.entries),
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user