Skip to content

feat(world-postgres): replace pg-boss with graphile-worker#1124

Merged
VaguelySerious merged 8 commits intovercel:mainfrom
kschmelter13:graphile-postgres
Feb 21, 2026
Merged

feat(world-postgres): replace pg-boss with graphile-worker#1124
VaguelySerious merged 8 commits intovercel:mainfrom
kschmelter13:graphile-postgres

Conversation

@kschmelter13
Copy link
Contributor

Implements Graphile Worker as the queue/runner for world-postgres, replacing pg-boss while keeping the existing PostgreSQL durability guarantees and public world API intact. Lead from conversations in pr #155

kschmelter13 and others added 3 commits February 19, 2026 11:47
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: kschmelter13 <kschmelter13@gmail.com>
Signed-off-by: kschmelter13 <kschmelter13@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: kschmelter13 <kschmelter13@gmail.com>
@changeset-bot
Copy link

changeset-bot bot commented Feb 19, 2026

🦋 Changeset detected

Latest commit: d7544bf

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 16 packages
Name Type
@workflow/web Patch
@workflow/world-postgres Patch
@workflow/world-testing Patch
@workflow/cli Patch
workflow Patch
@workflow/core Patch
@workflow/builders Patch
@workflow/next Patch
@workflow/nitro Patch
@workflow/web-shared Patch
@workflow/astro Patch
@workflow/nest Patch
@workflow/rollup Patch
@workflow/sveltekit Patch
@workflow/vite Patch
@workflow/nuxt Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel
Copy link
Contributor

vercel bot commented Feb 19, 2026

@kschmelter13 is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

@kschmelter13
Copy link
Contributor Author

kschmelter13 commented Feb 19, 2026

