added stat cache
This commit is contained in:
parent
10512e5738
commit
675bc4d125
2 changed files with 100 additions and 25 deletions
61
pkg/fs/fs.go
61
pkg/fs/fs.go
|
|
@ -22,18 +22,20 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type FS struct {
|
type FS struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
log logr.Logger
|
log logr.Logger
|
||||||
NoCache http.Handler
|
NoCache http.Handler
|
||||||
src string
|
src string
|
||||||
dst string
|
dst string
|
||||||
mdf *os.File
|
statCancel func()
|
||||||
jenc *json.Encoder
|
flushCancel func()
|
||||||
dc *DirCache
|
mdf *os.File
|
||||||
q *Quota
|
jenc *json.Encoder
|
||||||
md MetadataMap
|
dc *DirCache
|
||||||
pm map[string]func()
|
sc *StatCache
|
||||||
cancel func()
|
q *Quota
|
||||||
|
md MetadataMap
|
||||||
|
pm map[string]func()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFS(max int64, src, dst, metadata string, log logr.Logger) (fs *FS, err error) {
|
func NewFS(max int64, src, dst, metadata string, log logr.Logger) (fs *FS, err error) {
|
||||||
|
|
@ -53,16 +55,21 @@ func NewFS(max int64, src, dst, metadata string, log logr.Logger) (fs *FS, err e
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
statCtx, statCancel := context.WithCancel(context.Background())
|
||||||
|
flushCtx, flushCancel := context.WithCancel(context.Background())
|
||||||
fs = &FS{
|
fs = &FS{
|
||||||
log: log,
|
log: log,
|
||||||
NoCache: http.FileServer(http.Dir(src)),
|
NoCache: http.FileServer(http.Dir(src)),
|
||||||
src: src,
|
src: src,
|
||||||
dst: dst,
|
dst: dst,
|
||||||
mdf: mdf,
|
mdf: mdf,
|
||||||
dc: NewDirCache(),
|
statCancel: statCancel,
|
||||||
md: make(MetadataMap),
|
flushCancel: flushCancel,
|
||||||
pm: make(map[string]func()),
|
dc: NewDirCache(),
|
||||||
jenc: json.NewEncoder(mdf),
|
sc: NewStatCache(statCtx),
|
||||||
|
md: make(MetadataMap),
|
||||||
|
pm: make(map[string]func()),
|
||||||
|
jenc: json.NewEncoder(mdf),
|
||||||
}
|
}
|
||||||
fs.q, err = NewQuota(max, fs, fs.log.WithName("quota"))
|
fs.q, err = NewQuota(max, fs, fs.log.WithName("quota"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -74,9 +81,7 @@ func NewFS(max int64, src, dst, metadata string, log logr.Logger) (fs *FS, err e
|
||||||
}
|
}
|
||||||
fs.initMetadata()
|
fs.initMetadata()
|
||||||
fs.q.Init()
|
fs.q.Init()
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
go fs.flusher(flushCtx)
|
||||||
fs.cancel = cancel
|
|
||||||
go fs.flusher(ctx)
|
|
||||||
return fs, err
|
return fs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -110,8 +115,13 @@ func (fs *FS) Stat(name string) (fi stdfs.FileInfo, err error) {
|
||||||
|
|
||||||
func (fs *FS) StatWithOffline(name string) (fi stdfs.FileInfo, offline bool, err error) {
|
func (fs *FS) StatWithOffline(name string) (fi stdfs.FileInfo, offline bool, err error) {
|
||||||
sp, dp := fs.paths(name)
|
sp, dp := fs.paths(name)
|
||||||
|
fi, err = fs.sc.Get(name)
|
||||||
|
if err == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
fi, err = os.Stat(sp)
|
fi, err = os.Stat(sp)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
fs.sc.Set(name, fi)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fi, err = os.Stat(dp)
|
fi, err = os.Stat(dp)
|
||||||
|
|
@ -347,7 +357,8 @@ func (fs *FS) flushMetadata() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *FS) Close() {
|
func (fs *FS) Close() {
|
||||||
fs.cancel()
|
fs.flushCancel()
|
||||||
|
fs.statCancel()
|
||||||
for _, cancel := range fs.pm {
|
for _, cancel := range fs.pm {
|
||||||
cancel()
|
cancel()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
64
pkg/fs/stat.go
Normal file
64
pkg/fs/stat.go
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
// Copyright (C) 2022 Marius Schellenberger
|
||||||
|
|
||||||
|
package fs
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const statExpiry = time.Minute * 5
|
||||||
|
|
||||||
|
type statCacheEntry struct {
|
||||||
|
mtime time.Time
|
||||||
|
fi os.FileInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
type StatCache struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
ctx context.Context
|
||||||
|
m map[string]*statCacheEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewStatCache(ctx context.Context) *StatCache {
|
||||||
|
sc := &StatCache{ctx: ctx, m: make(map[string]*statCacheEntry)}
|
||||||
|
go sc.cleanup()
|
||||||
|
return sc
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *StatCache) cleanup() {
|
||||||
|
t := time.NewTicker(statExpiry)
|
||||||
|
defer t.Stop()
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-sc.ctx.Done():
|
||||||
|
return
|
||||||
|
case <-t.C:
|
||||||
|
}
|
||||||
|
sc.mu.Lock()
|
||||||
|
now := time.Now()
|
||||||
|
for k, sce := range sc.m {
|
||||||
|
if now.After(sce.mtime) {
|
||||||
|
delete(sc.m, k)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sc.mu.Unlock()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *StatCache) Get(name string) (os.FileInfo, error) {
|
||||||
|
sc.mu.RLock()
|
||||||
|
defer sc.mu.RUnlock()
|
||||||
|
if sce, ok := sc.m[name]; ok {
|
||||||
|
return sce.fi, nil
|
||||||
|
}
|
||||||
|
return nil, NoCacheEntry
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sc *StatCache) Set(name string, fi os.FileInfo) {
|
||||||
|
sc.mu.Lock()
|
||||||
|
defer sc.mu.Unlock()
|
||||||
|
sc.m[name] = &statCacheEntry{time.Now().Add(statExpiry), fi}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue