Skip to content

snapshot query prototype#186

Draft
reeceyang wants to merge 1 commit into
mainfrom
reece/snapshot-query-prototype
Draft

snapshot query prototype#186
reeceyang wants to merge 1 commit into
mainfrom
reece/snapshot-query-prototype

Conversation

@reeceyang

Copy link
Copy Markdown
Contributor

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@coderabbitai

coderabbitai Bot commented Mar 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 70083f79-78a1-46d4-9807-ba483a22be43

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch reece/snapshot-query-prototype

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.

@pkg-pr-new

pkg-pr-new Bot commented Mar 21, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/get-convex/workpool/@convex-dev/workpool@186

commit: 4296141

@reeceyang reeceyang force-pushed the reece/snapshot-query-prototype branch from 0e703d1 to 4296141 Compare March 23, 2026 18:37
Comment thread src/component/loop.ts
console.debug(`[main] generation ${generation} behind: ${delayMs}ms`);

// Read snapshot of pendingStart and pendingCompletions
const { pending, completed } = await ctx.runQuery(

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this call would be a snapshot query

@reeceyang reeceyang requested a review from ianmacartney March 23, 2026 19:14

@ianmacartney ianmacartney left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple early thoughts:

  1. If we don't find anything to do in updateRunStatus, we still need to fetch once without it being a snapshot query - so we transactionally change from running to idle and there isn't a race where a completing task doesn't start the loop but the loop misses the completed task
  2. We could stop using segment numbers altogether - though that's a bit orthogonal
  3. We'd likely want a version of it that we run from updateRunStatus that checks if there's anything to do... but then again part of the point of that function was to break up the transaction. so maybe we could just have loop:main schedule itself and cut that second one out entirely - a lot of it is just carefully fetching a little more data to avoid OCC's. Doing a full check in a snapshot query is strictly better I think...
  4. We could do the recovery & reporting from a snapshot to avoid OCC's when trying to check the status of things that are changing (e.g. reading .count on tables!)

Comment thread src/component/loop.ts
.withIndex("segment", (q) =>
q
.gte("segment", incomingSegmentCursor - CURSOR_BUFFER_SEGMENTS)
.lte("segment", segment),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need to limit the upper bound anymore!

Comment thread src/component/loop.ts
.query("pendingStart")
.withIndex("segment", (q) =>
q
.gte("segment", incomingSegmentCursor - CURSOR_BUFFER_SEGMENTS)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

once we get index caching, we can drop this!

Comment thread src/component/loop.ts

export const getPending = internalQuery({
args: {
toSchedule: v.number(),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we could drop this from the args, we could get query caching when nothing new has been added to pendingStart, at the cost of over-fetching work to do.. maybe that's over-kill. Maybe we could round / etc. But that's an over-optimization, and generally we wouldn't expect to get cache hits since we're presumably immediately removing things from it

Comment thread src/component/loop.ts
// Read pendingCancelation, deleting from pendingStart. If it's still running, queue to cancel.
console.time("[main] pendingCancelation");
await handleCancelation(ctx, state, segment, console, toCancel);
const canceledWorkIds = await handleCancelation(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not read from cancelation at the same time as pending start/completed? we could even check when fetching if there were any overlap between canceled & pending start - though that isn't sufficient to know it's not in the table at all

Comment thread src/component/loop.ts
console.time("[main] pendingStart");
await handleStart(ctx, state, segment, console, globals);
const filteredPending = pending.filter(
(p) => !canceledWorkIds.has(p.workId),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that the work could be in the canceled table but not fetched in the latest batch of canceled ids

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