mirror of
https://github.com/restic/restic.git
synced 2026-06-27 11:04:17 +00:00
55e335ec6c
The treeCache in SnapshotsDir was never cleared when snapshots were reloaded. This caused the "latest" symlink to keep pointing to the previous snapshot even after new snapshots were added. Add a generation counter to SnapshotsDirStructure that is incremented whenever the directory structure is rebuilt (in makeDirs). The treeCache checks this generation on each lookup and resets itself when the generation changes, ensuring cached nodes (including symlinks) are refreshed after a snapshot reload.
83 lines
2.2 KiB
Go
83 lines
2.2 KiB
Go
//go:build darwin || freebsd || linux
|
|
|
|
package fuse
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"github.com/anacrolix/fuse"
|
|
"github.com/anacrolix/fuse/fs"
|
|
|
|
"github.com/restic/restic/internal/test"
|
|
)
|
|
|
|
type cacheTestNode struct {
|
|
id int
|
|
}
|
|
|
|
func (n cacheTestNode) Attr(context.Context, *fuse.Attr) error {
|
|
return nil
|
|
}
|
|
|
|
func TestTreeCacheGeneration(t *testing.T) {
|
|
cache := newTreeCache()
|
|
created := 0
|
|
create := func(forgetFn) (fs.Node, error) {
|
|
created++
|
|
return cacheTestNode{id: created}, nil
|
|
}
|
|
|
|
node1, err := cache.lookupOrCreate("node", 1, create)
|
|
test.OK(t, err)
|
|
node2, err := cache.lookupOrCreate("node", 1, create)
|
|
test.OK(t, err)
|
|
test.Assert(t, node1 == node2, "lookup should reuse cached node")
|
|
test.Equals(t, 1, created)
|
|
|
|
node3, err := cache.lookupOrCreate("node", 2, create)
|
|
test.OK(t, err)
|
|
test.Assert(t, node1 != node3, "lookup should recreate node after generation change")
|
|
test.Equals(t, 2, created)
|
|
|
|
node4, err := cache.lookupOrCreate("node", -1, create)
|
|
test.OK(t, err)
|
|
test.Assert(t, node3 == node4, "negative generation should not reset cache")
|
|
test.Equals(t, 2, created)
|
|
|
|
node5, err := cache.lookupOrCreate("node", 3, create)
|
|
test.OK(t, err)
|
|
test.Assert(t, node4 != node5, "lookup should still track later generation changes")
|
|
test.Equals(t, 3, created)
|
|
}
|
|
|
|
func TestTreeCacheForgetOnlyRemovesSameGeneration(t *testing.T) {
|
|
cache := newTreeCache()
|
|
created := 0
|
|
var forgets []forgetFn
|
|
create := func(forget forgetFn) (fs.Node, error) {
|
|
created++
|
|
forgets = append(forgets, forget)
|
|
return cacheTestNode{id: created}, nil
|
|
}
|
|
|
|
node1, err := cache.lookupOrCreate("node", 1, create)
|
|
test.OK(t, err)
|
|
node2, err := cache.lookupOrCreate("node", 2, create)
|
|
test.OK(t, err)
|
|
test.Assert(t, node1 != node2, "lookup should recreate node after generation change")
|
|
test.Equals(t, 2, created)
|
|
|
|
forgets[0]()
|
|
node3, err := cache.lookupOrCreate("node", 2, create)
|
|
test.OK(t, err)
|
|
test.Assert(t, node2 == node3, "forget from an old generation must not remove the current node")
|
|
test.Equals(t, 2, created)
|
|
|
|
forgets[1]()
|
|
node4, err := cache.lookupOrCreate("node", 2, create)
|
|
test.OK(t, err)
|
|
test.Assert(t, node3 != node4, "forget from the current generation should remove the cached node")
|
|
test.Equals(t, 3, created)
|
|
}
|