From e483b63c40e72325f495c030f517233aed383c0b Mon Sep 17 00:00:00 2001 From: Michael Eischer Date: Sun, 13 Dec 2020 20:11:01 +0100 Subject: [PATCH] retrybackend: Fail operations when context is already canceled Depending on the used backend, operations started with a canceled context may fail or not. For example the local backend still works in large parts when called with a canceled context. Backends transfering data via http don't work. It is also not possible to retry failed operations in that state as the RetryBackend will abort with a 'context canceled' error. Ensure uniform behavior of all backends by checking for a canceled context by checking for a canceled context as a first step in the RetryBackend. This ensures uniform behavior across all backends, as backends are always wrapped in a RetryBackend. --- internal/backend/backend_retry.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/internal/backend/backend_retry.go b/internal/backend/backend_retry.go index d47be2396..5d7a58b64 100644 --- a/internal/backend/backend_retry.go +++ b/internal/backend/backend_retry.go @@ -33,6 +33,16 @@ func NewRetryBackend(be restic.Backend, maxTries int, report func(string, error, } func (be *RetryBackend) retry(ctx context.Context, msg string, f func() error) error { + // Don't do anything when called with an already cancelled context. There would be + // no retries in that case either, so be consistent and abort always. + // This enforces a strict contract for backend methods: Using a cancelled context + // will prevent any backup repository modifications. This simplifies ensuring that + // a backup repository is not modified any further after a context was cancelled. + // The 'local' backend for example does not provide this guarantee on its own. + if ctx.Err() != nil { + return ctx.Err() + } + err := backoff.RetryNotify(f, backoff.WithContext(backoff.WithMaxRetries(backoff.NewExponentialBackOff(), uint64(be.MaxTries)), ctx), func(err error, d time.Duration) {