forked from TrueCloudLab/restic
Replace mount's per-file cache by a global LRU cache
This commit is contained in:
parent
d42c169458
commit
58719e1f47
6 changed files with 147 additions and 23 deletions
87
internal/fuse/blobcache.go
Normal file
87
internal/fuse/blobcache.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
package fuse
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/restic/restic/internal/debug"
|
||||
"github.com/restic/restic/internal/restic"
|
||||
|
||||
"github.com/hashicorp/golang-lru/simplelru"
|
||||
)
|
||||
|
||||
// Crude estimate of the overhead per blob: a SHA-256, a linked list node
|
||||
// and some pointers. See comment in blobCache.add.
|
||||
const cacheOverhead = len(restic.ID{}) + 64
|
||||
|
||||
// A blobCache is a fixed-size cache of blob contents.
|
||||
// It is safe for concurrent access.
|
||||
type blobCache struct {
|
||||
mu sync.Mutex
|
||||
c *simplelru.LRU
|
||||
|
||||
free, size int // Current and max capacity, in bytes.
|
||||
}
|
||||
|
||||
// Construct a blob cache that stores at most size bytes worth of blobs.
|
||||
func newBlobCache(size int) *blobCache {
|
||||
c := &blobCache{
|
||||
free: size,
|
||||
size: size,
|
||||
}
|
||||
|
||||
// NewLRU wants us to specify some max. number of entries, else it errors.
|
||||
// The actual maximum will be smaller than size/cacheOverhead, because we
|
||||
// evict entries (RemoveOldest in add) to maintain our size bound.
|
||||
maxEntries := size / cacheOverhead
|
||||
lru, err := simplelru.NewLRU(maxEntries, c.evict)
|
||||
if err != nil {
|
||||
panic(err) // Can only be maxEntries <= 0.
|
||||
}
|
||||
c.c = lru
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *blobCache) add(id restic.ID, blob []byte) {
|
||||
debug.Log("blobCache: add %v", id)
|
||||
|
||||
size := len(blob) + cacheOverhead
|
||||
if size > c.size {
|
||||
return
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
var key interface{} = id
|
||||
|
||||
if c.c.Contains(key) { // Doesn't update the recency list.
|
||||
return
|
||||
}
|
||||
|
||||
// This loop takes at most min(maxEntries, maxchunksize/cacheOverhead)
|
||||
// iterations.
|
||||
for size > c.free {
|
||||
c.c.RemoveOldest()
|
||||
}
|
||||
|
||||
c.c.Add(key, blob)
|
||||
c.free -= size
|
||||
}
|
||||
|
||||
func (c *blobCache) get(id restic.ID) ([]byte, bool) {
|
||||
c.mu.Lock()
|
||||
value, ok := c.c.Get(id)
|
||||
c.mu.Unlock()
|
||||
|
||||
debug.Log("blobCache: get %v, hit %v", id, ok)
|
||||
|
||||
blob, ok := value.([]byte)
|
||||
return blob, ok
|
||||
}
|
||||
|
||||
func (c *blobCache) evict(key, value interface{}) {
|
||||
blob := value.([]byte)
|
||||
debug.Log("blobCache: evict %v, %d bytes", key, len(blob))
|
||||
c.free += len(blob) + cacheOverhead
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue