Introduce background scheduling and migrations for analytics ingest, expand parser management and map toolbar UX, and ignore local data/session files from version control. Co-authored-by: Cursor <cursoragent@cursor.com>
59 lines
1.6 KiB
Python
59 lines
1.6 KiB
Python
import logging
|
|
import threading
|
|
import time
|
|
from datetime import datetime, timezone
|
|
|
|
from ..database import SessionLocal
|
|
from ..models import ParseJob
|
|
from .jobs import enqueue_job
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
TICK_SECONDS = 30
|
|
RECURRING_STATUSES = ("completed", "failed")
|
|
|
|
|
|
def run_scheduler_tick() -> None:
|
|
db = SessionLocal()
|
|
try:
|
|
now = datetime.now(timezone.utc)
|
|
jobs = (
|
|
db.query(ParseJob)
|
|
.filter(
|
|
ParseJob.is_active.is_(True),
|
|
ParseJob.interval_seconds > 0,
|
|
ParseJob.status.in_(RECURRING_STATUSES),
|
|
)
|
|
.all()
|
|
)
|
|
for job in jobs:
|
|
if job.last_run_at is None:
|
|
continue
|
|
elapsed = (now - job.last_run_at).total_seconds()
|
|
if elapsed < job.interval_seconds:
|
|
continue
|
|
|
|
job.status = "queued"
|
|
job.last_error = None
|
|
db.commit()
|
|
enqueue_job(job.id, job.source_type, job.source_config)
|
|
logger.info("Re-queued recurring job %s (interval %ss)", job.id, job.interval_seconds)
|
|
except Exception:
|
|
logger.exception("Scheduler tick failed")
|
|
db.rollback()
|
|
finally:
|
|
db.close()
|
|
|
|
|
|
def _scheduler_loop(stop_event: threading.Event) -> None:
|
|
while not stop_event.wait(TICK_SECONDS):
|
|
run_scheduler_tick()
|
|
|
|
|
|
def start_scheduler() -> threading.Event:
|
|
stop_event = threading.Event()
|
|
thread = threading.Thread(target=_scheduler_loop, args=(stop_event,), daemon=True)
|
|
thread.start()
|
|
logger.info("Parse job scheduler started (tick every %ss)", TICK_SECONDS)
|
|
return stop_event
|