fix: scheduler next_run accounts for elapsed time since last cycle

On restart the scheduler was counting interval_hours from now, ignoring
when the last cycle actually ran. It now reads last_run_at (in-memory or
DB) and deducts elapsed time so a restart doesn't silently push the next
fire by a full interval.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-23 10:52:54 +02:00
parent 2f9d067a94
commit 8d257adf3d

View File

@@ -1870,11 +1870,30 @@ def _scheduler_loop(stop_event: threading.Event):
_current_status["interval_hours"] = interval_hours
logger.info(f"[Scheduler] Weekend — prochain cycle à {next_slot.strftime('%H:%M')} UTC ({wait_secs/3600:.1f}h)")
else:
wait_secs = interval_hours * 3600
next_run = now_utc + timedelta(hours=interval_hours)
# Account for time already elapsed since last cycle so a restart
# doesn't reset the countdown — fire as soon as the interval is due.
last_run_iso = _current_status.get("last_run_at")
if not last_run_iso:
# Also check DB for the most recent completed run
try:
from services.database import get_cycle_runs
recent = get_cycle_runs(limit=1)
if recent:
last_run_iso = recent[0].get("created_at") or recent[0].get("run_id", "")[:19].replace("T", " ")
except Exception:
pass
elapsed_secs = 0.0
if last_run_iso:
try:
last_dt = datetime.fromisoformat(last_run_iso.replace(" ", "T").rstrip("Z"))
elapsed_secs = (now_utc - last_dt).total_seconds()
except Exception:
pass
wait_secs = max(60, interval_hours * 3600 - elapsed_secs)
next_run = now_utc + timedelta(seconds=wait_secs)
_current_status["next_run_at"] = next_run.isoformat()
_current_status["interval_hours"] = interval_hours
logger.info(f"[Scheduler] Prochain cycle dans {interval_hours}h ({next_run.strftime('%H:%M')} UTC)")
logger.info(f"[Scheduler] Prochain cycle dans {wait_secs/3600:.1f}h ({next_run.strftime('%H:%M')} UTC) — elapsed since last: {elapsed_secs/3600:.1f}h")
stop_event.wait(timeout=wait_secs)