hub-db-backup: wait for a snapshot being written, limit 7 h (one slot gap + 1 h) — the 2 h limit refused the hub's own catch-up and mailed a false failure at 10:28 (R-1234, closed); tests red-proved
gates / gates (push) Successful in 13m14s

This commit is contained in:
2026-10-23 10:35:16 +02:00
parent 34979d5d25
commit 52af38df6f
5 changed files with 52 additions and 6 deletions
+30 -3
View File
@@ -185,15 +185,42 @@ class Push(Base):
self.assertEqual(self.pushed(), [])
self.assertFalse(self.signal("felhom_hub_db_backup.prom"))
def test_d34_a_missed_slot_is_not_pushed_again(self):
# D34: snapshots every 6 h, so a newest copy 3 h old means the hub missed a slot — refuse, as for a day
self.snapshot(age_s=3 * 3600)
def test_d34_two_missed_slots_are_refused(self):
# D34: snapshots every 6 h; the limit is one slot gap + 1 h (7 h) — a newest copy 8 h old means the hub missed
# two slots, or stopped. RED-PROOF: MAX_AGE_H back to 26 → this pushes.
self.snapshot(age_s=8 * 3600)
r = self.run_script(PUSH)
self.assertNotEqual(r.returncode, 0)
self.assertIn("stopped snapshotting", r.stderr)
self.assertEqual(self.pushed(), [])
self.assertFalse(self.signal("felhom_hub_db_backup.prom"))
def test_d34_a_restart_within_one_slot_is_not_an_alarm(self):
# the hub's catch-up fires only past 6 h, so after a restart the newest can be up to 6 h old — that is pushed,
# not refused (2 h refused it: the false mail of 2026-10-23 10:28). RED-PROOF: MAX_AGE_H 2 → this fails.
self.snapshot(age_s=int(5.9 * 3600))
r = self.run_script(PUSH)
self.assertEqual(r.returncode, 0, r.stderr)
self.assertEqual(len(self.pushed()), 1)
def test_d34_waits_for_a_snapshot_being_written(self):
# the 2026-10-23 10:28 case: the hub's catch-up snapshot is being written (hub-*.db.tmp) and the only finished
# one is 8 h old. The push must wait for the new one, not refuse the old. RED-PROOF: drop the wait loop → fails.
import threading
self.snapshot(age_s=8 * 3600)
snaps = self.j("pod", "snapshots")
tmp = os.path.join(snaps, "hub-%s.db.tmp" % stamp(int(time.time())))
src = [os.path.join(snaps, n) for n in os.listdir(snaps) if n.endswith(".db")][0]
shutil.copy(src, tmp)
def finish():
time.sleep(1.5)
os.rename(tmp, tmp[: -len(".tmp")])
threading.Thread(target=finish).start()
r = self.run_script(PUSH, FELHOM_HUBBK_POLL_S="1", FELHOM_HUBBK_WAIT_S="20")
self.assertEqual(r.returncode, 0, r.stderr)
self.assertIn("being written", r.stdout)
self.assertEqual(len(self.pushed()), 1)
def test_truncated_copy_is_not_pushed(self):
self.snapshot()
r = self.run_script(PUSH, FAKE_TRUNCATE="1")