Merge pull request #4981 from konidev20/fix-gh-4934-cleanup-removed-snaphots-from-cache

cache: clear snapshot files from cache during load index
This commit is contained in:
Michael Eischer 2024-08-16 19:04:59 +00:00 committed by GitHub
commit 5cca6e66be
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 111 additions and 10 deletions

View file

@ -0,0 +1,8 @@
Enhancement: Clear removed snapshots from local cache of the current host
Restic only removed snapshots from the cache on the host that runs the `forget` command.
On other hosts that use the same repository, the old snapshots remained in the cache.
Restic now, automatically clears old snapshots from the local cache of the current host.
https://github.com/restic/restic/issues/4934
https://github.com/restic/restic/pull/4981

View file

@ -2,11 +2,14 @@ package cache
import (
"context"
"fmt"
"io"
"os"
"sync"
"github.com/restic/restic/internal/backend"
"github.com/restic/restic/internal/debug"
"github.com/restic/restic/internal/restic"
)
// Backend wraps a restic.Backend and adds a cache.
@ -215,3 +218,44 @@ func (b *Backend) IsNotExist(err error) bool {
func (b *Backend) Unwrap() backend.Backend {
return b.Backend
}
func (b *Backend) List(ctx context.Context, t backend.FileType, fn func(f backend.FileInfo) error) error {
if !b.Cache.canBeCached(t) {
return b.Backend.List(ctx, t, fn)
}
// will contain the IDs of the files that are in the repository
ids := restic.NewIDSet()
// wrap the original function to also add the file to the ids set
wrapFn := func(f backend.FileInfo) error {
id, err := restic.ParseID(f.Name)
if err != nil {
// returning error here since, if we cannot parse the ID, the file
// is invalid and the list must exit.
return err
}
ids.Insert(id)
// execute the original function
return fn(f)
}
err := b.Backend.List(ctx, t, wrapFn)
if err != nil {
return err
}
if ctx.Err() != nil {
return ctx.Err()
}
// clear the cache for files that are not in the repo anymore, ignore errors
err = b.Cache.Clear(t, ids)
if err != nil {
fmt.Fprintf(os.Stderr, "error clearing %s files in cache: %v\n", t.String(), err)
}
return nil
}

View file

@ -57,6 +57,13 @@ func randomData(n int) (backend.Handle, []byte) {
return h, data
}
func list(t testing.TB, be backend.Backend, fn func(backend.FileInfo) error) {
err := be.List(context.TODO(), backend.IndexFile, fn)
if err != nil {
t.Fatal(err)
}
}
func TestBackend(t *testing.T) {
be := mem.New()
c := TestNewCache(t)
@ -238,3 +245,54 @@ func TestErrorBackend(t *testing.T) {
wg.Wait()
}
func TestAutomaticCacheClear(t *testing.T) {
be := mem.New()
c := TestNewCache(t)
wbe := c.Wrap(be)
// add two handles h1 and h2
h1, data := randomData(2000)
// save h1 directly to the backend
save(t, be, h1, data)
if c.Has(h1) {
t.Errorf("cache has file1 too early")
}
h2, data2 := randomData(3000)
// save h2 directly to the backend
save(t, be, h2, data2)
if c.Has(h2) {
t.Errorf("cache has file2 too early")
}
loadAndCompare(t, wbe, h1, data)
if !c.Has(h1) {
t.Errorf("cache doesn't have file1 after load")
}
loadAndCompare(t, wbe, h2, data2)
if !c.Has(h2) {
t.Errorf("cache doesn't have file2 after load")
}
// remove h1 directly from the backend
remove(t, be, h1)
if !c.Has(h1) {
t.Errorf("file1 not in cache any more, should be removed from cache only after list")
}
// list all files in the backend
list(t, wbe, func(_ backend.FileInfo) error { return nil })
// h1 should be removed from the cache
if c.Has(h1) {
t.Errorf("cache has file1 after remove")
}
// h2 should still be in the cache
if !c.Has(h2) {
t.Errorf("cache doesn't have file2 after list")
}
}

View file

@ -706,19 +706,10 @@ func (r *Repository) prepareCache() error {
return nil
}
indexIDs := r.idx.IDs()
debug.Log("prepare cache with %d index files", len(indexIDs))
// clear old index files
err := r.Cache.Clear(restic.IndexFile, indexIDs)
if err != nil {
fmt.Fprintf(os.Stderr, "error clearing index files in cache: %v\n", err)
}
packs := r.idx.Packs(restic.NewIDSet())
// clear old packs
err = r.Cache.Clear(restic.PackFile, packs)
err := r.Cache.Clear(restic.PackFile, packs)
if err != nil {
fmt.Fprintf(os.Stderr, "error clearing pack files in cache: %v\n", err)
}