feat(oracle): auto-recover Oracle pool + surface status, stop silent import failures
After a power loss the app started before Oracle was ready; init_oracle() failed
once, the pool stayed None forever (no retry), and every sync silently failed
("Oracle pool not initialized") while still hammering the GoMag API each minute,
and order-detail 500'd.
- database.ensure_oracle_pool(force): thread-safe (re)create of the pool, called
at the start of every sync cycle → self-heals within one cycle once Oracle is
back (incl. after an Oracle service restart). init_oracle_client made idempotent
so re-init can't fall back to thin mode.
- database.oracle_status() exposed; main.py startup is non-fatal via ensure pool.
- run_sync ensures the pool before the GoMag download; on failure it records a
clear run status instead of crashing and skips the wasted API calls.
- /api/sync/health reports oracle_ready/last_error; dashboard health pill shows
"Oracle indisponibil" (top priority). Recovery via the existing Start Sync button.
- order_detail degrades gracefully (200 without CODMAT + notice) instead of 500.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -190,8 +190,11 @@ async def sync_health():
|
||||
counts = await sqlite_service.get_recent_phase_failures(limit=3)
|
||||
escalation_phase = next((p for p, c in counts.items() if c >= 3), None)
|
||||
|
||||
ora = database.oracle_status()
|
||||
|
||||
is_healthy = (
|
||||
last_status in (None, "completed")
|
||||
ora["ready"]
|
||||
and last_status in (None, "completed")
|
||||
and escalation_phase is None
|
||||
and sum(counts.values()) <= 1
|
||||
)
|
||||
@@ -203,6 +206,9 @@ async def sync_health():
|
||||
"recent_phase_failures": counts,
|
||||
"escalation_phase": escalation_phase,
|
||||
"is_healthy": is_healthy,
|
||||
"oracle_ready": ora["ready"],
|
||||
"oracle_last_error": ora["last_error"],
|
||||
"oracle_last_attempt_at": ora["last_attempt_at"],
|
||||
}
|
||||
|
||||
|
||||
@@ -422,10 +428,18 @@ async def order_detail(order_number: str):
|
||||
return {"error": "Order not found"}
|
||||
|
||||
items = detail.get("items", [])
|
||||
await _enrich_items_with_codmat(items)
|
||||
oracle_available = True
|
||||
try:
|
||||
await _enrich_items_with_codmat(items)
|
||||
except Exception as e:
|
||||
# Oracle down (pool not initialized): still return the order with its
|
||||
# items so the detail panel renders, just without CODMAT enrichment.
|
||||
oracle_available = False
|
||||
logger.warning(f"order_detail CODMAT enrich skipped (Oracle unavailable?): {e}")
|
||||
|
||||
# Enrich with invoice data
|
||||
order = detail.get("order", {})
|
||||
order["oracle_available"] = oracle_available
|
||||
if order.get("factura_numar") and order.get("factura_data"):
|
||||
order["invoice"] = {
|
||||
"facturat": True,
|
||||
|
||||
Reference in New Issue
Block a user