fix: a replay with short waits between events no longer stalls after the first one

Merged
HA
16h ago
+99-58

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-files and 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=1 and nothing scheduled another, leaving the session running with 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.