Skip to content

fix: prevent duplicate backups on container restart#240

Merged
itsDNNS merged 1 commit intomainfrom
fix/backup-restart-duplicates
Mar 15, 2026
Merged

fix: prevent duplicate backups on container restart#240
itsDNNS merged 1 commit intomainfrom
fix/backup-restart-duplicates

Conversation

@itsDNNS
Copy link
Owner

@itsDNNS itsDNNS commented Mar 15, 2026

Problem

The BackupCollector creates a backup immediately on every container startup because _last_poll is initialized to 0.0 — meaning should_poll() always returns True on the first tick. In environments where the container restarts frequently (e.g. via Dockhand auto-updates during active development), this leads to multiple backups per day despite a 24h interval being configured.

Observed on production instance (v2026-03-14.726):

monitoring_started Backup created
00:55:13 00:55:10
00:55:45 00:55:41
03:00:36 03:00:31
04:00:57 04:00:54

5 backups in 7 hours with a daily interval configured — each one triggered by a container restart.

Fix

On init, _seed_last_poll() reads the newest backup file's modification timestamp from disk and sets _last_poll accordingly. This way should_poll() correctly waits until the configured interval has elapsed since the last actual backup, even after a container restart.

Design decision: The seed uses the newest backup file regardless of whether it was created by the scheduled collector or a manual "Backup Now" click. This means a manual backup can shift the automatic schedule after a restart. This is intentional — the guarantee is "at least one backup every <interval>", not "backups at a fixed time of day". A manual backup is a valid backup, and waiting another full interval after it is operationally correct.

Changes

File Change
app/modules/backup/collector.py Add _seed_last_poll() in __init__, documented design decision
tests/test_collectors.py 9 new TestBackupCollector tests covering seed behavior, should_poll(), and manual backup inclusion

Test plan

  • 1580 tests pass locally
  • Container restart no longer creates duplicate backup
  • First startup with no existing backups → backup created immediately
  • Backup older than interval on disk → should_poll() returns True → backup created
  • Recent backup on disk → should_poll() returns False → no duplicate

@itsDNNS itsDNNS force-pushed the fix/backup-restart-duplicates branch from 7de7b9b to 5050a12 Compare March 15, 2026 08:43
Seed _last_poll from the newest backup file on disk during
BackupCollector init. This way should_poll() correctly waits
until the configured interval has passed since the last actual
backup, even after a container restart.

Previous approach (checking backup age in collect()) had two
issues flagged in review:
- A skipped collect still advanced the schedule via record_success()
- Manual backups suppressed the next automatic backup

The seed approach avoids both: _last_poll is set once on init
from disk state, and only the orchestrator updates it after a
real collect() run.
@itsDNNS itsDNNS force-pushed the fix/backup-restart-duplicates branch from 5050a12 to d475977 Compare March 15, 2026 08:48
@itsDNNS
Copy link
Owner Author

itsDNNS commented Mar 15, 2026

Reworked approach (v2)

Changed from skip-in-collect to seed-on-init after review:

Old approach: Check backup age in collect(), return skip result

  • Problem: Skip still advanced schedule via record_success()
  • Problem: Manual backups suppressed next automatic backup

New approach: Seed _last_poll from newest backup file on disk during __init__

  • should_poll() correctly returns False until interval has elapsed
  • collect() only runs when actually due — no skip logic needed
  • Seed uses newest file regardless of source (scheduled or manual) — design decision documented in code

Tests now verify should_poll() behavior directly instead of skip/create decisions.

@itsDNNS itsDNNS merged commit 90603d9 into main Mar 15, 2026
2 checks passed
@itsDNNS itsDNNS deleted the fix/backup-restart-duplicates branch March 15, 2026 08:50
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