mirror of
https://github.com/restic/restic.git
synced 2026-05-28 04:35:41 -04:00
Merge ec9ecb2875 into f000da3b35
This commit is contained in:
commit
c4f72dca81
4 changed files with 65 additions and 1 deletions
|
|
@ -107,7 +107,6 @@ func (be *Backend) retry(ctx context.Context, msg string, f func() error) error
|
|||
|
||||
bo := backoff.NewExponentialBackOff()
|
||||
bo.MaxElapsedTime = be.MaxElapsedTime
|
||||
|
||||
if feature.Flag.Enabled(feature.BackendErrorRedesign) {
|
||||
bo.InitialInterval = 1 * time.Second
|
||||
bo.Multiplier = 2
|
||||
|
|
@ -135,14 +134,25 @@ func (be *Backend) retry(ctx context.Context, msg string, f func() error) error
|
|||
err := retryNotifyErrorWithSuccess(
|
||||
func() error {
|
||||
err := f()
|
||||
|
||||
// Running out of local disk space is not a transient backend
|
||||
// failure. For example, `restic check` can fail while writing to a
|
||||
// temporary cache directory. Retrying only repeats the same local
|
||||
// disk-space error.
|
||||
if isNoSpaceError(err) {
|
||||
return backoff.Permanent(err)
|
||||
}
|
||||
|
||||
// don't retry permanent errors as those very likely cannot be fixed by retrying
|
||||
// TODO remove IsNotExist(err) special cases when removing the feature flag
|
||||
if feature.Flag.Enabled(feature.BackendErrorRedesign) && !errors.Is(err, &backoff.PermanentError{}) && be.Backend.IsPermanentError(err) {
|
||||
permanentErrorAttempts--
|
||||
}
|
||||
|
||||
if permanentErrorAttempts <= 0 {
|
||||
return backoff.Permanent(err)
|
||||
}
|
||||
|
||||
return err
|
||||
},
|
||||
backoff.WithContext(b, ctx),
|
||||
|
|
|
|||
|
|
@ -4,11 +4,13 @@ import (
|
|||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/cenkalti/backoff/v4"
|
||||
|
||||
"github.com/restic/restic/internal/backend"
|
||||
"github.com/restic/restic/internal/backend/mock"
|
||||
"github.com/restic/restic/internal/errors"
|
||||
|
|
@ -283,6 +285,31 @@ func TestBackendLoadRetry(t *testing.T) {
|
|||
test.Equals(t, 2, attempt)
|
||||
}
|
||||
|
||||
func TestBackendLoadNoSpaceNotRetried(t *testing.T) {
|
||||
noSpaceErr := &os.PathError{
|
||||
Op: "write",
|
||||
Path: "/tmp/restic-check-cache/data/tmp-123",
|
||||
Err: errNoSpace,
|
||||
}
|
||||
|
||||
attempt := 0
|
||||
be := mock.NewBackend()
|
||||
be.OpenReaderFn = func(ctx context.Context, h backend.Handle, length int, offset int64) (io.ReadCloser, error) {
|
||||
attempt++
|
||||
return nil, noSpaceErr
|
||||
}
|
||||
|
||||
TestFastRetries(t)
|
||||
retryBackend := New(be, time.Second, nil, nil)
|
||||
|
||||
err := retryBackend.Load(context.TODO(), backend.Handle{}, 0, 0, func(rd io.Reader) error {
|
||||
return nil
|
||||
})
|
||||
|
||||
test.Equals(t, noSpaceErr, err)
|
||||
test.Equals(t, 1, attempt)
|
||||
}
|
||||
|
||||
func testBackendLoadNotExists(t *testing.T, hasFlakyErrors bool) {
|
||||
// load should not retry if the error matches IsNotExist
|
||||
notFound := errors.New("not found")
|
||||
|
|
|
|||
14
internal/backend/retry/no_soace.go
Normal file
14
internal/backend/retry/no_soace.go
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
//go:build !windows
|
||||
|
||||
package retry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var errNoSpace = syscall.ENOSPC
|
||||
|
||||
func isNoSpaceError(err error) bool {
|
||||
return errors.Is(err, errNoSpace)
|
||||
}
|
||||
13
internal/backend/retry/no_space_windows.go
Normal file
13
internal/backend/retry/no_space_windows.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package retry
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var errNoSpace = syscall.ERROR_DISK_FULL
|
||||
|
||||
func isNoSpaceError(err error) bool {
|
||||
return errors.Is(err, errNoSpace) ||
|
||||
errors.Is(err, syscall.ERROR_HANDLE_DISK_FULL)
|
||||
}
|
||||
Loading…
Reference in a new issue