feat(T2): reconciliere anti-duplicat + retry/backoff + recuperare orfane
Inchide bucla de trimitere (plan.md sect. 4 worker, failure registry).
- app/reconcile.py: match_finalizata pe vin+dataPrestatie+odometruFinal (int),
alege id maxim la duplicate (RAR accepta duplicate, confirmat live)
- app/rar_client.get_finalizate: parseaza data.content (descoperit live ca
ruta = GET /prezentari/getAllPrezentariFinalizate; filtrele nu merg pe test)
- app/worker rescris:
- recuperare orfane (rand 'sending' peste lease = worker mort mid-POST)
- pe eroare tranzitorie/timeout: reconciliere INAINTE de re-send (anti-duplicat);
daca recordul exista la RAR -> sent fara re-POST
- retry/backoff exponential; peste worker_max_retries -> error + banner
- re-login la token expirat (JWT 30h)
- schema: coloana next_attempt_at (backoff) + migrare aditiva in init_db
- config: worker_sending_lease_s, worker_retry_base_s/max_s, worker_max_retries
- contract: documentata ruta+forma getAllPrezentariFinalizate (verificat live)
Verify: pytest 54 passed (15 noi T2) + validare live (reconciliere record 68514).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
10
app/db.py
10
app/db.py
@@ -27,14 +27,22 @@ def get_connection() -> sqlite3.Connection:
|
||||
|
||||
|
||||
def init_db() -> None:
|
||||
"""Creeaza schema daca lipseste. Idempotent — sigur la fiecare boot."""
|
||||
"""Creeaza schema daca lipseste + migrari aditive. Idempotent — sigur la fiecare boot."""
|
||||
conn = get_connection()
|
||||
try:
|
||||
conn.executescript(_SCHEMA.read_text(encoding="utf-8"))
|
||||
_migrate(conn)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
def _migrate(conn: sqlite3.Connection) -> None:
|
||||
"""Migrari aditive pentru DB create inainte de o coloana noua (CREATE IF NOT EXISTS nu altereaza)."""
|
||||
cols = {r["name"] for r in conn.execute("PRAGMA table_info(submissions)").fetchall()}
|
||||
if "next_attempt_at" not in cols:
|
||||
conn.execute("ALTER TABLE submissions ADD COLUMN next_attempt_at TEXT")
|
||||
|
||||
|
||||
def _now_iso() -> str:
|
||||
return datetime.now(timezone.utc).isoformat(timespec="seconds")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user