Files
schreifuchs.ch/internal/filesystem/cache.go
T
2026-03-02 22:38:28 +01:00

71 lines
1.4 KiB
Go

package filesystem
import (
"context"
"log/slog"
"sync/atomic"
"time"
"github.com/valkey-io/valkey-go"
)
const keyStat = "webdav:stat:"
// CachedClient wraps a FileSystem implementation with Valkey caching.
type CachedClient struct {
impl FS
cache valkey.Client
ttl time.Duration
hits atomic.Int64
misses atomic.Int64
readDirHits atomic.Int64
readHits atomic.Int64
}
// NewCachedClient creates a new CachedClient.
func NewCachedClient(impl FS, client valkey.Client, refreshtime time.Duration) *CachedClient {
c := &CachedClient{
impl: impl,
cache: client,
ttl: refreshtime * 5,
}
go func() {
t := time.NewTicker(refreshtime)
for {
<-t.C
c.revalidate(context.Background())
}
}()
return c
}
func (c *CachedClient) revalidate(ctx context.Context) {
go c.revalidateReadDir(ctx)
go c.revalidateRead(ctx)
go c.revalidateStat(ctx)
}
func (c *CachedClient) scanAndProcess(ctx context.Context, match string, process func(key string) error) {
cursor := uint64(0)
for {
res, err := c.cache.Do(ctx, c.cache.B().Scan().Cursor(cursor).Match(match).Build()).AsScanEntry()
if err != nil {
slog.Error("scan failed", "match", match, "err", err)
return
}
for _, key := range res.Elements {
if err := process(key); err != nil {
slog.Debug("process failed", "key", key, "err", err)
}
}
cursor = res.Cursor
if cursor == 0 {
break
}
}
}