Skip to content

Fail fast on missing remote objects, add allow_missing_files_on_download salvage mode - #1544

Merged
Slach merged 4 commits into
masterfrom
download_allow_missing_files
Sep 10, 2026
Merged

Fail fast on missing remote objects, add allow_missing_files_on_download salvage mode#1544
Slach merged 4 commits into
masterfrom
download_allow_missing_files

Conversation

@Slach

@Slach Slach commented Sep 9, 2026

Copy link
Copy Markdown
Collaborator

Fix #1456

Problem

Backuper.Classify retried every non-context error, so a permanently missing remote object (S3 NoSuchKey/404, GCS 404, Azure BlobNotFound, FTP/SFTP not-found) burned the whole retries_on_failure x retries_duration backoff per file. #1379 fixed this only for table metadata .json/.sql; part data archives, directory-format parts and required_backup diff parts still retried. There was also no way to salvage the intact parts of a partially corrupted remote backup.

Changes

  • storage.IsNotFoundErr(err) — recognises not-found across backends: storage.ErrNotFound, fs.ErrNotExist, GCS ErrObjectNotExist/googleapi 404, smithy NoSuchKey/NotFound/HTTP 404, Azure BlobNotFound/404, plus the string markers previously in isRemoteMetadataNotFound (which is removed in favour of the shared helper).
  • Backuper.Classify returns retrier.Fail for not-found errors, so every retried remote operation fails fast.
  • general.allow_missing_files_on_download (env ALLOW_MISSING_FILES_ON_DOWNLOAD, default false), --allow-missing-files for download/restore_remote, allow_missing_files query argument for POST /backup/download and POST /backup/restore_remote (same only-override-when-true semantics as --rebind-replica-path-if-exists).
  • Salvage mode in downloadTableData / downloadDiffParts: a part whose data is missing on remote is skipped with an error-level log, dropped from local table metadata (parts and files), counted and reported in a final summary; partially downloaded directory-format parts are removed. Metadata files are never skipped. With upload_by_part: false a missing archive stays fatal with an explicit message, since one part can't be carved out of a size-based bundle.
  • README (config, API, CLI help), ChangeLog, testflows default-config snapshot.

Tests

  • unit: TestIsNotFoundErr (typed SDK errors + string markers, negative cases), TestClassify extended.
  • integration: TestAllowMissingFilesOnDownload (MinIO) — two-part backup, mc rm one archive, download fails fast (<20s, no "and retry"), download --allow-missing-files succeeds, metadata drops the part, restore brings back the surviving 100 rows. TestMetadataNotFoundS3|SFTP|FTP still pass.
  • testflows: full suite locally, 8/8 suites pass.

🤖 Generated with Claude Code

Slach and others added 4 commits September 9, 2026 20:18
…wnload

Backuper.Classify retried every non-context error, so a permanently
missing remote object (S3 NoSuchKey/404, GCS 404, Azure BlobNotFound,
FTP/SFTP not-found) burned the whole retries_on_failure x
retries_duration backoff per file. storage.IsNotFoundErr now recognises
these across backends (typed SDK errors plus the string markers that
used to live in isRemoteMetadataNotFound) and Classify returns Fail.

Add general.allow_missing_files_on_download (ALLOW_MISSING_FILES_ON_DOWNLOAD,
default false), --allow-missing-files for download/restore_remote and the
allow_missing_files API query argument: a salvage mode which skips data
parts missing on remote storage with an error-level log and a final
summary, drops them from the local table metadata (parts + files) and
lets the intact parts of a partially corrupted backup be restored.
Metadata files are never skipped; upload_by_part=false bundles stay
fatal because a single part can't be carved out of a shared archive.

Fix #1456

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Fail fast on missing remote objects and add allow_missing_files_on_download

Backuper.Classify retried every non-context error, so a permanently
missing remote object (S3 NoSuchKey/404, GCS 404, Azure BlobNotFound,
FTP/SFTP not-found) burned the whole retries_on_failure x
retries_duration backoff per file. storage.IsNotFoundErr now recognises
these across backends (typed SDK errors plus the string markers that
used to live in isRemoteMetadataNotFound) and Classify returns Fail.

Add general.allow_missing_files_on_download (ALLOW_MISSING_FILES_ON_DOWNLOAD,
default false), --allow-missing-files for download/restore_remote and the
allow_missing_files API query argument: a salvage mode which skips data
parts missing on remote storage with an error-level log and a final
summary, drops them from the local table metadata (parts + files) and
lets the intact parts of a partially corrupted backup be restored.
Metadata files are never skipped; upload_by_part=false bundles stay
fatal because a single part can't be carved out of a shared archive.
TestKillRestore failed in CI with a bare `curl: (7) Connection refused`
from postAction and nothing about why the server was not listening.
Reuse waitForAPIServerReady instead of a fixed 3s sleep after
`clickhouse-backup server` starts, and attach the server log tail to
postAction failures so the next occurrence shows the actual cause.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 34438492208

Coverage increased (+0.2%) to 67.055%

Details

  • Coverage increased (+0.2%) from the base build.
  • Patch coverage: 18 uncovered changes across 2 files (109 of 127 lines covered, 85.83%).
  • 7 coverage regressions across 3 files.

Uncovered Changes

File Changed Covered %
pkg/backup/download.go 74 62 83.78%
pkg/server/server.go 8 2 25.0%
Total (5 files) 127 109 85.83%

Coverage Regressions

7 previously-covered lines in 3 files lost coverage.

File Lines Losing Coverage Coverage
pkg/storage/gcs.go 3 54.46%
pkg/backup/create.go 2 74.58%
pkg/storage/object_disk/object_disk.go 2 68.63%

Coverage Stats

Coverage Status
Relevant Lines: 25870
Covered Lines: 17347
Line Coverage: 67.05%
Coverage Strength: 35514.43 hits per line

💛 - Coveralls

@Slach
Slach merged commit 2d3f411 into master Sep 10, 2026
58 of 60 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

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

2 participants