feat(T6/T7): supervizare worker (healthcheck+autoheal) + backup online + cheie partajata
T6 — worker supravegheat:
- app/worker/healthcheck.py: probe pe heartbeat-ul din DB (beat invechit -> exit 1).
Prinde worker-ul agatat (proces viu, beat inghetat) pe care restart:always nu-l
vede. Cablat ca healthcheck pe serviciul worker in compose.
- sidecar autoheal: restarteaza efectiv containerul unhealthy (compose simplu doar
marcheaza, nu restarteaza la unhealthy).
T7 — deploy:
- tools/backup.py: backup ONLINE via Connection.backup (WAL nu se copiaza sigur cu
cp); --keep N roteste snapshot-urile.
- .env.example documenteaza env-urile; volum persistent numit deja in compose.
Fix critic (split api/worker in 2 containere): AUTOPASS_CREDS_KEY trebuie PARTAJATA
api<->worker, altfel worker nu decripteaza creds-urile criptate de API -> submission
blocate. Acum impusa in compose (${...:?} -> fail explicit daca lipseste).
.gitignore: exceptie !.env.example.
5 teste noi (tests/test_deploy.py). 100 pass total.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
79
tools/backup.py
Normal file
79
tools/backup.py
Normal file
@@ -0,0 +1,79 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Backup online SQLite (T7) — copie consistenta a bazei pe volum persistent.
|
||||
|
||||
SQLite in WAL NU se copiaza sigur cu `cp` (WAL-ul poate avea tranzactii necheckpoint-ate).
|
||||
Folosim API-ul `Connection.backup` (online, consistent, fara oprirea worker-ului).
|
||||
|
||||
Utilizare:
|
||||
python -m tools.backup # -> <db_dir>/backups/autopass-YYYYMMDD-HHMMSS.db
|
||||
python -m tools.backup --out /path/snap.db # destinatie explicita
|
||||
python -m tools.backup --keep 14 # pastreaza ultimele 14, sterge restul
|
||||
|
||||
Recomandat: rulat dintr-un cron pe gazda (ex. zilnic), tinta pe volum/montaj separat.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import sqlite3
|
||||
import sys
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
|
||||
from app.config import get_settings
|
||||
|
||||
|
||||
def backup_db(src: Path, dest: Path) -> Path:
|
||||
"""Backup online (consistent) din `src` in `dest`. Intoarce `dest`."""
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
src_conn = sqlite3.connect(src)
|
||||
try:
|
||||
dst_conn = sqlite3.connect(dest)
|
||||
try:
|
||||
src_conn.backup(dst_conn)
|
||||
finally:
|
||||
dst_conn.close()
|
||||
finally:
|
||||
src_conn.close()
|
||||
return dest
|
||||
|
||||
|
||||
def prune(backup_dir: Path, keep: int) -> list[Path]:
|
||||
"""Pastreaza cele mai noi `keep` snapshot-uri (dupa nume = timestamp), sterge restul."""
|
||||
snaps = sorted(backup_dir.glob("autopass-*.db"), reverse=True)
|
||||
removed = []
|
||||
for old in snaps[keep:]:
|
||||
old.unlink(missing_ok=True)
|
||||
removed.append(old)
|
||||
return removed
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
parser = argparse.ArgumentParser(description="Backup online SQLite gateway RAR AUTOPASS")
|
||||
parser.add_argument("--out", type=Path, default=None, help="destinatie explicita (default: <db_dir>/backups/)")
|
||||
parser.add_argument("--keep", type=int, default=0, help="pastreaza ultimele N snapshot-uri (0 = nu sterge)")
|
||||
args = parser.parse_args(argv)
|
||||
|
||||
src = get_settings().db_path
|
||||
if not src.exists():
|
||||
print(f"eroare: baza {src} nu exista", file=sys.stderr)
|
||||
return 1
|
||||
|
||||
if args.out is not None:
|
||||
dest = args.out
|
||||
else:
|
||||
stamp = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
|
||||
dest = src.parent / "backups" / f"autopass-{stamp}.db"
|
||||
|
||||
backup_db(src, dest)
|
||||
print(f"backup -> {dest} ({dest.stat().st_size} bytes)")
|
||||
|
||||
if args.keep > 0:
|
||||
removed = prune(dest.parent, args.keep)
|
||||
if removed:
|
||||
print(f"sterse {len(removed)} snapshot-uri vechi (pastrez {args.keep})")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in New Issue
Block a user