Skip to content

missing remote objects (404/NoSuchKey) burn the full retry budget per file — fail fast + optional allow_missing_files_on_download salvage mode #1456

Description

@minguyen9988

Problem

Backuper.Classify (pkg/backup/backuper.go:72) returns retrier.Retry for every non-nil error:

func (b *Backuper) Classify(err error) retrier.Action {
	if err == nil {
		return retrier.Succeed
	}
	log.Warn().Err(err).Msgf("Will wait near %s and retry", ...)
	return retrier.Retry
}

A permanently-missing remote object — GCS storage: object doesn't exist, S3 NoSuchKey/404, Azure BlobNotFound — is retried through the full retries_on_failure x retries_duration exponential backoff budget per file, even though a 404 will never heal. On a backup with many missing objects (e.g., remote storage that suffered partial data loss, or object-disk keys removed by lifecycle rules), a download burns hours in pointless retries before failing.

Second, when the operator knows the backup is partially corrupt, there is currently no way to salvage the intact tables/parts: download hard-fails on the first missing object.

Proposal

  1. Add storage.IsNotFoundErr(err) that recognizes not-found errors across backends (GCS/S3/Azure/SFTP/FTP + generic 404/NoSuchKey/BlobNotFound strings), and make Classify return retrier.Fail for those → fail fast instead of burning the retry budget.
  2. Add opt-in allow_missing_files_on_download (general section, default false): when enabled, a not-found data file/part archive is skipped with a loud per-file error-level log and a final summary count, letting operators salvage everything that still exists in a partially-corrupt backup. Metadata files are never skipped — only part data. Default behavior (fail on missing) is unchanged.

Extracted from a production fork used to back up multi-TiB ClickHouse clusters, where salvage mode has been used for disaster recovery of partially-corrupted remote backups.

I have a PR ready to submit.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions