From d2e53730d609ed5db918c2ce3c5f6866b0ec4108 Mon Sep 17 00:00:00 2001 From: Alexander Weiss Date: Sat, 17 Oct 2020 21:55:46 +0200 Subject: [PATCH] Add test that repo.List is only called once --- changelog/unreleased/pull-2718 | 7 +++-- cmd/restic/integration_test.go | 56 ++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/changelog/unreleased/pull-2718 b/changelog/unreleased/pull-2718 index a9eefab79..1a0985b5b 100644 --- a/changelog/unreleased/pull-2718 +++ b/changelog/unreleased/pull-2718 @@ -3,8 +3,11 @@ Enhancement: Improve pruning performance and make pruning more customizable The `prune` command is now much faster. This is especially the case for remote repositories or repositories with not much data to remove. Also the memory usage of the `prune` command is now reduced. -Restic used to rebuild the index from scratch after pruning. This is now -changed and the index rebuilding uses the information already known by `prune`. +Restic used to rebuild the index from scratch after pruning. This could lead +to missing packs in the index in some cases for eventually consistent +backends, like e.g. AWS S3. +This behavior is now changed and the index rebuilding uses the information +already known by `prune`. By default, the `prune` command no longer removes all unused data. This behavior can be fine-tuned by new options, like the acceptable amount of unused space or diff --git a/cmd/restic/integration_test.go b/cmd/restic/integration_test.go index 0faf07cb5..42fe34886 100644 --- a/cmd/restic/integration_test.go +++ b/cmd/restic/integration_test.go @@ -1559,6 +1559,62 @@ func testEdgeCaseRepo(t *testing.T, tarfile string, optionsCheck CheckOptions, o } } +// a listOnceBackend only allows listing once per filetype +// listing filetypes more than once may cause problems with eventually consistent +// backends (like e.g. AWS S3) as the second listing may be inconsistent to what +// is expected by the first listing + some operations. +type listOnceBackend struct { + restic.Backend + listedFileType map[restic.FileType]bool +} + +func newListOnceBackend(be restic.Backend) *listOnceBackend { + return &listOnceBackend{ + Backend: be, + listedFileType: make(map[restic.FileType]bool), + } +} + +func (be *listOnceBackend) List(ctx context.Context, t restic.FileType, fn func(restic.FileInfo) error) error { + if t != restic.LockFile && be.listedFileType[t] { + return errors.Errorf("tried listing type %v the second time", t) + } + be.listedFileType[t] = true + return be.Backend.List(ctx, t, fn) +} + +func TestPruneListOnce(t *testing.T) { + env, cleanup := withTestEnvironment(t) + defer cleanup() + + env.gopts.backendTestHook = func(r restic.Backend) (restic.Backend, error) { + return newListOnceBackend(r), nil + } + + pruneOpts := PruneOptions{MaxUnused: "0"} + checkOpts := CheckOptions{ReadData: true, CheckUnused: true} + + testSetupBackupData(t, env) + opts := BackupOptions{} + + testRunBackup(t, "", []string{filepath.Join(env.testdata, "0", "0", "9")}, opts, env.gopts) + firstSnapshot := testRunList(t, "snapshots", env.gopts) + rtest.Assert(t, len(firstSnapshot) == 1, + "expected one snapshot, got %v", firstSnapshot) + + testRunBackup(t, "", []string{filepath.Join(env.testdata, "0", "0", "9", "2")}, opts, env.gopts) + testRunBackup(t, "", []string{filepath.Join(env.testdata, "0", "0", "9", "3")}, opts, env.gopts) + + snapshotIDs := testRunList(t, "snapshots", env.gopts) + rtest.Assert(t, len(snapshotIDs) == 3, + "expected 3 snapshot, got %v", snapshotIDs) + + testRunForgetJSON(t, env.gopts) + testRunForget(t, env.gopts, firstSnapshot[0].String()) + testRunPrune(t, env.gopts, pruneOpts) + rtest.OK(t, runCheck(checkOpts, env.gopts, nil)) +} + func TestHardLink(t *testing.T) { // this test assumes a test set with a single directory containing hard linked files env, cleanup := withTestEnvironment(t)