fix: close unconfirmed displays superseded by confirmed updates
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
"""Durable, read-only recovery progress for writes with uncertain outcomes."""
|
||||
|
||||
import json
|
||||
from datetime import UTC, datetime
|
||||
from uuid import uuid4
|
||||
|
||||
from pigway_frame_flow.errors import ErrorCode
|
||||
from pigway_frame_flow.storage.database import encode, now_iso
|
||||
|
||||
|
||||
@@ -71,3 +73,47 @@ class RecoveryMonitor:
|
||||
if row:
|
||||
row["requires_manual_review"] = row["state"] == "manual_review"
|
||||
return row
|
||||
|
||||
def close_superseded_displays(self):
|
||||
for op in self.db.all(
|
||||
"SELECT * FROM operations WHERE action='display' AND write_started=1 "
|
||||
"AND status IN ('SYNCING','UNKNOWN')"
|
||||
):
|
||||
result = json.loads(op["result"] or "{}")
|
||||
if not result.get("image_id") or json.loads(op["payload"]).get(
|
||||
"options", {}
|
||||
).get("restore_previous"):
|
||||
continue
|
||||
newer = self.db.one(
|
||||
"SELECT id,result FROM operations WHERE device_id=? AND action='display' "
|
||||
"AND status='COMPLETED' AND write_started=1 AND created_at>? "
|
||||
"AND COALESCE(json_extract(payload,'$.options.restore_previous'),0)=0 "
|
||||
"AND json_extract(result,'$.image_id') IS NOT NULL "
|
||||
"ORDER BY created_at DESC LIMIT 1",
|
||||
(op["device_id"], op["created_at"]),
|
||||
)
|
||||
if (
|
||||
not newer
|
||||
or json.loads(newer["result"])["image_id"] == result["image_id"]
|
||||
):
|
||||
continue
|
||||
# A later confirmed permanent display ends this attempt, but cannot
|
||||
# prove whether the earlier image was ever shown. Never replay it.
|
||||
self.service.update(
|
||||
op["id"],
|
||||
"FAILED",
|
||||
error={
|
||||
"code": ErrorCode.DISPLAY_NOT_CONFIRMED,
|
||||
"message": "Superseded by a later confirmed display; earlier display unconfirmed",
|
||||
"request_id": op["request_id"],
|
||||
"details": {
|
||||
"superseded_by_operation_id": newer["id"],
|
||||
"previous_error": json.loads(op["error"] or "null"),
|
||||
},
|
||||
},
|
||||
)
|
||||
self.db.history(op, "DISPLAY_NOT_CONFIRMED")
|
||||
self.db.event(
|
||||
"operation.failed",
|
||||
{"operation_id": op["id"], "code": ErrorCode.DISPLAY_NOT_CONFIRMED},
|
||||
)
|
||||
|
||||
@@ -884,6 +884,8 @@ class DisplayService:
|
||||
except FlowError:
|
||||
pass
|
||||
|
||||
self.recovery.close_superseded_displays()
|
||||
|
||||
def expire(self):
|
||||
if not self.settings.mock and not self.settings.write_enabled:
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user