mirror of
https://github.com/tailscale/tailscale.git
synced 2024-11-27 03:55:36 +00:00
93 lines
1.9 KiB
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()
|
||
|
}
|
||
|
}
|