Fix webhook starvation and isolate scheduled job failures for v0.1.2 [skip ci]
This commit is contained in:
@@ -147,61 +147,72 @@ class Scheduler:
|
||||
"SELECT * FROM jobs WHERE enabled=1 AND next_run<=?",
|
||||
(current.isoformat(),),
|
||||
):
|
||||
job = JobInput.model_validate_json(row["payload"])
|
||||
slot = row["next_run"]
|
||||
if self.db.one(
|
||||
"SELECT * FROM job_runs WHERE job_id=? AND scheduled_at=?",
|
||||
(row["id"], slot),
|
||||
):
|
||||
continue
|
||||
# Skip overdue tasks (>5 minutes): avoid an unexpected artwork
|
||||
# overwrite after downtime. One-time tasks are retired too.
|
||||
late = (current - datetime.fromisoformat(slot)).total_seconds() > 300
|
||||
operation_id = None
|
||||
if not late:
|
||||
action = "restore" if job.source.type == "restore" else "display"
|
||||
payload = (
|
||||
{}
|
||||
if isinstance(job.source, RestoreContent)
|
||||
else DisplayRequest(
|
||||
device_id=job.device_id,
|
||||
content=job.source,
|
||||
options=Options(
|
||||
duration=job.display_duration,
|
||||
restore_previous=job.restore_previous,
|
||||
priority=job.priority,
|
||||
cleanup_unstarred=job.cleanup_unstarred,
|
||||
try:
|
||||
job = JobInput.model_validate_json(row["payload"])
|
||||
slot = row["next_run"]
|
||||
if self.db.one(
|
||||
"SELECT * FROM job_runs WHERE job_id=? AND scheduled_at=?",
|
||||
(row["id"], slot),
|
||||
):
|
||||
continue
|
||||
# Skip overdue tasks (>5 minutes): avoid an unexpected artwork
|
||||
# overwrite after downtime. One-time tasks are retired too.
|
||||
late = (
|
||||
current - datetime.fromisoformat(slot)
|
||||
).total_seconds() > 300
|
||||
operation_id = None
|
||||
if not late:
|
||||
action = (
|
||||
"restore" if job.source.type == "restore" else "display"
|
||||
)
|
||||
payload = (
|
||||
{}
|
||||
if isinstance(job.source, RestoreContent)
|
||||
else DisplayRequest(
|
||||
device_id=job.device_id,
|
||||
content=job.source,
|
||||
options=Options(
|
||||
duration=job.display_duration,
|
||||
restore_previous=job.restore_previous,
|
||||
priority=job.priority,
|
||||
cleanup_unstarred=job.cleanup_unstarred,
|
||||
),
|
||||
).model_dump(mode="json")
|
||||
)
|
||||
operation_id = self.service.submit(
|
||||
action,
|
||||
payload,
|
||||
job.device_id,
|
||||
"scheduler",
|
||||
"schedule-" + uuid4().hex,
|
||||
"job-" + row["id"] + "-" + slot,
|
||||
)
|
||||
following = next_run(job.schedule, current)
|
||||
enabled = bool(following)
|
||||
updated = job.model_copy(update={"enabled": enabled}).model_dump(
|
||||
mode="json"
|
||||
)
|
||||
with self.db.transaction() as tx:
|
||||
tx.execute(
|
||||
"INSERT OR IGNORE INTO job_runs VALUES(?,?,?)",
|
||||
(row["id"], slot, operation_id),
|
||||
)
|
||||
tx.execute(
|
||||
"UPDATE jobs SET payload=?,enabled=?,next_run=?,last_run=?,last_result=? WHERE id=?",
|
||||
(
|
||||
encode(updated),
|
||||
int(enabled),
|
||||
following,
|
||||
now_iso(),
|
||||
"SKIPPED_OVERDUE" if late else operation_id,
|
||||
row["id"],
|
||||
),
|
||||
).model_dump(mode="json")
|
||||
)
|
||||
operation_id = self.service.submit(
|
||||
action,
|
||||
payload,
|
||||
job.device_id,
|
||||
"scheduler",
|
||||
"schedule-" + uuid4().hex,
|
||||
"job-" + row["id"] + "-" + slot,
|
||||
)
|
||||
following = next_run(job.schedule, current)
|
||||
enabled = bool(following)
|
||||
updated = job.model_copy(update={"enabled": enabled}).model_dump(
|
||||
mode="json"
|
||||
)
|
||||
with self.db.transaction() as tx:
|
||||
tx.execute(
|
||||
"INSERT OR IGNORE INTO job_runs VALUES(?,?,?)",
|
||||
(row["id"], slot, operation_id),
|
||||
)
|
||||
tx.execute(
|
||||
"UPDATE jobs SET payload=?,enabled=?,next_run=?,last_run=?,last_result=? WHERE id=?",
|
||||
(
|
||||
encode(updated),
|
||||
int(enabled),
|
||||
following,
|
||||
now_iso(),
|
||||
"SKIPPED_OVERDUE" if late else operation_id,
|
||||
row["id"],
|
||||
),
|
||||
)
|
||||
except Exception:
|
||||
# Keep this slot pending for retry or the existing overdue policy.
|
||||
# A single device must not prevent other jobs or queue consumption.
|
||||
logging.getLogger("frame-flow").error(
|
||||
"Scheduled job failed; details suppressed"
|
||||
)
|
||||
for row in self.db.all(
|
||||
"SELECT * FROM playlists WHERE next_run<=?", (current.isoformat(),)
|
||||
|
||||
Reference in New Issue
Block a user