@VaguelySerious quick heads up: the required checks are blocked due to fork security gates.
GitHub Actions shows “workflow(s) awaiting approval” and Vercel checks show “Authorization required to deploy” (fork preview).
Can you approve the workflow runs + authorize the fork deployments so CI can execute?
Ran all tests locally and they pass. (Unit + e2e)
(No changes to .github/workflows/* in this PR.) Thanks!

Copy link
Member

@VaguelySerious VaguelySerious left a comment

Choose a reason for hiding this comment

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

Code LGTM. Checks are running, will check back in a bit. Also this PR is missing a changeset pnpm changeset for world-postgres. Should be a minor bump since we're not changing schemas and are in beta.

Signed-off-by: kschmelter13 <kschmelter13@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Signed-off-by: kschmelter13 <kschmelter13@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Copy link
Member

@VaguelySerious VaguelySerious left a comment

Choose a reason for hiding this comment

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

LGTM - two wording changes to make

kschmelter13 and others added 2 commits February 19, 2026 14:44
Minor to patch

Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Kevin <kschmelter13@gmail.com>
Co-authored-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Kevin <kschmelter13@gmail.com>
@kschmelter13
Copy link
Contributor Author

@VaguelySerious All changes made

@vercel
Copy link
Contributor

vercel bot commented Feb 19, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
example-nextjs-workflow-turbopack Ready Ready Preview, Comment Feb 19, 2026 8:01pm
example-nextjs-workflow-webpack Ready Ready Preview, Comment Feb 19, 2026 8:01pm
example-workflow Ready Ready Preview, Comment Feb 19, 2026 8:01pm
workbench-astro-workflow Ready Ready Preview, Comment Feb 19, 2026 8:01pm
workbench-express-workflow Ready Ready Preview, Comment Feb 19, 2026 8:01pm
workbench-fastify-workflow Ready Ready Preview, Comment Feb 19, 2026 8:01pm
workbench-hono-workflow Ready Ready Preview, Comment Feb 19, 2026 8:01pm
workbench-nitro-workflow Ready Ready Preview, Comment Feb 19, 2026 8:01pm
workbench-nuxt-workflow Ready Ready Preview, Comment Feb 19, 2026 8:01pm
workbench-sveltekit-workflow Ready Ready Preview, Comment Feb 19, 2026 8:01pm
workbench-vite-workflow Ready Ready Preview, Comment Feb 19, 2026 8:01pm
workflow-nest Ready Ready Preview, Comment Feb 19, 2026 8:01pm
workflow-swc-playground Ready Ready Preview, Comment Feb 19, 2026 8:01pm

@VaguelySerious
Copy link
Member

Thanks! I'm re-running CI and merging once it's green

@rovo89
Copy link
Contributor

rovo89 commented Feb 19, 2026

New schema/tables will be created automatically, right? Should it clean up the pgboss schema?

const postgres = createPostgres(config.connectionString);
const drizzle = createClient(postgres);
const queue = createQueue(boss, config);
const queue = createQueue(config.connectionString, config);
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like createQueue() could simply get the connectionString from the config?

Copy link
Contributor

Choose a reason for hiding this comment

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

diff --git a/packages/world-postgres/src/index.ts b/packages/world-postgres/src/index.t
s
index 21d8f4a4..41c09a92 100644
--- a/packages/world-postgres/src/index.ts
+++ b/packages/world-postgres/src/index.ts
@@ -34,7 +34,7 @@ export function createWorld(
 ): World & { start(): Promise<void> } {
   const postgres = createPostgres(config.connectionString);
   const drizzle = createClient(postgres);
-  const queue = createQueue(config.connectionString, config);
+  const queue = createQueue(config);
   const storage = createStorage(drizzle);
   const streamer = createStreamer(postgres, drizzle);
 
diff --git a/packages/world-postgres/src/queue.ts b/packages/world-postgres/src/queue.ts
index c5b8dc61..3f622a44 100644
--- a/packages/world-postgres/src/queue.ts
+++ b/packages/world-postgres/src/queue.ts
@@ -43,10 +43,7 @@ export type PostgresQueue = Queue & {
   close(): Promise<void>;
 };
 
-export function createQueue(
-  connectionString: string,
-  config: PostgresWorldConfig
-): PostgresQueue {
+export function createQueue(config: PostgresWorldConfig): PostgresQueue {
   const port = process.env.PORT ? Number(process.env.PORT) : undefined;
   const localWorld = createLocalWorld({ dataDir: undefined, port });
 
@@ -73,7 +70,7 @@ export function createQueue(
     if (!startPromise) {
       startPromise = (async () => {
         workerUtils = await makeWorkerUtils({
-          connectionString,
+          connectionString: config.connectionString,
           logger: stderrLogger,
         });
         await workerUtils.migrate();
@@ -136,7 +133,7 @@ export function createQueue(
     }
 
     runner = await run({
-      connectionString,
+      connectionString: config.connectionString,
       concurrency: config.queueConcurrency || 10,
       logger: stderrLogger,
       pollInterval: 500, // 500ms = 0.5s (graphile-worker uses LISTEN/NOTIFY when avai
lable)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes good call, changes made.

@VaguelySerious
Copy link
Member

@rovo89 Inflight pg-boss items be lost, i.e. any queued but unprocessed workflow/step jobs in pg-boss at upgrade time will be silently dropped. Graphile will run its own migration to set up the table, but there's no migration for inflight requests

@rovo89
Copy link
Contributor

rovo89 commented Feb 19, 2026

Yes, just saw the await workerUtils.migrate() call.

"Inflight" might also mean that a workflow waiting for sleep('7d') won't be able to finish, right? Don't misunderstand me, I'm not trying to block at all, just want to check which side-effects the change has.

And with cleanup, I meant deleting the pgboss schema.

@VaguelySerious
Copy link
Member

@rovo89 I'll create migrations at the very least for cleaning up the old tables, and potentially a full migration if it seems feasible, though for now I'd expect that upgrading to the the code in this PR and the new package version will halt inflight runs.

@rovo89
Copy link
Contributor

rovo89 commented Feb 19, 2026

@kschmelter13 Thanks for the PR! I reviewed the code as well, although I haven't used Graphile Worker before. LGTM, with one nitpick: #1124 (comment)

When a job arrives, workers make HTTP fetch calls to the local world endpoints (.well-known/workflow/v1/flow or .well-known/workflow/v1/step) to execute the actual workflow logic.

@VaguelySerious That's still the way to go?

@VaguelySerious
Copy link
Member

@rovo89

When a job arrives, workers make HTTP fetch calls to the local world endpoints

Yes, that's still the intended use. The actual workers can be on a different host than postgres and are managed by the user, but the workers need to be running in the same environment as the server making these endpoints available.

@VaguelySerious
Copy link
Member

Migrations added in #1126, see this commit diff. Did some upgrade testing and seems to pick up existing jobs correctly.

Signed-off-by: kschmelter13 <kschmelter13@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
@kschmelter13
Copy link
Contributor Author

@rovo89 Of course! I made those changes, thanks for looking at it

@kschmelter13
Copy link
Contributor Author

@VaguelySerious Thanks for the migrations. The vercel world and some comment tests are failing because of a vercel token. Do those need to pass for merge?

@rovo89
Copy link
Contributor

rovo89 commented Feb 19, 2026

I made those changes

Thanks, but I think @VaguelySerious had already considered them in his migration commit (which would now conflict). 😉

@VaguelySerious
Copy link
Member

@kschmelter13 Vercel world tests don't need to run on this PR. I'm waiting with merging mainly for us to be able to release a new version before this lands on main, nothing more needed from your side.

No worries about the merge conflicts, I'll rebase my PR once this PR lands

@kschmelter13
Copy link
Contributor Author

@VaguelySerious @rovo89 Perfect sounds good, thanks guys

@VaguelySerious VaguelySerious merged commit 1f9a67c into vercel:main Feb 21, 2026
3 of 17 checks passed
Comment on lines +22 to +29
// Redirect graphile-worker logs to stderr so CLI --json on stdout stays clean.
// TODO: When CI=1 suppresses logging, replace with conditional stdout (e.g. log to stdout when not in JSON/CI mode).
const stderrLogger = new Logger(
() => (level: string, message: string, meta?: unknown) => {
const line = [level, message, meta].filter(Boolean).join(' ') + '\n';
process.stderr.write(line);
}
);
Copy link
Contributor

Choose a reason for hiding this comment

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

@kschmelter13 Graphile Worker is quite noisy in the logs. Can we change the log level to show only errors maybe? Currently, it even shows debug messages like "spawned" (multiple times, I hope that's not some indicator of unexpected/unsupported behavior).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

By default it logs all levels, I can default to errors only and add an env for override?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah, something like that. Just check yourself which kinds of messages come up in npm run dev. The default logger seems to ignore debug messages unless some env is set: https://worker.graphile.org/docs/library/logger
Probably that's gone because you override the logger. But I think it also wrote several non-debug messages, like which queues it's listening to, that's why I suggested error only, maybe warning.

Copy link
Contributor

Choose a reason for hiding this comment

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

These are the messages I see:

info Failed to read crontab file '/workspaces/playground/crontab'; cron is disabled
debug Registering termination signal handlers (SIGUSR2, SIGINT, SIGTERM, SIGHUP, SIGABRT) [object Object]
debug Spawned
debug Spawned
debug Spawned
debug Spawned
debug Spawned
debug Spawned
debug Spawned
debug Spawned
debug Spawned
debug Spawned
info Worker connected and looking for jobs... (task names: 'workflow_flows', 'workflow_steps')
debug Graphile Worker Cron fired 1.796s too early (clock skew?); rescheduling [object Object]
debug Graphile Worker Cron fired 3.557s too early (clock skew?); rescheduling [object Object]
debug Graphile Worker Cron fired 3.581s too early (clock skew?); rescheduling [object Object]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@rovo89 I wrote some tests around the logging and chose to just use the default graphile logger, and remove the cusotm logger implementation which wasnt really needed. I opened a pr if you want to take a look: #1163

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.

3 participants