Skip to content

Refactor scheduled function execution to use explicit timing control - #82

Closed
ianmacartney wants to merge 6 commits into
mainfrom
ian/explicit-scheduler-queue
Closed

Refactor scheduled function execution to use explicit timing control#82
ianmacartney wants to merge 6 commits into
mainfrom
ian/explicit-scheduler-queue

Conversation

@ianmacartney

@ianmacartney ianmacartney commented Mar 29, 2026

Copy link
Copy Markdown
Member

Refactor scheduled function execution to use explicit scheduling instead of setTimeout

This change replaces the setTimeout-based scheduled function execution with an explicit scheduling system to reduce races from setTimeout.

Currently, scheduled functions can execute mid-transaction, especially if you don't mock timers.

Key Changes:

  • Explicit execution control: Scheduled functions are no longer automatically executed via setTimeout. Instead, they are executed explicitly through finishInProgressScheduledFunctions() and finishAllScheduledFunctions()
  • Function path resolution: Added scheduledFunctionPaths map to track resolved function paths for function handles that may target different components than where the job is stored.
  • Improved timer compatibility: Adds an empty empty setTimeout call at the scheduled function time so vi.runAllTimers() still works to fast-forward time until all scheduled functions should be enqueued.
  • Sequential execution: Functions are now executed one at a time in order of scheduled time, instead of previously where setTimeout could fire mid-transaction.

@coderabbitai

coderabbitai Bot commented Mar 29, 2026

Copy link
Copy Markdown

Warning

Rate limit exceeded

@ianmacartney has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 54 minutes and 17 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: get-convex/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0b5c4fe7-031a-4927-a56b-3060a422bc7e

📥 Commits

Reviewing files that changed from the base of the PR and between c94c792 and f9d337c.

📒 Files selected for processing (3)
  • convex/scheduler.test.ts
  • convex/scheduler.ts
  • index.ts
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ian/explicit-scheduler-queue

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Member Author

This stack of pull requests is managed by Graphite. Learn more about stacking.

@pkg-pr-new

pkg-pr-new Bot commented Mar 29, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/convex-test@82

commit: f9d337c

@ianmacartney ianmacartney changed the title Revert "allow setTimeout to work within actions (#78)" Refactor scheduled function execution to use explicit timing control Mar 29, 2026
@ianmacartney
ianmacartney force-pushed the ian/explicit-scheduler-queue branch from 964b72d to 4864881 Compare April 1, 2026 20:57

Copy link
Copy Markdown
Member Author

For context, this approach pre-dated the AsyncLocalStorage overhaul, and was an alternative branch to ALS to solve the problem of serializing transactions. #112 is one such manifestation
It was updated to use ALS after rebasing.
After thinking more on it, I think we could leverage the new ALS-based transaction serialization, without serializing all scheduled functions manually like this.
In particular, serializing scheduled functions I think would deadlock for actions that schedule a function then poll for its completion.
Instead I think we could still use setTimeout, but have it call the serialized entrypoint, so if something is scheduled or executed during a transaction, it'll naturally wait until after the transaction finishes.
So I think it's worth exploring that direction so users don't have to run finishInProgressScheduledFunctions at all, which used to work in trivial cases only but now could work pretty fluidly.

cc @Nicolapps

Copy link
Copy Markdown
Member

Replaced by #114

@Nicolapps Nicolapps closed this May 12, 2026
ianmacartney added a commit that referenced this pull request May 13, 2026
Currently a scheduled function uses setTimeout, which can fire mid-way through a transaction if real timers are used.

And scheduling multiple functions can also race. This is because they detect the current global transaction lock and infer they're a nested transaction call.

This PR waits until the nested transaction has finished before starting the next scheduled function, allowing it to use the global transaction lock to serialize scheduled mutations, regardless of where they're scheduled from or whether real timers are used or not.

Fixes #112

Closes #82
@renovate renovate Bot mentioned this pull request Aug 12, 2026
1 task
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants