From d15ffd9c92a9c47a6f541a32c8b87afad826cf4a Mon Sep 17 00:00:00 2001 From: Arash Farr Date: Wed, 18 Oct 2023 12:51:39 -0500 Subject: [PATCH] retry: Do not retry Load() if file does not exist --- changelog/unreleased/issue-4515 | 8 ++++++ internal/backend/retry/backend_retry.go | 6 ++++- internal/backend/retry/backend_retry_test.go | 28 ++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 changelog/unreleased/issue-4515 diff --git a/changelog/unreleased/issue-4515 b/changelog/unreleased/issue-4515 new file mode 100644 index 000000000..3832dc605 --- /dev/null +++ b/changelog/unreleased/issue-4515 @@ -0,0 +1,8 @@ +Change: Don't retry to load files that don't exist + +Restic used to always retry to load files. It now only retries to load +files if they exist. + +https://github.com/restic/restic/issues/4515 +https://github.com/restic/restic/issues/1523 +https://github.com/restic/restic/pull/4520 diff --git a/internal/backend/retry/backend_retry.go b/internal/backend/retry/backend_retry.go index 9c51efedc..b23fb41b8 100644 --- a/internal/backend/retry/backend_retry.go +++ b/internal/backend/retry/backend_retry.go @@ -128,7 +128,11 @@ func (be *Backend) Save(ctx context.Context, h restic.Handle, rd restic.RewindRe func (be *Backend) Load(ctx context.Context, h restic.Handle, length int, offset int64, consumer func(rd io.Reader) error) (err error) { return be.retry(ctx, fmt.Sprintf("Load(%v, %v, %v)", h, length, offset), func() error { - return be.Backend.Load(ctx, h, length, offset, consumer) + err := be.Backend.Load(ctx, h, length, offset, consumer) + if be.Backend.IsNotExist(err) { + return backoff.Permanent(err) + } + return err }) } diff --git a/internal/backend/retry/backend_retry_test.go b/internal/backend/retry/backend_retry_test.go index 9f2f39589..a24f3643a 100644 --- a/internal/backend/retry/backend_retry_test.go +++ b/internal/backend/retry/backend_retry_test.go @@ -274,6 +274,34 @@ func TestBackendLoadRetry(t *testing.T) { test.Equals(t, 2, attempt) } +func TestBackendLoadNotExists(t *testing.T) { + // load should not retry if the error matches IsNotExist + notFound := errors.New("not found") + attempt := 0 + + be := mock.NewBackend() + be.OpenReaderFn = func(ctx context.Context, h restic.Handle, length int, offset int64) (io.ReadCloser, error) { + attempt++ + if attempt > 1 { + t.Fail() + return nil, errors.New("must not retry") + } + return nil, notFound + } + be.IsNotExistFn = func(err error) bool { + return errors.Is(err, notFound) + } + + TestFastRetries(t) + retryBackend := New(be, 10, nil, nil) + + err := retryBackend.Load(context.TODO(), restic.Handle{}, 0, 0, func(rd io.Reader) (err error) { + return nil + }) + test.Assert(t, be.IsNotExistFn(err), "unexpected error %v", err) + test.Equals(t, 1, attempt) +} + func TestBackendStatNotExists(t *testing.T) { // stat should not retry if the error matches IsNotExist notFound := errors.New("not found")