Skip to content

Order pending work by commit and snapshot timestamps - #220

Open
ianmacartney wants to merge 17 commits into
ian/pending-start-pointerfrom
ian/use-commit-ts
Open

ianmacartney wants to merge 17 commits into
ian/pending-start-pointerfrom
ian/use-commit-ts

Conversation

@ianmacartney

@ianmacartney ianmacartney commented Aug 4, 2026 •

Copy link
Copy Markdown
Member

Order ready work, completions, and cancellations by commit timestamp. A sweep finds scheduled enqueues that commit behind the incoming cursor, which stays bounded by the transaction snapshot.

Upgrade legacy keys and pointers before scanning, preserving their original time ordering. Requires Convex 1.43.

Validation: 162 tests pass; build, typecheck, lint, and changed-file formatting pass.

@ianmacartney
ianmacartney requested a review from reeceyang August 4, 2026 00:43

ianmacartney commented Aug 4, 2026 •

Copy link
Copy Markdown
Member Author

Warning

This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
Learn more

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

@pkg-pr-new

pkg-pr-new Bot commented Aug 4, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

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

commit: 0287944

Copy link
Copy Markdown
Member Author

One thing to note here is that reverting to an older version would require a migration to convert segments into their old format, - which could be hard to get right, but could make some assumptions about the maximum segment a user would have previously used, or migrate based on creation time of the documents or something..?

@ianmacartney ianmacartney changed the title update convex use commitTS for workpool Aug 4, 2026

@reeceyang reeceyang left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

When users migrate to this version, would work that was originally scheduled to run in the future get executed immediately?

Comment thread src/component/loop.test.ts Outdated
Comment thread src/component/loop.test.ts Outdated
Comment thread src/component/loop.ts Outdated
@ianmacartney

Copy link
Copy Markdown
Member Author

@cubic review

@cubic-dev-ai

cubic-dev-ai Bot commented Sep 9, 2026

Copy link
Copy Markdown

@cubic review

@ianmacartney I have started the AI code review. It will take a few minutes to complete.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 26 files

Not reviewed (too large): example/benchmarks/2026-09-08.json (~5,865 lines) - if these are generated or fixture files, add them to ignored paths to exclude them from future reviews.

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/component/loop.ts
Comment thread src/component/loop.test.ts Outdated
Comment thread src/component/stats.ts Outdated
Comment thread src/component/schema.ts
Comment thread src/component/danger.ts Outdated
Comment thread src/component/danger.ts Outdated
Comment thread example/convex/test/scheduling.ts Outdated
Comment thread src/component/loop.test.ts Outdated
Comment thread example/convex/test/cleanup.ts Outdated
Comment thread CHANGELOG.md Outdated

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 17 files (changes from recent commits).

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/component/README.md Outdated

Copy link
Copy Markdown
Member Author

I split out the pendingStart batching into a stacked PR

@ianmacartney ianmacartney changed the title use commitTS for workpool Order pending work by commit and snapshot timestamps Sep 9, 2026

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

All reported issues were addressed across 20 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/component/stateMachine.test.ts Outdated
@ianmacartney
ianmacartney force-pushed the ian/use-commit-ts branch 2 times, most recently from 2ccceca to ba7eb18 Compare September 12, 2026 00:02
Comment thread src/component/lib.ts Outdated
segment: max(toSegment(runAt), getCurrentSegment()),
segment,
// Only near-term starts can commit behind the cursor.
...(scheduled && (segment as bigint) <= toTimestamp(now + 5 * MINUTE)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is there a way to avoid the cast here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

we could do different inserts based on scheduled, since segment is only ever the placeholder if scheduled is false?

Comment thread src/component/loop.ts
const batchFields = {
// The segment at query time — what "now" was when the batch was built.
segment: v.int64(),
upgrade: v.optional(v.object({ starts: v.array(vStart), done: v.boolean() })),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

iiuc, these are the legacy entries that we now want to convert to use nanoseconds?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yes

Comment thread src/component/loop.ts Outdated
Comment on lines +405 to +406
// (1) Out-of-order entries left at the boundary stamp. Nothing at or above
// the cursor is read, so a bulk enqueue sharing this stamp costs nothing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

which cursor is meant in

Nothing at or above the cursor is read, so a bulk enqueue sharing this stamp costs nothing.

It looks like we do read at least one doc at or above the sweep cursor?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We start at the sweepCursor for the scanTs field, but we only read up to incomingCursor for the segment, since anything greater than that will get picked up by the normal incomingCursor query.
So if sweep is at 3, incomingCursor is at 10, the queries are:

1. scanTs == sweepCursor && segment < incomingCursor
2. scanTs ≥ sweepCursor
3. segment ≥ incomingCursor

(1) is an optimization for the case where a huge transaction lands in a single scanTs. Instead of scanning over the whole transaction, once we land in the middle of it, the next iteration will only consider the section of it that is actually actionable, before moving on to the next scanTs.

Comment thread src/component/loop.ts Outdated
// Raised to at least the snapshot, which the cursor may reach this run, so
// the retry can't land behind it; a backoff shorter than the clocks' skew
// just starts next iteration.
const segment = maxBigint(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is this still needed now that snapshotTs <= Date.now <= commitTs?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's not needed for contexts where that's gone out. I'll wait for convex-test to support it before removing it though. I'll leave a TODO

Comment thread src/component/loop.test.ts Outdated
// ────────────────────────────────────────────────────────────────────

describe("getBatch", () => {
it("waits for wall time when the snapshot clock is ahead", async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

this scenario is not possible in practice, right?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

it was possible before, but I believe the Date.now() change should be out soon/already. And it can happen in convex-test pretty often, since Date.now() is fixed for tests, but each transaction bumps the db clock

expect(o.running.map((r) => r.workId)).toEqual([scheduledId]);
});

it("starts scheduled work once it's due, without ever rewriting it", async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

we don't ever rewrite scheduled work now, right? (unless it's being upgraded from a legacy entry?)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

correct

@ianmacartney
ianmacartney changed the base branch from main to graphite-base/220 October 28, 2026 02:00
@ianmacartney
ianmacartney changed the base branch from graphite-base/220 to ian/pending-start-pointer October 28, 2026 02:01
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