fix: a replay with short waits between events no longer stalls after the first one
Summary
Small bug found while checking the replay metrics in #401: a replay session whose next event is due at once, or within a few milliseconds, stops after one event and stays running. That covers every replay at speed 100 and anything replaying events with equal or very close timestamps. Locally a 30-event session at speed 100 sat at running with 1 event sent, and the scheduler logged "skipped: maximum number of running instances reached (1)".
Changes
_dispatch_next sent one event and then added its own APScheduler job again for the next one. When that run came due while the current run still counted as running, APScheduler skipped it, and nothing scheduled another. Now one job, _dispatch_events, loops over the events and awaits each delay itself. Pause and cancel stop it through the status check as before. A resume that lands while the loop still waits is skipped by the same rule, and the loop carries on.
Type of Change
- Bug fix (non-breaking change that fixes an issue)
- New feature (non-breaking change that adds functionality)
- Breaking change (fix or feature that would cause existing functionality to change)
- Documentation update
- Refactoring (no functional changes)
- CI/CD or tooling changes
Test Plan
- Backend unit tests pass (
cd backend && uv run pytest tests/unit) - Frontend unit tests pass (
cd frontend && npm run test) - Backend E2E tests pass (
docker compose exec -T backend sh -c 'uv sync --group test --no-dev --frozen --no-install-project --no-build && pytest tests/e2e') - Frontend E2E tests pass (
cd frontend && npm run test:e2e) - API spec and client regenerated if routes or schemas changed (
./deploy.sh openapi, then./deploy.sh types), never edited by hand - Manual testing performed
Validation: the new test replays 25 events at speed 100 and 99 through start_session and times out on main's code; with the fix it passes. Unit 572 passed, e2e 488 passed and 1 skipped. The 30-event session at speed 100 now completes in 0.055 s.
Checklist
- My code follows the project's style guidelines
- I have run
pre-commit run --all-filesand all checks pass - I have added tests that prove my fix/feature works
- I have updated documentation if needed
- My changes don't introduce new warnings
Summary by cubic
Fixes replay sessions stalling after the first event when waits between events are zero or within a few milliseconds, which covers every replay at speed 100 and any session with equal or very close event timestamps.
- Scheduling one APScheduler job per event meant the next run could come due while the current one still counted as running, so it was skipped under
max_instances=1and nothing scheduled another, leaving the sessionrunningwith one event sent. - A single job,
_dispatch_events, now loops over the events and awaits each delay itself. - Pause and cancel stop the loop through the status check, as before.