From e1dfaf5d87098a533363bcd1de0611bdefa253c4 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sun, 24 Sep 2017 22:50:35 +0200 Subject: [PATCH] cache: Allow proactive caching of tree packs This commit adds a function to the cache which can decide to proactively load the complete pack file and store it in the cache. This is helpful for pack files containing only tree blobs, as it is likely that the same file is accessed again in the future. --- internal/cache/backend.go | 60 +++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/internal/cache/backend.go b/internal/cache/backend.go index 06926bf59..05129a3a2 100644 --- a/internal/cache/backend.go +++ b/internal/cache/backend.go @@ -82,6 +82,25 @@ var autoCacheFiles = map[restic.FileType]bool{ restic.SnapshotFile: true, } +func (b *Backend) cacheFile(ctx context.Context, h restic.Handle) error { + rd, err := b.Backend.Load(ctx, h, 0, 0) + if err != nil { + return err + } + + if err = b.Cache.Save(h, rd); err != nil { + return err + } + + if err = rd.Close(); err != nil { + // try to remove from the cache, ignore errors + _ = b.Cache.Remove(h) + return err + } + + return nil +} + // Load loads a file from the cache or the backend. func (b *Backend) Load(ctx context.Context, h restic.Handle, length int, offset int64) (io.ReadCloser, error) { if b.Cache.Has(h) { @@ -91,39 +110,36 @@ func (b *Backend) Load(ctx context.Context, h restic.Handle, length int, offset // partial file requested if offset != 0 || length != 0 { + if b.Cache.PerformReadahead(h) { + debug.Log("performing readahead for %v", h) + err := b.cacheFile(ctx, h) + if err == nil { + return b.Cache.Load(h, length, offset) + } + + debug.Log("error caching %v: %v", h, err) + } + debug.Log("Load(%v, %v, %v): partial file requested, delegating to backend", h, length, offset) return b.Backend.Load(ctx, h, length, offset) } - rd, err := b.Backend.Load(ctx, h, length, offset) - if err != nil { - if b.Backend.IsNotExist(err) { - // try to remove from the cache, ignore errors - _ = b.Cache.Remove(h) - } - - return nil, err - } - + // if we don't automatically cache this file type, fall back to the backend if _, ok := autoCacheFiles[h.Type]; !ok { - return rd, nil + debug.Log("Load(%v, %v, %v): delegating to backend", h, length, offset) + return b.Backend.Load(ctx, h, length, offset) } debug.Log("auto-store %v in the cache", h) + err := b.cacheFile(ctx, h) - // cache the file, then return cached copy - if err = b.Cache.Save(h, rd); err != nil { - return nil, err + if err == nil { + // load the cached version + return b.Cache.Load(h, 0, 0) } - if err = rd.Close(); err != nil { - // try to remove from the cache, ignore errors - _ = b.Cache.Remove(h) - return nil, err - } - - // load from the cache and save in the backend - return b.Cache.Load(h, 0, 0) + debug.Log("error caching %v: %v, falling back to backend", h, err) + return b.Backend.Load(ctx, h, length, offset) } // Stat tests whether the backend has a file. If it does not exist but still