Skip to content

fix(inmemory): move next_run on when fetch_schedule picks a schedule - #998

Merged
janbjorge merged 1 commit into
janbjorge:mainfrom
ericdelorefice:fix/inmemory-schedule-next-run
Oct 22, 2026
Merged

janbjorge merged 1 commit into
janbjorge:mainfrom
ericdelorefice:fix/inmemory-schedule-next-run

Conversation

@ericdelorefice

@ericdelorefice ericdelorefice commented Oct 22, 2026 •

Copy link
Copy Markdown
Contributor

Agentic

Summary

InMemoryQueries.fetch_schedule() picks a due schedule but leaves its next_run where it was. PostgreSQL's fetch_schedule sets next_run = date_trunc('seconds', NOW() + delay) in the same UPDATE, on both the due branch and the stale-heartbeat branch. In memory, set_schedule_queued() then puts the schedule back with next_run still in the past, so the next poll picks it again.

SchedulerManager.run() sleeps until the soonest schedule is due, so with two schedules on different cadences the slower one runs every time the scheduler wakes for the faster one. With * * * * * and */2 * * * * registered on a real clock for 250 seconds:

in-memory  every_minute        ran 4x at 13:46:01, 13:47:01, 13:48:01, 13:49:01
in-memory  every_other_minute  ran 4x at 13:46:01, 13:47:01, 13:48:01, 13:49:01
postgres   every_minute        ran 4x at 13:46:01, 13:47:01, 13:48:01, 13:49:01
postgres   every_other_minute  ran 2x at 13:46:01, 13:48:01

At the query level, against PyPI 1.6.0 and main (a4e26ee): insert a schedule due now, fetch_schedule it with a one-hour delay, set_schedule_queued, and fetch again. PostgreSQL (asyncpg and psycopg, PostgreSQL 16.2) moves next_run by one hour and returns nothing on the second fetch; in memory next_run does not move and the second fetch picks it again. Nothing was mocked or injected.

The in-memory reference page says SchedulerManager works unchanged against this adapter, so this looked like a gap rather than a choice. I may be missing a reason it was left out.

Change

Both pick branches in InMemoryQueries.fetch_schedule() now set next_run to now plus the delay passed in, truncated to whole seconds as the SQL does with date_trunc (+2 lines, one file). Release note added under v1.7.0, Fixed.

Intended behaviour change: under the in-memory adapter a picked schedule is not due again until its delay has passed, as in PostgreSQL.

Testing

  • uv sync --frozen && uv run ruff check . && uv run ruff format . --check && uv run lint-imports && uv run mypy . && uv run pytest passed (PostgreSQL 16.2 through EXTERNAL_POSTGRES_DSN, Python 3.12; mypy run as mypy . --no-incremental): 1483 passed, 10 skipped; 1479 passed, 10 skipped on main.
  • Additional testing steps: two new in-memory tests fail on main and pass with the fix: next_run moves to now + delay and an immediate second fetch returns nothing, and reclaiming a stale picked schedule also moves next_run. Two new controls pass on both: an in-memory schedule is picked again once its next_run comes due, and a PostgreSQL test pins the same next_run contract for Queries.

Checklist

  • I have read the Contributing Guide
  • I have added or updated tests
  • I have updated documentation if necessary

PostgreSQL sets next_run to now + delay when it picks a schedule. The
in-memory adapter left it in the past, so the schedule was due again on
every poll and a slower schedule ran each time the scheduler woke for a
faster one.
@janbjorge

Copy link
Copy Markdown
Owner

Thanks once more 🧞‍♂️ @ericdelorefice !

@janbjorge
janbjorge merged commit f2764c8 into janbjorge:main Oct 22, 2026
21 checks passed
@janbjorge janbjorge added bug Something isn't working Agentic Issue or PR written mainly by an AI agent. Required on all agent-authored contributions. labels Oct 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Agentic Issue or PR written mainly by an AI agent. Required on all agent-authored contributions. bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants