feat(worker): pause gate, inter-job delay, and force-stop watcher in main loop
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,7 @@ from lyra_worker.config import get_config
|
|||||||
from lyra_worker.crypto import secret_key_problem
|
from lyra_worker.crypto import secret_key_problem
|
||||||
from lyra_worker.db import wait_for_db
|
from lyra_worker.db import wait_for_db
|
||||||
from lyra_worker.discovery import DiscoveryConfig, DiscoveryResult, reset_pending, run_discovery
|
from lyra_worker.discovery import DiscoveryConfig, DiscoveryResult, reset_pending, run_discovery
|
||||||
|
from lyra_worker.floor import press_paused, job_delay_seconds, consume_kill_switch
|
||||||
from lyra_worker.library import clear_staging_root
|
from lyra_worker.library import clear_staging_root
|
||||||
from lyra_worker.monitor import MonitorConfig, fulfill_owned_releases, reconcile, sweep
|
from lyra_worker.monitor import MonitorConfig, fulfill_owned_releases, reconcile, sweep
|
||||||
from lyra_worker.pipeline import run_pipeline
|
from lyra_worker.pipeline import run_pipeline
|
||||||
@@ -226,9 +227,30 @@ def _liveness_heartbeat() -> None:
|
|||||||
time.sleep(HEARTBEAT_SECONDS)
|
time.sleep(HEARTBEAT_SECONDS)
|
||||||
|
|
||||||
|
|
||||||
|
KILL_POLL_SECONDS = 1.5
|
||||||
|
|
||||||
|
|
||||||
|
def _kill_switch_watch() -> None:
|
||||||
|
"""Poll floor.killRequested on a background daemon thread (its own DB connection) so a
|
||||||
|
force-stop aborts the process even while the main loop is blocked in a download. On any
|
||||||
|
DB hiccup, reconnect and keep watching."""
|
||||||
|
conn = wait_for_db()
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
consume_kill_switch(conn)
|
||||||
|
except Exception:
|
||||||
|
try:
|
||||||
|
conn.close()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
conn = wait_for_db()
|
||||||
|
time.sleep(KILL_POLL_SECONDS)
|
||||||
|
|
||||||
|
|
||||||
def run_forever() -> None:
|
def run_forever() -> None:
|
||||||
_require_secret_key()
|
_require_secret_key()
|
||||||
threading.Thread(target=_liveness_heartbeat, daemon=True).start()
|
threading.Thread(target=_liveness_heartbeat, daemon=True).start()
|
||||||
|
threading.Thread(target=_kill_switch_watch, daemon=True).start()
|
||||||
conn = wait_for_db()
|
conn = wait_for_db()
|
||||||
clear_staging_root(STAGING_ROOT) # sweep any staging dirs orphaned by a prior crash
|
clear_staging_root(STAGING_ROOT) # sweep any staging dirs orphaned by a prior crash
|
||||||
reclaimed = reclaim_stuck_jobs(conn) # requeue jobs a prior crash left mid-pipeline
|
reclaimed = reclaim_stuck_jobs(conn) # requeue jobs a prior crash left mid-pipeline
|
||||||
@@ -252,6 +274,7 @@ def run_forever() -> None:
|
|||||||
last_discover_tick = 0.0
|
last_discover_tick = 0.0
|
||||||
last_heartbeat = 0.0
|
last_heartbeat = 0.0
|
||||||
last_prune = 0.0
|
last_prune = 0.0
|
||||||
|
next_job_allowed = 0.0 # monotonic time before which no new job is claimed (inter-job delay)
|
||||||
try:
|
try:
|
||||||
while True:
|
while True:
|
||||||
# A transient DB connection loss (e.g. AdminShutdown when Postgres restarts) raises
|
# A transient DB connection loss (e.g. AdminShutdown when Postgres restarts) raises
|
||||||
@@ -283,6 +306,12 @@ def run_forever() -> None:
|
|||||||
|
|
||||||
maybe_run_scan(conn, resolver, probe, browser, config)
|
maybe_run_scan(conn, resolver, probe, browser, config)
|
||||||
|
|
||||||
|
# Press paused (downloads only) or the inter-job delay hasn't elapsed: keep the
|
||||||
|
# loop alive (monitor/scan/discovery above still run) but claim nothing.
|
||||||
|
if press_paused(config) or now < next_job_allowed:
|
||||||
|
time.sleep(IDLE_SLEEP)
|
||||||
|
continue
|
||||||
|
|
||||||
job_id = claim_next(conn)
|
job_id = claim_next(conn)
|
||||||
if job_id is None:
|
if job_id is None:
|
||||||
time.sleep(IDLE_SLEEP)
|
time.sleep(IDLE_SLEEP)
|
||||||
@@ -296,6 +325,7 @@ def run_forever() -> None:
|
|||||||
conn.rollback()
|
conn.rollback()
|
||||||
finish_job(conn, job_id, mcfg)
|
finish_job(conn, job_id, mcfg)
|
||||||
print(f"worker: finished job {job_id}", flush=True)
|
print(f"worker: finished job {job_id}", flush=True)
|
||||||
|
next_job_allowed = time.monotonic() + job_delay_seconds(config)
|
||||||
except psycopg.OperationalError as e:
|
except psycopg.OperationalError as e:
|
||||||
print(f"worker: db connection lost ({e}); reconnecting...", flush=True)
|
print(f"worker: db connection lost ({e}); reconnecting...", flush=True)
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user