Skip to content

fix(backup): retry EMFILE on source scan instead of skipping directories - #572

Open
BradKollmyer wants to merge 1 commit into
rustic-rs:mainfrom
BradKollmyer:fix/backup-emfile-source-scan
Open

BradKollmyer wants to merge 1 commit into
rustic-rs:mainfrom
BradKollmyer:fix/backup-emfile-source-scan

Conversation

@BradKollmyer

@BradKollmyer BradKollmyer commented Sep 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • detect EMFILE/ENFILE through walkdir/ignore's wrapped I/O errors (outer raw_os_error() is often None)
  • retry source opendir / File::open a couple of times after asking the cache layer to drop pack FDs
  • if it still fails, abort the backup instead of logging error: scan and omitting that directory from the snapshot
  • vanished paths and other unreadable-source errors still count and continue, as in feat: count unreadable source files during backup #561

release_cached_open_files() is a no-op on current main (cache files are opened per blob). Callers still go through it so a pack-handle cache (see #566) can hook in without changing the backup path again.

Why

A backup of many sibling date directories (…/2021-07-01, …/2021-07-23, …) hit No file descriptors available (os error 24) while scanning. rustic mapped that to a source-walk error, skipped the directory, and still saved a snapshot. That looks like a successful backup of a tree with holes.

restic-style incomplete backups should be detectable. Skipping a whole directory because of a transient FD limit is worse than skipping one unreadable file: the next snapshot treats those paths as deleted.

Validation

  • cargo test --all-features -p rustic_core --lib -- --test-threads=1 retry_on_too_many wrap_ignore rustic_error_from_io walk_lists walk_emfile
  • cargo test --all-features -p rustic_core --test integration -- --test-threads=1 test_backup_aborts_on_emfile test_backup_saves_snapshot_after_vanished test_backup_unreadable_file
  • unix rlimit child test: walking a date-dir tree at NOFILE=64 returns EMFILE instead of a successful partial listing
  • integration: a ReadSource that yields EMFILE does not save a snapshot; ENOENT still does

Notes

This is independent of the prune pack-FD cache in #566. That stack is where a 1024-FD process can hold hundreds of pack handles during backup parent-tree reads; this PR is the backup correctness side (do not omit source trees).

When ignore/walkdir cannot opendir a source path because the process is
out of file descriptors, backup currently logs a scan error and omits
that tree from the snapshot. Retry a couple of times, then abort so we
do not save a snapshot that silently dropped directories.

Vanished paths and other source errors are still counted and skipped,
as before (rustic-rs#561).
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.

1 participant