tailscale/tailfs/tailfsimpl/compositedav/stat_cache.go

93 lines
1.9 KiB
Go

// Copyright (c) Tailscale Inc & AUTHORS
// SPDX-License-Identifier: BSD-3-Clause
package compositedav
import (
"sync"
"time"
"github.com/jellydator/ttlcache/v3"
)
// StatCache provides a cache for directory listings and file metadata.
// Especially when used from the command-line, mapped WebDAV drives can
// generate repetitive requests for the same file metadata. This cache helps
// reduce the number of round-trips to the WebDAV server for such requests.
// This is similar to the DirectoryCacheLifetime setting of Windows' built-in
// SMB client, see
// https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-7/ff686200(v=ws.10)
type StatCache struct {
TTL time.Duration
// mu guards the below values.
mu sync.Mutex
cachesByDepthAndPath map[int]*ttlcache.Cache[string, []byte]
}
func (c *StatCache) get(name string, depth int) []byte {
if c == nil {
return nil
}
c.mu.Lock()
defer c.mu.Unlock()
if c.cachesByDepthAndPath == nil {
return nil
}
cache := c.cachesByDepthAndPath[depth]
if cache == nil {
return nil
}
item := cache.Get(name)
if item == nil {
return nil
}
return item.Value()
}
func (c *StatCache) set(name string, depth int, value []byte) {
if c == nil {
return
}
c.mu.Lock()
defer c.mu.Unlock()
if c.cachesByDepthAndPath == nil {
c.cachesByDepthAndPath = make(map[int]*ttlcache.Cache[string, []byte])
}
cache := c.cachesByDepthAndPath[depth]
if cache == nil {
cache = ttlcache.New(
ttlcache.WithTTL[string, []byte](c.TTL),
)
go cache.Start()
c.cachesByDepthAndPath[depth] = cache
}
cache.Set(name, value, ttlcache.DefaultTTL)
}
func (c *StatCache) invalidate() {
if c == nil {
return
}
c.mu.Lock()
defer c.mu.Unlock()
for _, cache := range c.cachesByDepthAndPath {
cache.DeleteAll()
}
}
func (c *StatCache) stop() {
c.mu.Lock()
defer c.mu.Unlock()
for _, cache := range c.cachesByDepthAndPath {
cache.Stop()
}
}