Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/scheduler/scheduler.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,12 @@ def __init__(
self.event_bus = event_bus
self.db_manager = db_manager
self.default_working_directory = default_working_directory
self._scheduler = AsyncIOScheduler()
self._scheduler = AsyncIOScheduler(
job_defaults={
"misfire_grace_time": None, # Always run, no matter how late
"coalesce": True, # Merge multiple missed runs into one
}
)

async def start(self) -> None:
"""Load persisted jobs and start the scheduler."""
Expand Down
37 changes: 37 additions & 0 deletions tests/unit/test_scheduler/test_misfire_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""Tests for scheduler misfire configuration."""

from unittest.mock import MagicMock

import pytest

from src.events.bus import EventBus
from src.scheduler.scheduler import JobScheduler
from src.storage.database import DatabaseManager


class TestSchedulerMisfireConfig:
"""Verify APScheduler is configured for resilient job execution."""

@pytest.fixture
def scheduler(self, tmp_path):
event_bus = EventBus()
db_manager = MagicMock(spec=DatabaseManager)
return JobScheduler(
event_bus=event_bus,
db_manager=db_manager,
default_working_directory=tmp_path,
)

def test_misfire_grace_time_is_none(self, scheduler):
"""misfire_grace_time=None ensures jobs always run, no matter how late."""
job_defaults = scheduler._scheduler._job_defaults
assert job_defaults.get("misfire_grace_time") is None, (
"misfire_grace_time must be None to guarantee late jobs still fire"
)

def test_coalesce_is_enabled(self, scheduler):
"""coalesce=True merges multiple missed runs into a single execution."""
job_defaults = scheduler._scheduler._job_defaults
assert job_defaults.get("coalesce") is True, (
"coalesce must be True to prevent spam-firing missed runs"
)