@sonamu-kit/tasks 0.0.2 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client.test.js +2 -1
- package/dist/client.test.js.map +1 -1
- package/dist/database/backend.d.ts +7 -8
- package/dist/database/backend.d.ts.map +1 -1
- package/dist/database/backend.js +97 -39
- package/dist/database/backend.js.map +1 -1
- package/dist/database/backend.test.js +3 -1
- package/dist/database/backend.test.js.map +1 -1
- package/dist/database/base.d.ts +2 -2
- package/dist/database/base.d.ts.map +1 -1
- package/dist/database/base.js +17 -5
- package/dist/database/base.js.map +1 -1
- package/dist/execution.test.js +4 -2
- package/dist/execution.test.js.map +1 -1
- package/dist/practices/01-remote-workflow.js +2 -1
- package/dist/practices/01-remote-workflow.js.map +1 -1
- package/dist/testing/connection.d.ts +1 -1
- package/dist/testing/connection.d.ts.map +1 -1
- package/dist/testing/connection.js +5 -4
- package/dist/testing/connection.js.map +1 -1
- package/dist/worker.test.js +2 -1
- package/dist/worker.test.js.map +1 -1
- package/package.json +1 -1
- package/scripts/migrate.ts +1 -4
- package/src/client.test.ts +2 -1
- package/src/database/backend.test.ts +3 -1
- package/src/database/backend.ts +124 -45
- package/src/database/base.ts +12 -7
- package/src/execution.test.ts +4 -2
- package/src/practices/01-remote-workflow.ts +2 -1
- package/src/testing/connection.ts +5 -3
- package/src/worker.test.ts +2 -1
- package/templates/openworkflow.config.ts +1 -1
package/dist/client.test.js
CHANGED
|
@@ -7,10 +7,11 @@ import { KNEX_GLOBAL_CONFIG } from "./testing/connection.js";
|
|
|
7
7
|
describe("OpenWorkflow", ()=>{
|
|
8
8
|
let backend;
|
|
9
9
|
beforeEach(async ()=>{
|
|
10
|
-
backend =
|
|
10
|
+
backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {
|
|
11
11
|
namespaceId: randomUUID(),
|
|
12
12
|
runMigrations: false
|
|
13
13
|
});
|
|
14
|
+
await backend.initialize();
|
|
14
15
|
});
|
|
15
16
|
afterEach(async ()=>{
|
|
16
17
|
await backend.stop();
|
package/dist/client.test.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/client.test.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { afterEach, beforeEach, describe, expect, test } from \"vitest\";\nimport { z } from \"zod\";\nimport { BackendPostgres } from \".\";\nimport { declareWorkflow, OpenWorkflow } from \"./client\";\nimport { KNEX_GLOBAL_CONFIG } from \"./testing/connection\";\n\ndescribe(\"OpenWorkflow\", () => {\n let backend: BackendPostgres;\n\n beforeEach(async () => {\n backend = await BackendPostgres.connect(KNEX_GLOBAL_CONFIG, {\n namespaceId: randomUUID(),\n runMigrations: false,\n });\n });\n\n afterEach(async () => {\n await backend.stop();\n });\n\n test(\"enqueues workflow runs via backend\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"enqueue-test\" }, noopFn);\n await workflow.run({ docUrl: \"https://example.com\" });\n\n const workerId = \"enqueue-worker\";\n const claimed = await backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: 1000,\n });\n\n expect(claimed?.workflowName).toBe(\"enqueue-test\");\n expect(claimed?.workerId).toBe(workerId);\n expect(claimed?.input).toEqual({ docUrl: \"https://example.com\" });\n });\n\n describe(\"schema validation\", () => {\n describe(\"Zod schema\", () => {\n const schema = z.object({\n userId: z.uuid(),\n count: z.number().int().positive(),\n });\n\n test(\"accepts valid input\", async () => {\n const client = new OpenWorkflow({ backend });\n const workflow = client.defineWorkflow({ name: \"schema-zod-valid\", schema }, noopFn);\n\n const handle = await workflow.run({\n userId: randomUUID(),\n count: 3,\n });\n\n await handle.cancel();\n });\n\n test(\"rejects invalid input\", async () => {\n const client = new OpenWorkflow({ backend });\n const workflow = client.defineWorkflow({ name: \"schema-zod-invalid\", schema }, noopFn);\n\n await expect(workflow.run({ userId: \"not-a-uuid\", count: 0 } as never)).rejects.toThrow();\n });\n });\n });\n\n test(\"result resolves when workflow succeeds\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"result-success\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n const workerId = \"test-worker\";\n const claimed = await backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: 1000,\n });\n expect(claimed).not.toBeNull();\n if (!claimed) throw new Error(\"workflow run was not claimed\");\n\n await backend.completeWorkflowRun({\n workflowRunId: claimed.id,\n workerId,\n output: { ok: true },\n });\n\n // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression\n const result = await handle.result();\n expect(result).toEqual({ ok: true });\n });\n\n test(\"result rejects when workflow fails\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"result-failure\" }, noopFn);\n await workflow.run({ value: 1 });\n\n const workerId = \"test-worker\";\n const claimed = await backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: 1000,\n });\n expect(claimed).not.toBeNull();\n if (!claimed) throw new Error(\"workflow run was not claimed\");\n\n // mark as failed (should reschedule))\n await backend.failWorkflowRun({\n workflowRunId: claimed.id,\n workerId,\n error: { message: \"boom\" },\n });\n\n const rescheduled = await backend.getWorkflowRun({\n workflowRunId: claimed.id,\n });\n expect(rescheduled?.status).toBe(\"pending\");\n expect(rescheduled?.error).toEqual({ message: \"boom\" });\n });\n\n test(\"creates workflow run with deadline\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"deadline-test\" }, noopFn);\n const deadline = new Date(Date.now() + 60_000); // in 1 minute\n const handle = await workflow.run({ value: 1 }, { deadlineAt: deadline });\n\n expect(handle.workflowRun.deadlineAt).not.toBeNull();\n expect(handle.workflowRun.deadlineAt?.getTime()).toBe(deadline.getTime());\n });\n\n test(\"creates workflow run with version\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"versioned-test\", version: \"v2.0\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n expect(handle.workflowRun.version).toBe(\"v2.0\");\n });\n\n test(\"creates workflow run without version\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"unversioned-test\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n expect(handle.workflowRun.version).toBeNull();\n });\n\n test(\"cancels workflow run via handle\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"cancel-test\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n await handle.cancel();\n\n const workflowRun = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(workflowRun?.status).toBe(\"canceled\");\n expect(workflowRun?.finishedAt).not.toBeNull();\n });\n\n describe(\"declareWorkflow / implementWorkflow API\", () => {\n test(\"declareWorkflow returns a spec that can be used to schedule runs\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({ name: \"declare-test\" });\n\n const handle = await client.runWorkflow(spec, { message: \"hello\" });\n expect(handle.workflowRun.workflowName).toBe(\"declare-test\");\n\n await handle.cancel();\n });\n\n test(\"implementWorkflow registers the workflow for worker execution\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({ name: \"implement-test\" });\n client.implementWorkflow(spec, ({ input }) => {\n return { received: input };\n });\n\n const handle = await client.runWorkflow(spec, { data: 42 });\n const worker = client.newWorker();\n await worker.tick();\n await sleep(100); // wait for background execution\n\n const result = await handle.result();\n expect(result).toEqual({ received: { data: 42 } });\n });\n\n test(\"implementWorkflow throws when workflow is already registered\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({ name: \"duplicate-test\" });\n client.implementWorkflow(spec, noopFn);\n\n expect(() => {\n client.implementWorkflow(spec, noopFn);\n }).toThrow('Workflow \"duplicate-test\" is already registered');\n });\n\n test(\"implementWorkflow allows registering different versions of the same workflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const specV1 = declareWorkflow({\n name: \"multi-version\",\n version: \"v1\",\n });\n const specV2 = declareWorkflow({\n name: \"multi-version\",\n version: \"v2\",\n });\n\n // no throwing...\n client.implementWorkflow(specV1, noopFn);\n client.implementWorkflow(specV2, noopFn);\n });\n\n test(\"implementWorkflow throws for same name+version combination\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec1 = declareWorkflow({\n name: \"version-duplicate\",\n version: \"v1\",\n });\n const spec2 = declareWorkflow({\n name: \"version-duplicate\",\n version: \"v1\",\n });\n\n client.implementWorkflow(spec1, noopFn);\n\n expect(() => {\n client.implementWorkflow(spec2, noopFn);\n }).toThrow('Workflow \"version-duplicate\" (version: v1) is already registered');\n });\n\n test(\"declareWorkflow with schema validates input on runWorkflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const schema = z.object({\n email: z.email(),\n });\n const spec = declareWorkflow({\n name: \"declare-schema-test\",\n schema,\n });\n\n const handle = await client.runWorkflow(spec, {\n email: \"test@example.com\",\n });\n await handle.cancel();\n\n await expect(client.runWorkflow(spec, { email: \"not-an-email\" })).rejects.toThrow();\n });\n\n test(\"declareWorkflow with version sets version on workflow run\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({\n name: \"declare-version-test\",\n version: \"v1.2.3\",\n });\n\n const handle = await client.runWorkflow(spec);\n expect(handle.workflowRun.version).toBe(\"v1.2.3\");\n\n await handle.cancel();\n });\n\n test(\"defineWorkflow wraps declareWorkflow and implementWorkflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"define-wrap-test\" }, ({ input }) => ({\n doubled: (input as { n: number }).n * 2,\n }));\n\n const handle = await workflow.run({ n: 21 });\n const worker = client.newWorker();\n await worker.tick();\n await sleep(100); // wait for background execution\n\n const result = await handle.result();\n expect(result).toEqual({ doubled: 42 });\n });\n });\n});\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function noopFn() {\n // no-op\n}\n"],"names":["randomUUID","afterEach","beforeEach","describe","expect","test","z","BackendPostgres","declareWorkflow","OpenWorkflow","KNEX_GLOBAL_CONFIG","backend","connect","namespaceId","runMigrations","stop","client","workflow","defineWorkflow","name","noopFn","run","docUrl","workerId","claimed","claimWorkflowRun","leaseDurationMs","workflowName","toBe","input","toEqual","schema","object","userId","uuid","count","number","int","positive","handle","cancel","rejects","toThrow","value","not","toBeNull","Error","completeWorkflowRun","workflowRunId","id","output","ok","result","failWorkflowRun","error","message","rescheduled","getWorkflowRun","status","deadline","Date","now","deadlineAt","workflowRun","getTime","version","finishedAt","spec","runWorkflow","implementWorkflow","received","data","worker","newWorker","tick","sleep","specV1","specV2","spec1","spec2","email","doubled","n","ms","Promise","resolve","setTimeout"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/client.test.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { afterEach, beforeEach, describe, expect, test } from \"vitest\";\nimport { z } from \"zod\";\nimport { BackendPostgres } from \".\";\nimport { declareWorkflow, OpenWorkflow } from \"./client\";\nimport { KNEX_GLOBAL_CONFIG } from \"./testing/connection\";\n\ndescribe(\"OpenWorkflow\", () => {\n let backend: BackendPostgres;\n\n beforeEach(async () => {\n backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {\n namespaceId: randomUUID(),\n runMigrations: false,\n });\n await backend.initialize();\n });\n\n afterEach(async () => {\n await backend.stop();\n });\n\n test(\"enqueues workflow runs via backend\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"enqueue-test\" }, noopFn);\n await workflow.run({ docUrl: \"https://example.com\" });\n\n const workerId = \"enqueue-worker\";\n const claimed = await backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: 1000,\n });\n\n expect(claimed?.workflowName).toBe(\"enqueue-test\");\n expect(claimed?.workerId).toBe(workerId);\n expect(claimed?.input).toEqual({ docUrl: \"https://example.com\" });\n });\n\n describe(\"schema validation\", () => {\n describe(\"Zod schema\", () => {\n const schema = z.object({\n userId: z.uuid(),\n count: z.number().int().positive(),\n });\n\n test(\"accepts valid input\", async () => {\n const client = new OpenWorkflow({ backend });\n const workflow = client.defineWorkflow({ name: \"schema-zod-valid\", schema }, noopFn);\n\n const handle = await workflow.run({\n userId: randomUUID(),\n count: 3,\n });\n\n await handle.cancel();\n });\n\n test(\"rejects invalid input\", async () => {\n const client = new OpenWorkflow({ backend });\n const workflow = client.defineWorkflow({ name: \"schema-zod-invalid\", schema }, noopFn);\n\n await expect(workflow.run({ userId: \"not-a-uuid\", count: 0 } as never)).rejects.toThrow();\n });\n });\n });\n\n test(\"result resolves when workflow succeeds\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"result-success\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n const workerId = \"test-worker\";\n const claimed = await backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: 1000,\n });\n expect(claimed).not.toBeNull();\n if (!claimed) throw new Error(\"workflow run was not claimed\");\n\n await backend.completeWorkflowRun({\n workflowRunId: claimed.id,\n workerId,\n output: { ok: true },\n });\n\n // eslint-disable-next-line @typescript-eslint/no-confusing-void-expression\n const result = await handle.result();\n expect(result).toEqual({ ok: true });\n });\n\n test(\"result rejects when workflow fails\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"result-failure\" }, noopFn);\n await workflow.run({ value: 1 });\n\n const workerId = \"test-worker\";\n const claimed = await backend.claimWorkflowRun({\n workerId,\n leaseDurationMs: 1000,\n });\n expect(claimed).not.toBeNull();\n if (!claimed) throw new Error(\"workflow run was not claimed\");\n\n // mark as failed (should reschedule))\n await backend.failWorkflowRun({\n workflowRunId: claimed.id,\n workerId,\n error: { message: \"boom\" },\n });\n\n const rescheduled = await backend.getWorkflowRun({\n workflowRunId: claimed.id,\n });\n expect(rescheduled?.status).toBe(\"pending\");\n expect(rescheduled?.error).toEqual({ message: \"boom\" });\n });\n\n test(\"creates workflow run with deadline\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"deadline-test\" }, noopFn);\n const deadline = new Date(Date.now() + 60_000); // in 1 minute\n const handle = await workflow.run({ value: 1 }, { deadlineAt: deadline });\n\n expect(handle.workflowRun.deadlineAt).not.toBeNull();\n expect(handle.workflowRun.deadlineAt?.getTime()).toBe(deadline.getTime());\n });\n\n test(\"creates workflow run with version\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"versioned-test\", version: \"v2.0\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n expect(handle.workflowRun.version).toBe(\"v2.0\");\n });\n\n test(\"creates workflow run without version\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"unversioned-test\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n expect(handle.workflowRun.version).toBeNull();\n });\n\n test(\"cancels workflow run via handle\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"cancel-test\" }, noopFn);\n const handle = await workflow.run({ value: 1 });\n\n await handle.cancel();\n\n const workflowRun = await backend.getWorkflowRun({\n workflowRunId: handle.workflowRun.id,\n });\n expect(workflowRun?.status).toBe(\"canceled\");\n expect(workflowRun?.finishedAt).not.toBeNull();\n });\n\n describe(\"declareWorkflow / implementWorkflow API\", () => {\n test(\"declareWorkflow returns a spec that can be used to schedule runs\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({ name: \"declare-test\" });\n\n const handle = await client.runWorkflow(spec, { message: \"hello\" });\n expect(handle.workflowRun.workflowName).toBe(\"declare-test\");\n\n await handle.cancel();\n });\n\n test(\"implementWorkflow registers the workflow for worker execution\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({ name: \"implement-test\" });\n client.implementWorkflow(spec, ({ input }) => {\n return { received: input };\n });\n\n const handle = await client.runWorkflow(spec, { data: 42 });\n const worker = client.newWorker();\n await worker.tick();\n await sleep(100); // wait for background execution\n\n const result = await handle.result();\n expect(result).toEqual({ received: { data: 42 } });\n });\n\n test(\"implementWorkflow throws when workflow is already registered\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({ name: \"duplicate-test\" });\n client.implementWorkflow(spec, noopFn);\n\n expect(() => {\n client.implementWorkflow(spec, noopFn);\n }).toThrow('Workflow \"duplicate-test\" is already registered');\n });\n\n test(\"implementWorkflow allows registering different versions of the same workflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const specV1 = declareWorkflow({\n name: \"multi-version\",\n version: \"v1\",\n });\n const specV2 = declareWorkflow({\n name: \"multi-version\",\n version: \"v2\",\n });\n\n // no throwing...\n client.implementWorkflow(specV1, noopFn);\n client.implementWorkflow(specV2, noopFn);\n });\n\n test(\"implementWorkflow throws for same name+version combination\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec1 = declareWorkflow({\n name: \"version-duplicate\",\n version: \"v1\",\n });\n const spec2 = declareWorkflow({\n name: \"version-duplicate\",\n version: \"v1\",\n });\n\n client.implementWorkflow(spec1, noopFn);\n\n expect(() => {\n client.implementWorkflow(spec2, noopFn);\n }).toThrow('Workflow \"version-duplicate\" (version: v1) is already registered');\n });\n\n test(\"declareWorkflow with schema validates input on runWorkflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const schema = z.object({\n email: z.email(),\n });\n const spec = declareWorkflow({\n name: \"declare-schema-test\",\n schema,\n });\n\n const handle = await client.runWorkflow(spec, {\n email: \"test@example.com\",\n });\n await handle.cancel();\n\n await expect(client.runWorkflow(spec, { email: \"not-an-email\" })).rejects.toThrow();\n });\n\n test(\"declareWorkflow with version sets version on workflow run\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const spec = declareWorkflow({\n name: \"declare-version-test\",\n version: \"v1.2.3\",\n });\n\n const handle = await client.runWorkflow(spec);\n expect(handle.workflowRun.version).toBe(\"v1.2.3\");\n\n await handle.cancel();\n });\n\n test(\"defineWorkflow wraps declareWorkflow and implementWorkflow\", async () => {\n const client = new OpenWorkflow({ backend });\n\n const workflow = client.defineWorkflow({ name: \"define-wrap-test\" }, ({ input }) => ({\n doubled: (input as { n: number }).n * 2,\n }));\n\n const handle = await workflow.run({ n: 21 });\n const worker = client.newWorker();\n await worker.tick();\n await sleep(100); // wait for background execution\n\n const result = await handle.result();\n expect(result).toEqual({ doubled: 42 });\n });\n });\n});\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function noopFn() {\n // no-op\n}\n"],"names":["randomUUID","afterEach","beforeEach","describe","expect","test","z","BackendPostgres","declareWorkflow","OpenWorkflow","KNEX_GLOBAL_CONFIG","backend","namespaceId","runMigrations","initialize","stop","client","workflow","defineWorkflow","name","noopFn","run","docUrl","workerId","claimed","claimWorkflowRun","leaseDurationMs","workflowName","toBe","input","toEqual","schema","object","userId","uuid","count","number","int","positive","handle","cancel","rejects","toThrow","value","not","toBeNull","Error","completeWorkflowRun","workflowRunId","id","output","ok","result","failWorkflowRun","error","message","rescheduled","getWorkflowRun","status","deadline","Date","now","deadlineAt","workflowRun","getTime","version","finishedAt","spec","runWorkflow","implementWorkflow","received","data","worker","newWorker","tick","sleep","specV1","specV2","spec1","spec2","email","doubled","n","ms","Promise","resolve","setTimeout"],"mappings":"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"}
|
|
@@ -13,19 +13,18 @@ interface BackendPostgresOptions {
|
|
|
13
13
|
* Manages a connection to a Postgres database for workflow operations.
|
|
14
14
|
*/
|
|
15
15
|
export declare class BackendPostgres implements Backend {
|
|
16
|
-
private
|
|
16
|
+
private config;
|
|
17
17
|
private namespaceId;
|
|
18
18
|
private usePubSub;
|
|
19
19
|
private pubsub;
|
|
20
|
-
private
|
|
20
|
+
private initialized;
|
|
21
|
+
private runMigrations;
|
|
22
|
+
private _knex;
|
|
23
|
+
private get knex();
|
|
24
|
+
constructor(config: Knex.Config, options?: BackendPostgresOptions);
|
|
25
|
+
initialize(): Promise<void>;
|
|
21
26
|
subscribe(callback: OnSubscribed): Promise<void>;
|
|
22
27
|
publish(payload?: string): Promise<void>;
|
|
23
|
-
/**
|
|
24
|
-
* Create and initialize a new BackendPostgres instance. This will
|
|
25
|
-
* automatically run migrations on startup unless `runMigrations` is set to
|
|
26
|
-
* false.
|
|
27
|
-
*/
|
|
28
|
-
static connect(dbConf: Knex.Config, options?: BackendPostgresOptions): Promise<BackendPostgres>;
|
|
29
28
|
stop(): Promise<void>;
|
|
30
29
|
createWorkflowRun(params: CreateWorkflowRunParams): Promise<WorkflowRun>;
|
|
31
30
|
getWorkflowRun(params: GetWorkflowRunParams): Promise<WorkflowRun | null>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/database/backend.ts"],"names":[],"mappings":"AACA,OAAa,EAAE,KAAK,IAAI,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,uBAAuB,EAC5B,KAAK,sBAAsB,EAC3B,KAAK,yBAAyB,EAC9B,KAAK,yBAAyB,EAC9B,KAAK,uBAAuB,EAC5B,KAAK,uBAAuB,EAE5B,KAAK,4BAA4B,EACjC,KAAK,qBAAqB,EAC1B,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,oBAAoB,EACzB,KAAK,sBAAsB,EAC3B,KAAK,sBAAsB,EAC3B,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC5B,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,UAAU,CAAC;AAE7D,eAAO,MAAM,sBAAsB,EAAG,WAAoB,CAAC;AAG3D,UAAU,sBAAsB;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,OAAO,CAAC;IAGxB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;GAEG;AACH,qBAAa,eAAgB,YAAW,OAAO;IAC7C,OAAO,CAAC,
|
|
1
|
+
{"version":3,"file":"backend.d.ts","sourceRoot":"","sources":["../../src/database/backend.ts"],"names":[],"mappings":"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"}
|
package/dist/database/backend.js
CHANGED
|
@@ -9,16 +9,62 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
|
|
|
9
9
|
/**
|
|
10
10
|
* Manages a connection to a Postgres database for workflow operations.
|
|
11
11
|
*/ export class BackendPostgres {
|
|
12
|
-
|
|
12
|
+
config;
|
|
13
13
|
namespaceId;
|
|
14
14
|
usePubSub;
|
|
15
15
|
pubsub = null;
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
initialized = false;
|
|
17
|
+
runMigrations;
|
|
18
|
+
_knex = null;
|
|
19
|
+
get knex() {
|
|
20
|
+
if (!this._knex) {
|
|
21
|
+
this._knex = knex(this.config);
|
|
22
|
+
}
|
|
23
|
+
return this._knex;
|
|
24
|
+
}
|
|
25
|
+
constructor(config, options){
|
|
26
|
+
this.config = {
|
|
27
|
+
...config,
|
|
28
|
+
postProcessResponse: (result, _queryContext)=>{
|
|
29
|
+
if (result === null || result === undefined) {
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
if (config?.postProcessResponse) {
|
|
33
|
+
result = config.postProcessResponse(result, _queryContext);
|
|
34
|
+
}
|
|
35
|
+
const camelizeRow = (row)=>Object.fromEntries(Object.entries(row).map(([key, value])=>[
|
|
36
|
+
camelize(key, true),
|
|
37
|
+
value
|
|
38
|
+
]));
|
|
39
|
+
if (Array.isArray(result)) {
|
|
40
|
+
return result.map(camelizeRow);
|
|
41
|
+
}
|
|
42
|
+
return camelizeRow(result);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const { namespaceId, usePubSub, runMigrations } = {
|
|
46
|
+
namespaceId: DEFAULT_NAMESPACE_ID,
|
|
47
|
+
usePubSub: true,
|
|
48
|
+
runMigrations: true,
|
|
49
|
+
...options
|
|
50
|
+
};
|
|
18
51
|
this.namespaceId = namespaceId;
|
|
19
52
|
this.usePubSub = usePubSub;
|
|
53
|
+
this.runMigrations = runMigrations;
|
|
54
|
+
}
|
|
55
|
+
async initialize() {
|
|
56
|
+
if (this.initialized) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (this.runMigrations) {
|
|
60
|
+
await migrate(this.config, DEFAULT_SCHEMA);
|
|
61
|
+
}
|
|
62
|
+
this.initialized = true;
|
|
20
63
|
}
|
|
21
64
|
async subscribe(callback) {
|
|
65
|
+
if (!this.initialized) {
|
|
66
|
+
throw new Error("Backend not initialized");
|
|
67
|
+
}
|
|
22
68
|
if (!this.usePubSub) {
|
|
23
69
|
return;
|
|
24
70
|
}
|
|
@@ -28,53 +74,26 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
|
|
|
28
74
|
this.pubsub.listenEvent(DEFAULT_LISTEN_CHANNEL, callback);
|
|
29
75
|
}
|
|
30
76
|
async publish(payload) {
|
|
77
|
+
if (!this.initialized) {
|
|
78
|
+
throw new Error("Backend not initialized");
|
|
79
|
+
}
|
|
31
80
|
if (!this.usePubSub) {
|
|
32
81
|
return;
|
|
33
82
|
}
|
|
34
83
|
await this.knex.raw(payload ? `NOTIFY ${DEFAULT_LISTEN_CHANNEL}, '${payload}'` : `NOTIFY ${DEFAULT_LISTEN_CHANNEL}`);
|
|
35
84
|
}
|
|
36
|
-
/**
|
|
37
|
-
* Create and initialize a new BackendPostgres instance. This will
|
|
38
|
-
* automatically run migrations on startup unless `runMigrations` is set to
|
|
39
|
-
* false.
|
|
40
|
-
*/ static async connect(dbConf, options) {
|
|
41
|
-
const postProcessResponse = (result, _queryContext)=>{
|
|
42
|
-
if (result === null || result === undefined) {
|
|
43
|
-
return result;
|
|
44
|
-
}
|
|
45
|
-
if (dbConf?.postProcessResponse) {
|
|
46
|
-
result = dbConf.postProcessResponse(result, _queryContext);
|
|
47
|
-
}
|
|
48
|
-
const camelizeRow = (row)=>Object.fromEntries(Object.entries(row).map(([key, value])=>[
|
|
49
|
-
camelize(key, true),
|
|
50
|
-
value
|
|
51
|
-
]));
|
|
52
|
-
if (Array.isArray(result)) {
|
|
53
|
-
return result.map(camelizeRow);
|
|
54
|
-
}
|
|
55
|
-
return camelizeRow(result);
|
|
56
|
-
};
|
|
57
|
-
const { namespaceId, runMigrations, usePubSub } = {
|
|
58
|
-
namespaceId: DEFAULT_NAMESPACE_ID,
|
|
59
|
-
runMigrations: true,
|
|
60
|
-
usePubSub: true,
|
|
61
|
-
...options
|
|
62
|
-
};
|
|
63
|
-
const knexInstance = knex({
|
|
64
|
-
...dbConf,
|
|
65
|
-
postProcessResponse
|
|
66
|
-
});
|
|
67
|
-
if (runMigrations) {
|
|
68
|
-
await migrate(knexInstance, DEFAULT_SCHEMA);
|
|
69
|
-
}
|
|
70
|
-
return new BackendPostgres(knexInstance, namespaceId, usePubSub);
|
|
71
|
-
}
|
|
72
85
|
async stop() {
|
|
86
|
+
if (!this.initialized) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
73
89
|
await this.pubsub?.destroy();
|
|
74
90
|
this.pubsub = null;
|
|
75
91
|
await this.knex.destroy();
|
|
76
92
|
}
|
|
77
93
|
async createWorkflowRun(params) {
|
|
94
|
+
if (!this.initialized) {
|
|
95
|
+
throw new Error("Backend not initialized");
|
|
96
|
+
}
|
|
78
97
|
const qb = this.knex.withSchema(DEFAULT_SCHEMA).table("workflow_runs").insert({
|
|
79
98
|
namespace_id: this.namespaceId,
|
|
80
99
|
id: crypto.randomUUID(),
|
|
@@ -98,10 +117,16 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
|
|
|
98
117
|
return workflowRun[0];
|
|
99
118
|
}
|
|
100
119
|
async getWorkflowRun(params) {
|
|
120
|
+
if (!this.initialized) {
|
|
121
|
+
throw new Error("Backend not initialized");
|
|
122
|
+
}
|
|
101
123
|
const workflowRun = await this.knex.withSchema(DEFAULT_SCHEMA).table("workflow_runs").where("namespace_id", this.namespaceId).where("id", params.workflowRunId).select("namespace_id", "id", "workflow_name", "version", "status", "idempotency_key", "config", "context", "input", "output", "error", "attempts", "parent_step_attempt_namespace_id", "parent_step_attempt_id", "worker_id", "available_at", "deadline_at", "started_at", "finished_at", "created_at", "updated_at").first();
|
|
102
124
|
return workflowRun ?? null;
|
|
103
125
|
}
|
|
104
126
|
async listWorkflowRuns(params) {
|
|
127
|
+
if (!this.initialized) {
|
|
128
|
+
throw new Error("Backend not initialized");
|
|
129
|
+
}
|
|
105
130
|
const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;
|
|
106
131
|
const { after, before } = params;
|
|
107
132
|
let cursor = null;
|
|
@@ -127,6 +152,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
|
|
|
127
152
|
return qb;
|
|
128
153
|
}
|
|
129
154
|
async claimWorkflowRun(params) {
|
|
155
|
+
if (!this.initialized) {
|
|
156
|
+
throw new Error("Backend not initialized");
|
|
157
|
+
}
|
|
130
158
|
const claimed = await this.knex.with("expired", (qb)=>qb.withSchema(DEFAULT_SCHEMA).table("workflow_runs").update({
|
|
131
159
|
status: "failed",
|
|
132
160
|
error: JSON.stringify({
|
|
@@ -157,6 +185,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
|
|
|
157
185
|
return claimed[0] ?? null;
|
|
158
186
|
}
|
|
159
187
|
async extendWorkflowRunLease(params) {
|
|
188
|
+
if (!this.initialized) {
|
|
189
|
+
throw new Error("Backend not initialized");
|
|
190
|
+
}
|
|
160
191
|
const [updated] = await this.knex.withSchema(DEFAULT_SCHEMA).table("workflow_runs").where("namespace_id", this.namespaceId).where("id", params.workflowRunId).where("status", "running").where("worker_id", params.workerId).update({
|
|
161
192
|
available_at: this.knex.raw(`NOW() + ${params.leaseDurationMs} * INTERVAL '1 millisecond'`),
|
|
162
193
|
updated_at: this.knex.fn.now()
|
|
@@ -167,6 +198,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
|
|
|
167
198
|
return updated;
|
|
168
199
|
}
|
|
169
200
|
async sleepWorkflowRun(params) {
|
|
201
|
+
if (!this.initialized) {
|
|
202
|
+
throw new Error("Backend not initialized");
|
|
203
|
+
}
|
|
170
204
|
// 'succeeded' status is deprecated
|
|
171
205
|
const [updated] = await this.knex.withSchema(DEFAULT_SCHEMA).table("workflow_runs").where("namespace_id", this.namespaceId).where("id", params.workflowRunId).whereNotIn("status", [
|
|
172
206
|
"succeeded",
|
|
@@ -185,6 +219,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
|
|
|
185
219
|
return updated;
|
|
186
220
|
}
|
|
187
221
|
async completeWorkflowRun(params) {
|
|
222
|
+
if (!this.initialized) {
|
|
223
|
+
throw new Error("Backend not initialized");
|
|
224
|
+
}
|
|
188
225
|
const [updated] = await this.knex.withSchema(DEFAULT_SCHEMA).table("workflow_runs").where("namespace_id", this.namespaceId).where("id", params.workflowRunId).where("status", "running").where("worker_id", params.workerId).update({
|
|
189
226
|
status: "completed",
|
|
190
227
|
output: JSON.stringify(params.output),
|
|
@@ -200,6 +237,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
|
|
|
200
237
|
return updated;
|
|
201
238
|
}
|
|
202
239
|
async failWorkflowRun(params) {
|
|
240
|
+
if (!this.initialized) {
|
|
241
|
+
throw new Error("Backend not initialized");
|
|
242
|
+
}
|
|
203
243
|
const { workflowRunId, error } = params;
|
|
204
244
|
const { initialIntervalMs, backoffCoefficient, maximumIntervalMs } = DEFAULT_RETRY_POLICY;
|
|
205
245
|
// this beefy query updates a workflow's status, available_at, and
|
|
@@ -225,6 +265,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
|
|
|
225
265
|
return updated;
|
|
226
266
|
}
|
|
227
267
|
async cancelWorkflowRun(params) {
|
|
268
|
+
if (!this.initialized) {
|
|
269
|
+
throw new Error("Backend not initialized");
|
|
270
|
+
}
|
|
228
271
|
const [updated] = await this.knex.withSchema(DEFAULT_SCHEMA).table("workflow_runs").where("namespace_id", this.namespaceId).where("id", params.workflowRunId).whereIn("status", [
|
|
229
272
|
"pending",
|
|
230
273
|
"running",
|
|
@@ -262,6 +305,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
|
|
|
262
305
|
return updated;
|
|
263
306
|
}
|
|
264
307
|
async createStepAttempt(params) {
|
|
308
|
+
if (!this.initialized) {
|
|
309
|
+
throw new Error("Backend not initialized");
|
|
310
|
+
}
|
|
265
311
|
const [stepAttempt] = await this.knex.withSchema(DEFAULT_SCHEMA).table("step_attempts").insert({
|
|
266
312
|
namespace_id: this.namespaceId,
|
|
267
313
|
id: crypto.randomUUID(),
|
|
@@ -281,10 +327,16 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
|
|
|
281
327
|
return stepAttempt;
|
|
282
328
|
}
|
|
283
329
|
async getStepAttempt(params) {
|
|
330
|
+
if (!this.initialized) {
|
|
331
|
+
throw new Error("Backend not initialized");
|
|
332
|
+
}
|
|
284
333
|
const stepAttempt = await this.knex.withSchema(DEFAULT_SCHEMA).table("step_attempts").where("namespace_id", this.namespaceId).where("id", params.stepAttemptId).first();
|
|
285
334
|
return stepAttempt ?? null;
|
|
286
335
|
}
|
|
287
336
|
async listStepAttempts(params) {
|
|
337
|
+
if (!this.initialized) {
|
|
338
|
+
throw new Error("Backend not initialized");
|
|
339
|
+
}
|
|
288
340
|
const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;
|
|
289
341
|
const { after, before } = params;
|
|
290
342
|
let cursor = null;
|
|
@@ -342,6 +394,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
|
|
|
342
394
|
};
|
|
343
395
|
}
|
|
344
396
|
async completeStepAttempt(params) {
|
|
397
|
+
if (!this.initialized) {
|
|
398
|
+
throw new Error("Backend not initialized");
|
|
399
|
+
}
|
|
345
400
|
const [updated] = await this.knex.withSchema(DEFAULT_SCHEMA).table("step_attempts as sa").update({
|
|
346
401
|
status: "completed",
|
|
347
402
|
output: JSON.stringify(params.output),
|
|
@@ -355,6 +410,9 @@ const DEFAULT_PAGINATION_PAGE_SIZE = 100;
|
|
|
355
410
|
return updated;
|
|
356
411
|
}
|
|
357
412
|
async failStepAttempt(params) {
|
|
413
|
+
if (!this.initialized) {
|
|
414
|
+
throw new Error("Backend not initialized");
|
|
415
|
+
}
|
|
358
416
|
const [updated] = await this.knex.withSchema(DEFAULT_SCHEMA).table("step_attempts as sa").update({
|
|
359
417
|
status: "failed",
|
|
360
418
|
output: null,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/database/backend.ts"],"sourcesContent":["import { camelize } from \"inflection\";\nimport knex, { type Knex } from \"knex\";\nimport {\n type Backend,\n type CancelWorkflowRunParams,\n type ClaimWorkflowRunParams,\n type CompleteStepAttemptParams,\n type CompleteWorkflowRunParams,\n type CreateStepAttemptParams,\n type CreateWorkflowRunParams,\n DEFAULT_NAMESPACE_ID,\n type ExtendWorkflowRunLeaseParams,\n type FailStepAttemptParams,\n type FailWorkflowRunParams,\n type GetStepAttemptParams,\n type GetWorkflowRunParams,\n type ListStepAttemptsParams,\n type ListWorkflowRunsParams,\n type PaginatedResponse,\n type SleepWorkflowRunParams,\n} from \"../backend\";\nimport { DEFAULT_RETRY_POLICY } from \"../core/retry\";\nimport type { StepAttempt } from \"../core/step\";\nimport type { WorkflowRun } from \"../core/workflow\";\nimport { DEFAULT_SCHEMA, migrate } from \"./base\";\nimport { type OnSubscribed, PostgresPubSub } from \"./pubsub\";\n\nexport const DEFAULT_LISTEN_CHANNEL = \"new_tasks\" as const;\nconst DEFAULT_PAGINATION_PAGE_SIZE = 100 as const;\n\ninterface BackendPostgresOptions {\n namespaceId?: string;\n runMigrations?: boolean;\n\n // default: true\n usePubSub?: boolean;\n}\n\n/**\n * Manages a connection to a Postgres database for workflow operations.\n */\nexport class BackendPostgres implements Backend {\n private knex: Knex;\n private namespaceId: string;\n private usePubSub: boolean;\n private pubsub: PostgresPubSub | null = null;\n\n private constructor(knex: Knex, namespaceId: string, usePubSub: boolean) {\n this.knex = knex;\n this.namespaceId = namespaceId;\n this.usePubSub = usePubSub;\n }\n\n async subscribe(callback: OnSubscribed) {\n if (!this.usePubSub) {\n return;\n }\n\n if (!this.pubsub) {\n this.pubsub = await PostgresPubSub.create(this.knex);\n }\n\n this.pubsub.listenEvent(DEFAULT_LISTEN_CHANNEL, callback);\n }\n\n async publish(payload?: string): Promise<void> {\n if (!this.usePubSub) {\n return;\n }\n\n await this.knex.raw(\n payload\n ? `NOTIFY ${DEFAULT_LISTEN_CHANNEL}, '${payload}'`\n : `NOTIFY ${DEFAULT_LISTEN_CHANNEL}`,\n );\n }\n\n /**\n * Create and initialize a new BackendPostgres instance. This will\n * automatically run migrations on startup unless `runMigrations` is set to\n * false.\n */\n static async connect(\n dbConf: Knex.Config,\n options?: BackendPostgresOptions,\n ): Promise<BackendPostgres> {\n const postProcessResponse: Knex.Config[\"postProcessResponse\"] = (result, _queryContext) => {\n if (result === null || result === undefined) {\n return result;\n }\n\n if (dbConf?.postProcessResponse) {\n result = dbConf.postProcessResponse(result, _queryContext);\n }\n\n const camelizeRow = (row: Record<string, unknown>) =>\n Object.fromEntries(Object.entries(row).map(([key, value]) => [camelize(key, true), value]));\n\n if (Array.isArray(result)) {\n return result.map(camelizeRow);\n }\n\n return camelizeRow(result);\n };\n\n const { namespaceId, runMigrations, usePubSub } = {\n namespaceId: DEFAULT_NAMESPACE_ID,\n runMigrations: true,\n usePubSub: true,\n ...options,\n };\n\n const knexInstance = knex({ ...dbConf, postProcessResponse });\n if (runMigrations) {\n await migrate(knexInstance, DEFAULT_SCHEMA);\n }\n\n return new BackendPostgres(knexInstance, namespaceId, usePubSub);\n }\n\n async stop(): Promise<void> {\n await this.pubsub?.destroy();\n this.pubsub = null;\n await this.knex.destroy();\n }\n\n async createWorkflowRun(params: CreateWorkflowRunParams): Promise<WorkflowRun> {\n const qb = this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .insert({\n namespace_id: this.namespaceId,\n id: crypto.randomUUID(),\n workflow_name: params.workflowName,\n version: params.version,\n status: \"pending\",\n idempotency_key: params.idempotencyKey,\n config: params.config,\n context: params.context,\n input: params.input,\n attempts: 0,\n available_at: params.availableAt ?? this.knex.fn.now(),\n deadline_at: params.deadlineAt,\n created_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n const workflowRun = await qb;\n if (!workflowRun[0]) {\n throw new Error(\"Failed to create workflow run\");\n }\n\n return workflowRun[0];\n }\n\n async getWorkflowRun(params: GetWorkflowRunParams): Promise<WorkflowRun | null> {\n const workflowRun = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .select(\n \"namespace_id\",\n \"id\",\n \"workflow_name\",\n \"version\",\n \"status\",\n \"idempotency_key\",\n \"config\",\n \"context\",\n \"input\",\n \"output\",\n \"error\",\n \"attempts\",\n \"parent_step_attempt_namespace_id\",\n \"parent_step_attempt_id\",\n \"worker_id\",\n \"available_at\",\n \"deadline_at\",\n \"started_at\",\n \"finished_at\",\n \"created_at\",\n \"updated_at\",\n )\n .first();\n\n return workflowRun ?? null;\n }\n\n async listWorkflowRuns(params: ListWorkflowRunsParams): Promise<PaginatedResponse<WorkflowRun>> {\n const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;\n const { after, before } = params;\n\n let cursor: Cursor | null = null;\n if (after) {\n cursor = decodeCursor(after);\n } else if (before) {\n cursor = decodeCursor(before);\n }\n\n const qb = this.buildListWorkflowRunsWhere(params, cursor);\n const rows = await qb\n .orderBy(\"created_at\", before ? \"desc\" : \"asc\")\n .orderBy(\"id\", before ? \"desc\" : \"asc\")\n .limit(limit + 1);\n\n return this.processPaginationResults(\n rows,\n limit,\n typeof after === \"string\",\n typeof before === \"string\",\n );\n }\n\n private buildListWorkflowRunsWhere(params: ListWorkflowRunsParams, cursor: Cursor | null) {\n const { after } = params;\n const qb = this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId);\n\n if (cursor) {\n const operator = after ? \">\" : \"<\";\n return qb.whereRaw(`(\"created_at\", \"id\") ${operator} (?, ?)`, [\n cursor.createdAt.toISOString(),\n cursor.id,\n ]);\n }\n\n return qb;\n }\n\n async claimWorkflowRun(params: ClaimWorkflowRunParams): Promise<WorkflowRun | null> {\n const claimed = await this.knex\n .with(\"expired\", (qb) =>\n qb\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .update({\n status: \"failed\",\n error: JSON.stringify({ message: \"Workflow run deadline exceeded\" }),\n worker_id: null,\n available_at: null,\n finished_at: this.knex.raw(\"NOW()\"),\n updated_at: this.knex.raw(\"NOW()\"),\n })\n .where(\"namespace_id\", this.namespaceId)\n .whereIn(\"status\", [\"pending\", \"running\", \"sleeping\"])\n .whereNotNull(\"deadline_at\")\n .where(\"deadline_at\", \"<=\", this.knex.raw(\"NOW()\"))\n .returning(\"id\"),\n )\n .with(\"candidate\", (qb) =>\n qb\n .withSchema(DEFAULT_SCHEMA)\n .select(\"id\")\n .from(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .whereIn(\"status\", [\"pending\", \"running\", \"sleeping\"])\n .where(\"available_at\", \"<=\", this.knex.raw(\"NOW()\"))\n .where((qb2) => {\n qb2.whereNull(\"deadline_at\").orWhere(\"deadline_at\", \">\", this.knex.raw(\"NOW()\"));\n })\n .orderByRaw(\"CASE WHEN status = 'pending' THEN 0 ELSE 1 END\")\n .orderBy(\"available_at\", \"asc\")\n .orderBy(\"created_at\", \"asc\")\n .limit(1)\n .forUpdate()\n .skipLocked(),\n )\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs as wr\")\n .where(\"wr.namespace_id\", this.namespaceId)\n .where(\"wr.id\", this.knex.ref(\"candidate.id\"))\n .update({\n status: \"running\",\n attempts: this.knex.raw(\"wr.attempts + 1\"),\n worker_id: params.workerId,\n available_at: this.knex.raw(`NOW() + ${params.leaseDurationMs} * INTERVAL '1 millisecond'`),\n started_at: this.knex.raw(\"COALESCE(wr.started_at, NOW())\"),\n updated_at: this.knex.raw(\"NOW()\"),\n })\n .updateFrom(\"candidate\")\n .returning(\"wr.*\");\n\n return claimed[0] ?? null;\n }\n\n async extendWorkflowRunLease(params: ExtendWorkflowRunLeaseParams): Promise<WorkflowRun> {\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .where(\"status\", \"running\")\n .where(\"worker_id\", params.workerId)\n .update({\n available_at: this.knex.raw(`NOW() + ${params.leaseDurationMs} * INTERVAL '1 millisecond'`),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to extend lease for workflow run\");\n }\n\n return updated;\n }\n\n async sleepWorkflowRun(params: SleepWorkflowRunParams): Promise<WorkflowRun> {\n // 'succeeded' status is deprecated\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .whereNotIn(\"status\", [\"succeeded\", \"completed\", \"failed\", \"canceled\"])\n .where(\"worker_id\", params.workerId)\n .update({\n status: \"sleeping\",\n available_at: params.availableAt,\n worker_id: null,\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to sleep workflow run\");\n }\n\n return updated;\n }\n\n async completeWorkflowRun(params: CompleteWorkflowRunParams): Promise<WorkflowRun> {\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .where(\"status\", \"running\")\n .where(\"worker_id\", params.workerId)\n .update({\n status: \"completed\",\n output: JSON.stringify(params.output),\n error: null,\n worker_id: params.workerId,\n available_at: null,\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to complete workflow run\");\n }\n\n return updated;\n }\n\n async failWorkflowRun(params: FailWorkflowRunParams): Promise<WorkflowRun> {\n const { workflowRunId, error } = params;\n const { initialIntervalMs, backoffCoefficient, maximumIntervalMs } = DEFAULT_RETRY_POLICY;\n\n // this beefy query updates a workflow's status, available_at, and\n // finished_at based on the workflow's deadline and retry policy\n //\n // if the next retry would exceed the deadline, the run is marked as\n // 'failed' and finalized, otherwise, the run is rescheduled with an updated\n // 'available_at' timestamp for the next retry\n const retryIntervalExpr = `LEAST(${initialIntervalMs} * POWER(${backoffCoefficient}, \"attempts\" - 1), ${maximumIntervalMs}) * INTERVAL '1 millisecond'`;\n const deadlineExceededCondition = `\"deadline_at\" IS NOT NULL AND NOW() + (${retryIntervalExpr}) >= \"deadline_at\"`;\n\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", workflowRunId)\n .where(\"status\", \"running\")\n .where(\"worker_id\", params.workerId)\n .update({\n status: this.knex.raw(\n `CASE WHEN ${deadlineExceededCondition} THEN 'failed' ELSE 'pending' END`,\n ),\n available_at: this.knex.raw(\n `CASE WHEN ${deadlineExceededCondition} THEN NULL ELSE NOW() + (${retryIntervalExpr}) END`,\n ),\n finished_at: this.knex.raw(\n `CASE WHEN ${deadlineExceededCondition} THEN NOW() ELSE NULL END`,\n ),\n error: JSON.stringify(error),\n worker_id: null,\n started_at: null,\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to mark workflow run failed\");\n }\n\n return updated;\n }\n\n async cancelWorkflowRun(params: CancelWorkflowRunParams): Promise<WorkflowRun> {\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .whereIn(\"status\", [\"pending\", \"running\", \"sleeping\"])\n .update({\n status: \"canceled\",\n worker_id: null,\n available_at: null,\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n // workflow may already be in a terminal state\n const existing = await this.getWorkflowRun({\n workflowRunId: params.workflowRunId,\n });\n if (!existing) {\n throw new Error(`Workflow run ${params.workflowRunId} does not exist`);\n }\n\n // if already canceled, just return it\n if (existing.status === \"canceled\") {\n return existing;\n }\n\n // throw error for completed/failed workflows\n // 'succeeded' status is deprecated\n if ([\"succeeded\", \"completed\", \"failed\"].includes(existing.status)) {\n throw new Error(\n `Cannot cancel workflow run ${params.workflowRunId} with status ${existing.status}`,\n );\n }\n\n throw new Error(\"Failed to cancel workflow run\");\n }\n\n return updated;\n }\n\n async createStepAttempt(params: CreateStepAttemptParams): Promise<StepAttempt> {\n const [stepAttempt] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts\")\n .insert({\n namespace_id: this.namespaceId,\n id: crypto.randomUUID(),\n workflow_run_id: params.workflowRunId,\n step_name: params.stepName,\n kind: params.kind,\n status: \"running\",\n config: JSON.stringify(params.config),\n context: JSON.stringify(params.context),\n started_at: this.knex.fn.now(),\n created_at: this.knex.raw(\"date_trunc('milliseconds', NOW())\"),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!stepAttempt) {\n throw new Error(\"Failed to create step attempt\");\n }\n\n return stepAttempt;\n }\n\n async getStepAttempt(params: GetStepAttemptParams): Promise<StepAttempt | null> {\n const stepAttempt = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.stepAttemptId)\n .first();\n\n return stepAttempt ?? null;\n }\n\n async listStepAttempts(params: ListStepAttemptsParams): Promise<PaginatedResponse<StepAttempt>> {\n const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;\n const { after, before } = params;\n\n let cursor: Cursor | null = null;\n if (after) {\n cursor = decodeCursor(after);\n } else if (before) {\n cursor = decodeCursor(before);\n }\n\n const qb = this.buildListStepAttemptsWhere(params, cursor);\n const rows = await qb\n .orderBy(\"created_at\", before ? \"desc\" : \"asc\")\n .orderBy(\"id\", before ? \"desc\" : \"asc\")\n .limit(limit + 1);\n\n return this.processPaginationResults(\n rows,\n limit,\n typeof after === \"string\",\n typeof before === \"string\",\n );\n }\n\n private buildListStepAttemptsWhere(params: ListStepAttemptsParams, cursor: Cursor | null) {\n const { after } = params;\n const qb = this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"workflow_run_id\", params.workflowRunId);\n\n if (cursor) {\n const operator = after ? \">\" : \"<\";\n return qb.whereRaw(`(\"created_at\", \"id\") ${operator} (?, ?)`, [\n cursor.createdAt.toISOString(),\n cursor.id,\n ]);\n }\n\n return qb;\n }\n\n private processPaginationResults<T extends Cursor>(\n rows: T[],\n limit: number,\n hasAfter: boolean,\n hasBefore: boolean,\n ): PaginatedResponse<T> {\n const data = rows;\n let hasNext = false;\n let hasPrev = false;\n\n if (hasBefore) {\n data.reverse();\n if (data.length > limit) {\n hasPrev = true;\n data.shift();\n }\n hasNext = true;\n } else {\n if (data.length > limit) {\n hasNext = true;\n data.pop();\n }\n if (hasAfter) {\n hasPrev = true;\n }\n }\n\n const lastItem = data.at(-1);\n const nextCursor = hasNext && lastItem ? encodeCursor(lastItem) : null;\n const firstItem = data[0];\n const prevCursor = hasPrev && firstItem ? encodeCursor(firstItem) : null;\n\n return {\n data,\n pagination: {\n next: nextCursor,\n prev: prevCursor,\n },\n };\n }\n\n async completeStepAttempt(params: CompleteStepAttemptParams): Promise<StepAttempt> {\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts as sa\")\n .update({\n status: \"completed\",\n output: JSON.stringify(params.output),\n error: null,\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .updateFrom(`${DEFAULT_SCHEMA}.workflow_runs as wr`)\n .where(\"sa.namespace_id\", this.namespaceId)\n .where(\"sa.workflow_run_id\", params.workflowRunId)\n .where(\"sa.id\", params.stepAttemptId)\n .where(\"sa.status\", \"running\")\n .where(\"wr.namespace_id\", this.knex.ref(\"sa.namespace_id\"))\n .where(\"wr.id\", this.knex.ref(\"sa.workflow_run_id\"))\n .where(\"wr.status\", \"running\")\n .where(\"wr.worker_id\", params.workerId)\n .returning(\"sa.*\");\n\n if (!updated) {\n throw new Error(\"Failed to mark step attempt completed\");\n }\n\n return updated;\n }\n\n async failStepAttempt(params: FailStepAttemptParams): Promise<StepAttempt> {\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts as sa\")\n .update({\n status: \"failed\",\n output: null,\n error: JSON.stringify(params.error),\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .updateFrom(`${DEFAULT_SCHEMA}.workflow_runs as wr`)\n .where(\"sa.namespace_id\", this.namespaceId)\n .where(\"sa.workflow_run_id\", params.workflowRunId)\n .where(\"sa.id\", params.stepAttemptId)\n .where(\"sa.status\", \"running\")\n .where(\"wr.namespace_id\", this.knex.ref(\"sa.namespace_id\"))\n .where(\"wr.id\", this.knex.ref(\"sa.workflow_run_id\"))\n .where(\"wr.status\", \"running\")\n .where(\"wr.worker_id\", params.workerId)\n .returning(\"sa.*\");\n\n if (!updated) {\n throw new Error(\"Failed to mark step attempt failed\");\n }\n\n return updated;\n }\n}\n\n/**\n * Cursor used for pagination. Requires created_at and id fields. Because JS\n * Date does not natively support microsecond precision dates, created_at should\n * be stored with millisecond precision in paginated tables to avoid issues with\n * cursor comparisons.\n */\ninterface Cursor {\n createdAt: Date;\n id: string;\n}\n\nfunction encodeCursor(item: Cursor): string {\n const encoded = Buffer.from(\n JSON.stringify({ createdAt: item.createdAt.toISOString(), id: item.id }),\n ).toString(\"base64\");\n return encoded;\n}\n\nexport function decodeCursor(cursor: string): Cursor {\n const decoded = Buffer.from(cursor, \"base64\").toString(\"utf8\");\n const parsed = JSON.parse(decoded) as { createdAt: string; id: string };\n return {\n createdAt: new Date(parsed.createdAt),\n id: parsed.id,\n };\n}\n"],"names":["camelize","knex","DEFAULT_NAMESPACE_ID","DEFAULT_RETRY_POLICY","DEFAULT_SCHEMA","migrate","PostgresPubSub","DEFAULT_LISTEN_CHANNEL","DEFAULT_PAGINATION_PAGE_SIZE","BackendPostgres","namespaceId","usePubSub","pubsub","subscribe","callback","create","listenEvent","publish","payload","raw","connect","dbConf","options","postProcessResponse","result","_queryContext","undefined","camelizeRow","row","Object","fromEntries","entries","map","key","value","Array","isArray","runMigrations","knexInstance","stop","destroy","createWorkflowRun","params","qb","withSchema","table","insert","namespace_id","id","crypto","randomUUID","workflow_name","workflowName","version","status","idempotency_key","idempotencyKey","config","context","input","attempts","available_at","availableAt","fn","now","deadline_at","deadlineAt","created_at","updated_at","returning","workflowRun","Error","getWorkflowRun","where","workflowRunId","select","first","listWorkflowRuns","limit","after","before","cursor","decodeCursor","buildListWorkflowRunsWhere","rows","orderBy","processPaginationResults","operator","whereRaw","createdAt","toISOString","claimWorkflowRun","claimed","with","update","error","JSON","stringify","message","worker_id","finished_at","whereIn","whereNotNull","from","qb2","whereNull","orWhere","orderByRaw","forUpdate","skipLocked","ref","workerId","leaseDurationMs","started_at","updateFrom","extendWorkflowRunLease","updated","sleepWorkflowRun","whereNotIn","completeWorkflowRun","output","failWorkflowRun","initialIntervalMs","backoffCoefficient","maximumIntervalMs","retryIntervalExpr","deadlineExceededCondition","cancelWorkflowRun","existing","includes","createStepAttempt","stepAttempt","workflow_run_id","step_name","stepName","kind","getStepAttempt","stepAttemptId","listStepAttempts","buildListStepAttemptsWhere","hasAfter","hasBefore","data","hasNext","hasPrev","reverse","length","shift","pop","lastItem","at","nextCursor","encodeCursor","firstItem","prevCursor","pagination","next","prev","completeStepAttempt","failStepAttempt","item","encoded","Buffer","toString","decoded","parsed","parse","Date"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../src/database/backend.ts"],"sourcesContent":["import { camelize } from \"inflection\";\nimport knex, { type Knex } from \"knex\";\nimport {\n type Backend,\n type CancelWorkflowRunParams,\n type ClaimWorkflowRunParams,\n type CompleteStepAttemptParams,\n type CompleteWorkflowRunParams,\n type CreateStepAttemptParams,\n type CreateWorkflowRunParams,\n DEFAULT_NAMESPACE_ID,\n type ExtendWorkflowRunLeaseParams,\n type FailStepAttemptParams,\n type FailWorkflowRunParams,\n type GetStepAttemptParams,\n type GetWorkflowRunParams,\n type ListStepAttemptsParams,\n type ListWorkflowRunsParams,\n type PaginatedResponse,\n type SleepWorkflowRunParams,\n} from \"../backend\";\nimport { DEFAULT_RETRY_POLICY } from \"../core/retry\";\nimport type { StepAttempt } from \"../core/step\";\nimport type { WorkflowRun } from \"../core/workflow\";\nimport { DEFAULT_SCHEMA, migrate } from \"./base\";\nimport { type OnSubscribed, PostgresPubSub } from \"./pubsub\";\n\nexport const DEFAULT_LISTEN_CHANNEL = \"new_tasks\" as const;\nconst DEFAULT_PAGINATION_PAGE_SIZE = 100 as const;\n\ninterface BackendPostgresOptions {\n namespaceId?: string;\n runMigrations?: boolean;\n\n // default: true\n usePubSub?: boolean;\n}\n\n/**\n * Manages a connection to a Postgres database for workflow operations.\n */\nexport class BackendPostgres implements Backend {\n private config: Knex.Config;\n private namespaceId: string;\n private usePubSub: boolean;\n private pubsub: PostgresPubSub | null = null;\n private initialized: boolean = false;\n private runMigrations: boolean;\n\n private _knex: Knex | null = null;\n private get knex(): Knex {\n if (!this._knex) {\n this._knex = knex(this.config);\n }\n\n return this._knex;\n }\n\n constructor(config: Knex.Config, options?: BackendPostgresOptions) {\n this.config = {\n ...config,\n postProcessResponse: (result, _queryContext) => {\n if (result === null || result === undefined) {\n return result;\n }\n\n if (config?.postProcessResponse) {\n result = config.postProcessResponse(result, _queryContext);\n }\n\n const camelizeRow = (row: Record<string, unknown>) =>\n Object.fromEntries(\n Object.entries(row).map(([key, value]) => [camelize(key, true), value]),\n );\n\n if (Array.isArray(result)) {\n return result.map(camelizeRow);\n }\n\n return camelizeRow(result);\n },\n };\n\n const { namespaceId, usePubSub, runMigrations } = {\n namespaceId: DEFAULT_NAMESPACE_ID,\n usePubSub: true,\n runMigrations: true,\n ...options,\n };\n\n this.namespaceId = namespaceId;\n this.usePubSub = usePubSub;\n this.runMigrations = runMigrations;\n }\n\n async initialize() {\n if (this.initialized) {\n return;\n }\n\n if (this.runMigrations) {\n await migrate(this.config, DEFAULT_SCHEMA);\n }\n\n this.initialized = true;\n }\n\n async subscribe(callback: OnSubscribed) {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n if (!this.usePubSub) {\n return;\n }\n\n if (!this.pubsub) {\n this.pubsub = await PostgresPubSub.create(this.knex);\n }\n\n this.pubsub.listenEvent(DEFAULT_LISTEN_CHANNEL, callback);\n }\n\n async publish(payload?: string): Promise<void> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n if (!this.usePubSub) {\n return;\n }\n\n await this.knex.raw(\n payload\n ? `NOTIFY ${DEFAULT_LISTEN_CHANNEL}, '${payload}'`\n : `NOTIFY ${DEFAULT_LISTEN_CHANNEL}`,\n );\n }\n\n async stop(): Promise<void> {\n if (!this.initialized) {\n return;\n }\n\n await this.pubsub?.destroy();\n this.pubsub = null;\n await this.knex.destroy();\n }\n\n async createWorkflowRun(params: CreateWorkflowRunParams): Promise<WorkflowRun> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const qb = this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .insert({\n namespace_id: this.namespaceId,\n id: crypto.randomUUID(),\n workflow_name: params.workflowName,\n version: params.version,\n status: \"pending\",\n idempotency_key: params.idempotencyKey,\n config: params.config,\n context: params.context,\n input: params.input,\n attempts: 0,\n available_at: params.availableAt ?? this.knex.fn.now(),\n deadline_at: params.deadlineAt,\n created_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n const workflowRun = await qb;\n if (!workflowRun[0]) {\n throw new Error(\"Failed to create workflow run\");\n }\n\n return workflowRun[0];\n }\n\n async getWorkflowRun(params: GetWorkflowRunParams): Promise<WorkflowRun | null> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const workflowRun = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .select(\n \"namespace_id\",\n \"id\",\n \"workflow_name\",\n \"version\",\n \"status\",\n \"idempotency_key\",\n \"config\",\n \"context\",\n \"input\",\n \"output\",\n \"error\",\n \"attempts\",\n \"parent_step_attempt_namespace_id\",\n \"parent_step_attempt_id\",\n \"worker_id\",\n \"available_at\",\n \"deadline_at\",\n \"started_at\",\n \"finished_at\",\n \"created_at\",\n \"updated_at\",\n )\n .first();\n\n return workflowRun ?? null;\n }\n\n async listWorkflowRuns(params: ListWorkflowRunsParams): Promise<PaginatedResponse<WorkflowRun>> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;\n const { after, before } = params;\n\n let cursor: Cursor | null = null;\n if (after) {\n cursor = decodeCursor(after);\n } else if (before) {\n cursor = decodeCursor(before);\n }\n\n const qb = this.buildListWorkflowRunsWhere(params, cursor);\n const rows = await qb\n .orderBy(\"created_at\", before ? \"desc\" : \"asc\")\n .orderBy(\"id\", before ? \"desc\" : \"asc\")\n .limit(limit + 1);\n\n return this.processPaginationResults(\n rows,\n limit,\n typeof after === \"string\",\n typeof before === \"string\",\n );\n }\n\n private buildListWorkflowRunsWhere(params: ListWorkflowRunsParams, cursor: Cursor | null) {\n const { after } = params;\n const qb = this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId);\n\n if (cursor) {\n const operator = after ? \">\" : \"<\";\n return qb.whereRaw(`(\"created_at\", \"id\") ${operator} (?, ?)`, [\n cursor.createdAt.toISOString(),\n cursor.id,\n ]);\n }\n\n return qb;\n }\n\n async claimWorkflowRun(params: ClaimWorkflowRunParams): Promise<WorkflowRun | null> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const claimed = await this.knex\n .with(\"expired\", (qb) =>\n qb\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .update({\n status: \"failed\",\n error: JSON.stringify({ message: \"Workflow run deadline exceeded\" }),\n worker_id: null,\n available_at: null,\n finished_at: this.knex.raw(\"NOW()\"),\n updated_at: this.knex.raw(\"NOW()\"),\n })\n .where(\"namespace_id\", this.namespaceId)\n .whereIn(\"status\", [\"pending\", \"running\", \"sleeping\"])\n .whereNotNull(\"deadline_at\")\n .where(\"deadline_at\", \"<=\", this.knex.raw(\"NOW()\"))\n .returning(\"id\"),\n )\n .with(\"candidate\", (qb) =>\n qb\n .withSchema(DEFAULT_SCHEMA)\n .select(\"id\")\n .from(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .whereIn(\"status\", [\"pending\", \"running\", \"sleeping\"])\n .where(\"available_at\", \"<=\", this.knex.raw(\"NOW()\"))\n .where((qb2) => {\n qb2.whereNull(\"deadline_at\").orWhere(\"deadline_at\", \">\", this.knex.raw(\"NOW()\"));\n })\n .orderByRaw(\"CASE WHEN status = 'pending' THEN 0 ELSE 1 END\")\n .orderBy(\"available_at\", \"asc\")\n .orderBy(\"created_at\", \"asc\")\n .limit(1)\n .forUpdate()\n .skipLocked(),\n )\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs as wr\")\n .where(\"wr.namespace_id\", this.namespaceId)\n .where(\"wr.id\", this.knex.ref(\"candidate.id\"))\n .update({\n status: \"running\",\n attempts: this.knex.raw(\"wr.attempts + 1\"),\n worker_id: params.workerId,\n available_at: this.knex.raw(`NOW() + ${params.leaseDurationMs} * INTERVAL '1 millisecond'`),\n started_at: this.knex.raw(\"COALESCE(wr.started_at, NOW())\"),\n updated_at: this.knex.raw(\"NOW()\"),\n })\n .updateFrom(\"candidate\")\n .returning(\"wr.*\");\n\n return claimed[0] ?? null;\n }\n\n async extendWorkflowRunLease(params: ExtendWorkflowRunLeaseParams): Promise<WorkflowRun> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .where(\"status\", \"running\")\n .where(\"worker_id\", params.workerId)\n .update({\n available_at: this.knex.raw(`NOW() + ${params.leaseDurationMs} * INTERVAL '1 millisecond'`),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to extend lease for workflow run\");\n }\n\n return updated;\n }\n\n async sleepWorkflowRun(params: SleepWorkflowRunParams): Promise<WorkflowRun> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n // 'succeeded' status is deprecated\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .whereNotIn(\"status\", [\"succeeded\", \"completed\", \"failed\", \"canceled\"])\n .where(\"worker_id\", params.workerId)\n .update({\n status: \"sleeping\",\n available_at: params.availableAt,\n worker_id: null,\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to sleep workflow run\");\n }\n\n return updated;\n }\n\n async completeWorkflowRun(params: CompleteWorkflowRunParams): Promise<WorkflowRun> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .where(\"status\", \"running\")\n .where(\"worker_id\", params.workerId)\n .update({\n status: \"completed\",\n output: JSON.stringify(params.output),\n error: null,\n worker_id: params.workerId,\n available_at: null,\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to complete workflow run\");\n }\n\n return updated;\n }\n\n async failWorkflowRun(params: FailWorkflowRunParams): Promise<WorkflowRun> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const { workflowRunId, error } = params;\n const { initialIntervalMs, backoffCoefficient, maximumIntervalMs } = DEFAULT_RETRY_POLICY;\n\n // this beefy query updates a workflow's status, available_at, and\n // finished_at based on the workflow's deadline and retry policy\n //\n // if the next retry would exceed the deadline, the run is marked as\n // 'failed' and finalized, otherwise, the run is rescheduled with an updated\n // 'available_at' timestamp for the next retry\n const retryIntervalExpr = `LEAST(${initialIntervalMs} * POWER(${backoffCoefficient}, \"attempts\" - 1), ${maximumIntervalMs}) * INTERVAL '1 millisecond'`;\n const deadlineExceededCondition = `\"deadline_at\" IS NOT NULL AND NOW() + (${retryIntervalExpr}) >= \"deadline_at\"`;\n\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", workflowRunId)\n .where(\"status\", \"running\")\n .where(\"worker_id\", params.workerId)\n .update({\n status: this.knex.raw(\n `CASE WHEN ${deadlineExceededCondition} THEN 'failed' ELSE 'pending' END`,\n ),\n available_at: this.knex.raw(\n `CASE WHEN ${deadlineExceededCondition} THEN NULL ELSE NOW() + (${retryIntervalExpr}) END`,\n ),\n finished_at: this.knex.raw(\n `CASE WHEN ${deadlineExceededCondition} THEN NOW() ELSE NULL END`,\n ),\n error: JSON.stringify(error),\n worker_id: null,\n started_at: null,\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n throw new Error(\"Failed to mark workflow run failed\");\n }\n\n return updated;\n }\n\n async cancelWorkflowRun(params: CancelWorkflowRunParams): Promise<WorkflowRun> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"workflow_runs\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.workflowRunId)\n .whereIn(\"status\", [\"pending\", \"running\", \"sleeping\"])\n .update({\n status: \"canceled\",\n worker_id: null,\n available_at: null,\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!updated) {\n // workflow may already be in a terminal state\n const existing = await this.getWorkflowRun({\n workflowRunId: params.workflowRunId,\n });\n if (!existing) {\n throw new Error(`Workflow run ${params.workflowRunId} does not exist`);\n }\n\n // if already canceled, just return it\n if (existing.status === \"canceled\") {\n return existing;\n }\n\n // throw error for completed/failed workflows\n // 'succeeded' status is deprecated\n if ([\"succeeded\", \"completed\", \"failed\"].includes(existing.status)) {\n throw new Error(\n `Cannot cancel workflow run ${params.workflowRunId} with status ${existing.status}`,\n );\n }\n\n throw new Error(\"Failed to cancel workflow run\");\n }\n\n return updated;\n }\n\n async createStepAttempt(params: CreateStepAttemptParams): Promise<StepAttempt> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const [stepAttempt] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts\")\n .insert({\n namespace_id: this.namespaceId,\n id: crypto.randomUUID(),\n workflow_run_id: params.workflowRunId,\n step_name: params.stepName,\n kind: params.kind,\n status: \"running\",\n config: JSON.stringify(params.config),\n context: JSON.stringify(params.context),\n started_at: this.knex.fn.now(),\n created_at: this.knex.raw(\"date_trunc('milliseconds', NOW())\"),\n updated_at: this.knex.fn.now(),\n })\n .returning(\"*\");\n\n if (!stepAttempt) {\n throw new Error(\"Failed to create step attempt\");\n }\n\n return stepAttempt;\n }\n\n async getStepAttempt(params: GetStepAttemptParams): Promise<StepAttempt | null> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const stepAttempt = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"id\", params.stepAttemptId)\n .first();\n\n return stepAttempt ?? null;\n }\n\n async listStepAttempts(params: ListStepAttemptsParams): Promise<PaginatedResponse<StepAttempt>> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const limit = params.limit ?? DEFAULT_PAGINATION_PAGE_SIZE;\n const { after, before } = params;\n\n let cursor: Cursor | null = null;\n if (after) {\n cursor = decodeCursor(after);\n } else if (before) {\n cursor = decodeCursor(before);\n }\n\n const qb = this.buildListStepAttemptsWhere(params, cursor);\n const rows = await qb\n .orderBy(\"created_at\", before ? \"desc\" : \"asc\")\n .orderBy(\"id\", before ? \"desc\" : \"asc\")\n .limit(limit + 1);\n\n return this.processPaginationResults(\n rows,\n limit,\n typeof after === \"string\",\n typeof before === \"string\",\n );\n }\n\n private buildListStepAttemptsWhere(params: ListStepAttemptsParams, cursor: Cursor | null) {\n const { after } = params;\n const qb = this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts\")\n .where(\"namespace_id\", this.namespaceId)\n .where(\"workflow_run_id\", params.workflowRunId);\n\n if (cursor) {\n const operator = after ? \">\" : \"<\";\n return qb.whereRaw(`(\"created_at\", \"id\") ${operator} (?, ?)`, [\n cursor.createdAt.toISOString(),\n cursor.id,\n ]);\n }\n\n return qb;\n }\n\n private processPaginationResults<T extends Cursor>(\n rows: T[],\n limit: number,\n hasAfter: boolean,\n hasBefore: boolean,\n ): PaginatedResponse<T> {\n const data = rows;\n let hasNext = false;\n let hasPrev = false;\n\n if (hasBefore) {\n data.reverse();\n if (data.length > limit) {\n hasPrev = true;\n data.shift();\n }\n hasNext = true;\n } else {\n if (data.length > limit) {\n hasNext = true;\n data.pop();\n }\n if (hasAfter) {\n hasPrev = true;\n }\n }\n\n const lastItem = data.at(-1);\n const nextCursor = hasNext && lastItem ? encodeCursor(lastItem) : null;\n const firstItem = data[0];\n const prevCursor = hasPrev && firstItem ? encodeCursor(firstItem) : null;\n\n return {\n data,\n pagination: {\n next: nextCursor,\n prev: prevCursor,\n },\n };\n }\n\n async completeStepAttempt(params: CompleteStepAttemptParams): Promise<StepAttempt> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts as sa\")\n .update({\n status: \"completed\",\n output: JSON.stringify(params.output),\n error: null,\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .updateFrom(`${DEFAULT_SCHEMA}.workflow_runs as wr`)\n .where(\"sa.namespace_id\", this.namespaceId)\n .where(\"sa.workflow_run_id\", params.workflowRunId)\n .where(\"sa.id\", params.stepAttemptId)\n .where(\"sa.status\", \"running\")\n .where(\"wr.namespace_id\", this.knex.ref(\"sa.namespace_id\"))\n .where(\"wr.id\", this.knex.ref(\"sa.workflow_run_id\"))\n .where(\"wr.status\", \"running\")\n .where(\"wr.worker_id\", params.workerId)\n .returning(\"sa.*\");\n\n if (!updated) {\n throw new Error(\"Failed to mark step attempt completed\");\n }\n\n return updated;\n }\n\n async failStepAttempt(params: FailStepAttemptParams): Promise<StepAttempt> {\n if (!this.initialized) {\n throw new Error(\"Backend not initialized\");\n }\n\n const [updated] = await this.knex\n .withSchema(DEFAULT_SCHEMA)\n .table(\"step_attempts as sa\")\n .update({\n status: \"failed\",\n output: null,\n error: JSON.stringify(params.error),\n finished_at: this.knex.fn.now(),\n updated_at: this.knex.fn.now(),\n })\n .updateFrom(`${DEFAULT_SCHEMA}.workflow_runs as wr`)\n .where(\"sa.namespace_id\", this.namespaceId)\n .where(\"sa.workflow_run_id\", params.workflowRunId)\n .where(\"sa.id\", params.stepAttemptId)\n .where(\"sa.status\", \"running\")\n .where(\"wr.namespace_id\", this.knex.ref(\"sa.namespace_id\"))\n .where(\"wr.id\", this.knex.ref(\"sa.workflow_run_id\"))\n .where(\"wr.status\", \"running\")\n .where(\"wr.worker_id\", params.workerId)\n .returning(\"sa.*\");\n\n if (!updated) {\n throw new Error(\"Failed to mark step attempt failed\");\n }\n\n return updated;\n }\n}\n\n/**\n * Cursor used for pagination. Requires created_at and id fields. Because JS\n * Date does not natively support microsecond precision dates, created_at should\n * be stored with millisecond precision in paginated tables to avoid issues with\n * cursor comparisons.\n */\ninterface Cursor {\n createdAt: Date;\n id: string;\n}\n\nfunction encodeCursor(item: Cursor): string {\n const encoded = Buffer.from(\n JSON.stringify({ createdAt: item.createdAt.toISOString(), id: item.id }),\n ).toString(\"base64\");\n return encoded;\n}\n\nexport function decodeCursor(cursor: string): Cursor {\n const decoded = Buffer.from(cursor, \"base64\").toString(\"utf8\");\n const parsed = JSON.parse(decoded) as { createdAt: string; id: string };\n return {\n createdAt: new Date(parsed.createdAt),\n id: parsed.id,\n };\n}\n"],"names":["camelize","knex","DEFAULT_NAMESPACE_ID","DEFAULT_RETRY_POLICY","DEFAULT_SCHEMA","migrate","PostgresPubSub","DEFAULT_LISTEN_CHANNEL","DEFAULT_PAGINATION_PAGE_SIZE","BackendPostgres","config","namespaceId","usePubSub","pubsub","initialized","runMigrations","_knex","options","postProcessResponse","result","_queryContext","undefined","camelizeRow","row","Object","fromEntries","entries","map","key","value","Array","isArray","initialize","subscribe","callback","Error","create","listenEvent","publish","payload","raw","stop","destroy","createWorkflowRun","params","qb","withSchema","table","insert","namespace_id","id","crypto","randomUUID","workflow_name","workflowName","version","status","idempotency_key","idempotencyKey","context","input","attempts","available_at","availableAt","fn","now","deadline_at","deadlineAt","created_at","updated_at","returning","workflowRun","getWorkflowRun","where","workflowRunId","select","first","listWorkflowRuns","limit","after","before","cursor","decodeCursor","buildListWorkflowRunsWhere","rows","orderBy","processPaginationResults","operator","whereRaw","createdAt","toISOString","claimWorkflowRun","claimed","with","update","error","JSON","stringify","message","worker_id","finished_at","whereIn","whereNotNull","from","qb2","whereNull","orWhere","orderByRaw","forUpdate","skipLocked","ref","workerId","leaseDurationMs","started_at","updateFrom","extendWorkflowRunLease","updated","sleepWorkflowRun","whereNotIn","completeWorkflowRun","output","failWorkflowRun","initialIntervalMs","backoffCoefficient","maximumIntervalMs","retryIntervalExpr","deadlineExceededCondition","cancelWorkflowRun","existing","includes","createStepAttempt","stepAttempt","workflow_run_id","step_name","stepName","kind","getStepAttempt","stepAttemptId","listStepAttempts","buildListStepAttemptsWhere","hasAfter","hasBefore","data","hasNext","hasPrev","reverse","length","shift","pop","lastItem","at","nextCursor","encodeCursor","firstItem","prevCursor","pagination","next","prev","completeStepAttempt","failStepAttempt","item","encoded","Buffer","toString","decoded","parsed","parse","Date"],"mappings":"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"}
|
|
@@ -4,10 +4,12 @@ import { BackendPostgres } from "./backend.js";
|
|
|
4
4
|
import { testBackend } from "./backend.testsuite.js";
|
|
5
5
|
testBackend({
|
|
6
6
|
setup: async ()=>{
|
|
7
|
-
|
|
7
|
+
const backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {
|
|
8
8
|
namespaceId: randomUUID(),
|
|
9
9
|
runMigrations: false
|
|
10
10
|
});
|
|
11
|
+
await backend.initialize();
|
|
12
|
+
return backend;
|
|
11
13
|
},
|
|
12
14
|
teardown: async (backend)=>{
|
|
13
15
|
await backend.stop();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/database/backend.test.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { KNEX_GLOBAL_CONFIG } from \"../testing/connection\";\nimport { BackendPostgres } from \"./backend\";\nimport { testBackend } from \"./backend.testsuite\";\n\ntestBackend({\n setup: async () => {\n
|
|
1
|
+
{"version":3,"sources":["../../src/database/backend.test.ts"],"sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { KNEX_GLOBAL_CONFIG } from \"../testing/connection\";\nimport { BackendPostgres } from \"./backend\";\nimport { testBackend } from \"./backend.testsuite\";\n\ntestBackend({\n setup: async () => {\n const backend = new BackendPostgres(KNEX_GLOBAL_CONFIG, {\n namespaceId: randomUUID(),\n runMigrations: false,\n });\n await backend.initialize();\n return backend;\n },\n teardown: async (backend) => {\n await (backend as BackendPostgres).stop();\n },\n});\n"],"names":["randomUUID","KNEX_GLOBAL_CONFIG","BackendPostgres","testBackend","setup","backend","namespaceId","runMigrations","initialize","teardown","stop"],"mappings":"AAAA,SAASA,UAAU,QAAQ,cAAc;AACzC,SAASC,kBAAkB,QAAQ,2BAAwB;AAC3D,SAASC,eAAe,QAAQ,eAAY;AAC5C,SAASC,WAAW,QAAQ,yBAAsB;AAElDA,YAAY;IACVC,OAAO;QACL,MAAMC,UAAU,IAAIH,gBAAgBD,oBAAoB;YACtDK,aAAaN;YACbO,eAAe;QACjB;QACA,MAAMF,QAAQG,UAAU;QACxB,OAAOH;IACT;IACAI,UAAU,OAAOJ;QACf,MAAM,AAACA,QAA4BK,IAAI;IACzC;AACF"}
|
package/dist/database/base.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { type Knex } from "knex";
|
|
2
2
|
export declare const DEFAULT_SCHEMA = "sonamu_tasks";
|
|
3
3
|
/**
|
|
4
4
|
* migrate applies pending migrations to the database. Does nothing if the
|
|
5
5
|
* database is already up to date.
|
|
6
6
|
*/
|
|
7
|
-
export declare function migrate(
|
|
7
|
+
export declare function migrate(config: Knex.Config, schema: string): Promise<void>;
|
|
8
8
|
/**
|
|
9
9
|
* dropSchema drops the specified schema from the database.
|
|
10
10
|
*/
|