@shivaedev/jobs 0.24.0 → 0.26.0
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/CHANGELOG.md +49 -0
- package/README.md +49 -24
- package/dist/server/adapters/batchClockOffset.d.ts +4 -0
- package/dist/server/adapters/batchClockOffset.d.ts.map +1 -0
- package/dist/server/adapters/batchClockOffset.js +16 -0
- package/dist/server/adapters/batchClockOffset.js.map +1 -0
- package/dist/server/adapters/clientErrorReporter.d.ts +3 -0
- package/dist/server/adapters/clientErrorReporter.d.ts.map +1 -0
- package/dist/server/adapters/clientErrorReporter.js +8 -0
- package/dist/server/adapters/clientErrorReporter.js.map +1 -0
- package/dist/server/adapters/installSchema.d.ts +5 -0
- package/dist/server/adapters/installSchema.d.ts.map +1 -0
- package/dist/server/adapters/installSchema.js +57 -0
- package/dist/server/adapters/installSchema.js.map +1 -0
- package/dist/server/adapters/registerWorker.d.ts +5 -0
- package/dist/server/adapters/registerWorker.d.ts.map +1 -0
- package/dist/server/{internal → adapters}/registerWorker.js +5 -4
- package/dist/server/adapters/registerWorker.js.map +1 -0
- package/dist/server/{internal → adapters}/runSchemaPlan.d.ts.map +1 -1
- package/dist/server/{internal → adapters}/runSchemaPlan.js.map +1 -1
- package/dist/server/{internal → adapters}/schemaIndexBuilds.d.ts.map +1 -1
- package/dist/server/{internal → adapters}/schemaIndexBuilds.js.map +1 -1
- package/dist/server/{internal → adapters}/testingExpirations.d.ts.map +1 -1
- package/dist/server/{internal → adapters}/testingExpirations.js.map +1 -1
- package/dist/server/{internal → adapters}/transactionDb.d.ts.map +1 -1
- package/dist/server/{internal → adapters}/transactionDb.js.map +1 -1
- package/dist/server/adapters/workerDiagnostics.d.ts +3 -0
- package/dist/server/adapters/workerDiagnostics.d.ts.map +1 -0
- package/dist/server/adapters/workerDiagnostics.js +24 -0
- package/dist/server/adapters/workerDiagnostics.js.map +1 -0
- package/dist/server/client.d.ts +0 -1
- package/dist/server/client.d.ts.map +1 -1
- package/dist/server/client.js +1 -2
- package/dist/server/client.js.map +1 -1
- package/dist/server/defaultClientFactory.d.ts +3 -0
- package/dist/server/defaultClientFactory.d.ts.map +1 -0
- package/dist/server/{internal/nativeClient.js → defaultClientFactory.js} +2 -2
- package/dist/server/defaultClientFactory.js.map +1 -0
- package/dist/server/defineSchedule.d.ts +3 -0
- package/dist/server/defineSchedule.d.ts.map +1 -0
- package/dist/server/{internal/scheduleDefinition.js → defineSchedule.js} +2 -2
- package/dist/server/defineSchedule.js.map +1 -0
- package/dist/server/definition.d.ts +1 -2
- package/dist/server/definition.d.ts.map +1 -1
- package/dist/server/definition.js +2 -7
- package/dist/server/definition.js.map +1 -1
- package/dist/server/error.d.ts +17 -7
- package/dist/server/error.d.ts.map +1 -1
- package/dist/server/error.js +11 -3
- package/dist/server/error.js.map +1 -1
- package/dist/server/health.d.ts +1 -0
- package/dist/server/health.d.ts.map +1 -1
- package/dist/server/internal/adapters/testingFetches.d.ts +7 -0
- package/dist/server/internal/adapters/testingFetches.d.ts.map +1 -0
- package/dist/server/internal/adapters/testingFetches.js +30 -0
- package/dist/server/internal/adapters/testingFetches.js.map +1 -0
- package/dist/server/internal/batchHandler.d.ts +1 -2
- package/dist/server/internal/batchHandler.d.ts.map +1 -1
- package/dist/server/internal/batchHandler.js +18 -6
- package/dist/server/internal/batchHandler.js.map +1 -1
- package/dist/server/internal/deadLetterResolutionPlan.d.ts +2 -0
- package/dist/server/internal/deadLetterResolutionPlan.d.ts.map +1 -0
- package/dist/server/internal/deadLetterResolutionPlan.js +28 -0
- package/dist/server/internal/deadLetterResolutionPlan.js.map +1 -0
- package/dist/server/internal/enqueueFor.d.ts.map +1 -1
- package/dist/server/internal/enqueueFor.js +2 -2
- package/dist/server/internal/enqueueFor.js.map +1 -1
- package/dist/server/internal/jobControl.d.ts.map +1 -1
- package/dist/server/internal/jobControl.js +2 -1
- package/dist/server/internal/jobControl.js.map +1 -1
- package/dist/server/internal/jobStorePlans.d.ts +2 -0
- package/dist/server/internal/jobStorePlans.d.ts.map +1 -0
- package/dist/server/internal/jobStorePlans.js +6 -0
- package/dist/server/internal/jobStorePlans.js.map +1 -0
- package/dist/server/internal/lifecycle.d.ts +1 -1
- package/dist/server/internal/lifecycle.d.ts.map +1 -1
- package/dist/server/internal/lifecycle.js +5 -3
- package/dist/server/internal/lifecycle.js.map +1 -1
- package/dist/server/internal/listJobs.d.ts.map +1 -1
- package/dist/server/internal/listJobs.js +3 -2
- package/dist/server/internal/listJobs.js.map +1 -1
- package/dist/server/internal/register.d.ts +1 -2
- package/dist/server/internal/register.d.ts.map +1 -1
- package/dist/server/internal/register.js +8 -8
- package/dist/server/internal/register.js.map +1 -1
- package/dist/server/internal/registerReleasedJobs.d.ts +1 -2
- package/dist/server/internal/registerReleasedJobs.d.ts.map +1 -1
- package/dist/server/internal/registerReleasedJobs.js +3 -3
- package/dist/server/internal/registerReleasedJobs.js.map +1 -1
- package/dist/server/internal/runQueueWorker.d.ts +1 -1
- package/dist/server/internal/runQueueWorker.d.ts.map +1 -1
- package/dist/server/internal/runQueueWorker.js +4 -3
- package/dist/server/internal/runQueueWorker.js.map +1 -1
- package/dist/server/internal/storedRows.js +1 -1
- package/dist/server/internal/storedRows.js.map +1 -1
- package/dist/server/internal/testingClient.d.ts.map +1 -1
- package/dist/server/internal/testingClient.js +1 -1
- package/dist/server/internal/testingClient.js.map +1 -1
- package/dist/server/internal/testingWaits.d.ts +16 -0
- package/dist/server/internal/testingWaits.d.ts.map +1 -0
- package/dist/server/internal/testingWaits.js +56 -0
- package/dist/server/internal/testingWaits.js.map +1 -0
- package/dist/server/internal/waitForBatchExpiry.d.ts +1 -1
- package/dist/server/internal/waitForBatchExpiry.d.ts.map +1 -1
- package/dist/server/internal/waitForBatchExpiry.js +2 -4
- package/dist/server/internal/waitForBatchExpiry.js.map +1 -1
- package/dist/server/schema.d.ts.map +1 -1
- package/dist/server/schema.js +11 -53
- package/dist/server/schema.js.map +1 -1
- package/dist/server/service.d.ts +3 -3
- package/dist/server/service.d.ts.map +1 -1
- package/dist/server/service.js +6 -10
- package/dist/server/service.js.map +1 -1
- package/dist/server/testing.d.ts +5 -0
- package/dist/server/testing.d.ts.map +1 -1
- package/dist/server/testing.js +7 -2
- package/dist/server/testing.js.map +1 -1
- package/package.json +4 -4
- package/src/server/adapters/batchClockOffset.ts +17 -0
- package/src/server/adapters/clientErrorReporter.ts +8 -0
- package/src/server/adapters/installSchema.ts +68 -0
- package/src/server/{internal → adapters}/registerWorker.ts +8 -9
- package/src/server/adapters/workerDiagnostics.ts +28 -0
- package/src/server/client.ts +0 -3
- package/src/server/{internal/nativeClient.ts → defaultClientFactory.ts} +1 -1
- package/src/server/{internal/scheduleDefinition.ts → defineSchedule.ts} +1 -1
- package/src/server/definition.ts +2 -8
- package/src/server/error.ts +16 -3
- package/src/server/health.ts +1 -0
- package/src/server/internal/adapters/testingFetches.ts +36 -0
- package/src/server/internal/batchHandler.ts +20 -12
- package/src/server/internal/deadLetterResolutionPlan.ts +28 -0
- package/src/server/internal/enqueueFor.ts +2 -2
- package/src/server/internal/jobControl.ts +2 -1
- package/src/server/internal/jobStorePlans.ts +6 -0
- package/src/server/internal/lifecycle.ts +6 -3
- package/src/server/internal/listJobs.ts +11 -3
- package/src/server/internal/register.ts +7 -22
- package/src/server/internal/registerReleasedJobs.ts +2 -6
- package/src/server/internal/runQueueWorker.ts +5 -3
- package/src/server/internal/storedRows.ts +1 -1
- package/src/server/internal/testingClient.ts +1 -1
- package/src/server/internal/testingWaits.ts +98 -0
- package/src/server/internal/waitForBatchExpiry.ts +7 -4
- package/src/server/schema.ts +14 -65
- package/src/server/service.ts +12 -21
- package/src/server/testing.ts +11 -2
- package/dist/server/internal/nativeClient.d.ts +0 -3
- package/dist/server/internal/nativeClient.d.ts.map +0 -1
- package/dist/server/internal/nativeClient.js.map +0 -1
- package/dist/server/internal/registerWorker.d.ts +0 -6
- package/dist/server/internal/registerWorker.d.ts.map +0 -1
- package/dist/server/internal/registerWorker.js.map +0 -1
- package/dist/server/internal/scheduleDefinition.d.ts +0 -3
- package/dist/server/internal/scheduleDefinition.d.ts.map +0 -1
- package/dist/server/internal/scheduleDefinition.js.map +0 -1
- /package/dist/server/{internal → adapters}/runSchemaPlan.d.ts +0 -0
- /package/dist/server/{internal → adapters}/runSchemaPlan.js +0 -0
- /package/dist/server/{internal → adapters}/schemaIndexBuilds.d.ts +0 -0
- /package/dist/server/{internal → adapters}/schemaIndexBuilds.js +0 -0
- /package/dist/server/{internal → adapters}/testingExpirations.d.ts +0 -0
- /package/dist/server/{internal → adapters}/testingExpirations.js +0 -0
- /package/dist/server/{internal → adapters}/transactionDb.d.ts +0 -0
- /package/dist/server/{internal → adapters}/transactionDb.js +0 -0
- /package/src/server/{internal → adapters}/runSchemaPlan.ts +0 -0
- /package/src/server/{internal → adapters}/schemaIndexBuilds.ts +0 -0
- /package/src/server/{internal → adapters}/testingExpirations.ts +0 -0
- /package/src/server/{internal → adapters}/transactionDb.ts +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAQ,KAAK,EAAE,KAAK,MAAM,EAAE,QAAQ,EAAE,KAAK,MAAM,EAAS,MAAM,QAAQ,CAAC;AACjG,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"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"}
|
package/dist/server/service.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Context, Effect, Exit, Layer, Redacted, Scope } from "effect";
|
|
2
2
|
import { defineService } from "@shivaedev/service/defineService.ts";
|
|
3
3
|
import { genericMethod } from "@shivaedev/service/genericMethod.ts";
|
|
4
|
+
import { clientErrorReporter } from "#adapters/clientErrorReporter.ts";
|
|
4
5
|
import { logClientError, subscribeClientErrors } from "#internal/clientErrors.ts";
|
|
5
6
|
import { acquireClient, releaseClient } from "#internal/lifecycle.ts";
|
|
6
7
|
import { makeService } from "#internal/makeService.ts";
|
|
@@ -11,8 +12,7 @@ import { PgBoss } from "#PgBoss.ts";
|
|
|
11
12
|
export function pgBossLayer(options) {
|
|
12
13
|
const { connectionString, clientFactory, clientCacheKey, stop, ...settings } = options;
|
|
13
14
|
return Layer.effect(PgBoss, Effect.gen(function* () {
|
|
14
|
-
const
|
|
15
|
-
const execute = Effect.runPromiseWith(dependencies);
|
|
15
|
+
const report = yield* clientErrorReporter(logClientError);
|
|
16
16
|
const acquired = yield* Effect.acquireRelease(acquireClient({
|
|
17
17
|
clientCacheKey,
|
|
18
18
|
clientFactory,
|
|
@@ -20,9 +20,7 @@ export function pgBossLayer(options) {
|
|
|
20
20
|
...settings,
|
|
21
21
|
...(connectionString === undefined ? {} : { connectionString: Redacted.value(connectionString) }),
|
|
22
22
|
},
|
|
23
|
-
}, (
|
|
24
|
-
void execute(logClientError(error));
|
|
25
|
-
}), (client) => releaseClient(client, stop));
|
|
23
|
+
}, report), (client) => releaseClient(client, stop));
|
|
26
24
|
return { ...acquired, schema: settings.schema, stop };
|
|
27
25
|
}));
|
|
28
26
|
}
|
|
@@ -33,16 +31,14 @@ export function makePgBoss(identifier, queueNames) {
|
|
|
33
31
|
initialize: Effect.gen(function* () {
|
|
34
32
|
yield* validateQueueNames(options.jobs, queueNames);
|
|
35
33
|
const acquired = yield* PgBoss;
|
|
36
|
-
const execute = Effect.runPromiseWith(dependencies);
|
|
37
34
|
const workerScope = yield* Scope.make();
|
|
38
35
|
const service = makeService(acquired.client, options.jobs.map(registrationName), acquired.schema);
|
|
39
36
|
const context = Context.add(dependencies, Service, service);
|
|
40
37
|
const reportError = options.onError ?? logClientError;
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
});
|
|
38
|
+
const report = yield* clientErrorReporter(reportError).pipe(Effect.provide(context));
|
|
39
|
+
const unsubscribe = subscribeClientErrors(acquired.client, report);
|
|
44
40
|
yield* Effect.addFinalizer(() => Scope.close(workerScope, Exit.void).pipe(Effect.ensuring(Effect.sync(unsubscribe))));
|
|
45
|
-
yield* registerReleasedJobs(acquired.client, options.jobs, context, acquired.reused,
|
|
41
|
+
yield* registerReleasedJobs(acquired.client, options.jobs, context, acquired.reused, acquired.schema, acquired.stop?.graceful !== false).pipe(Effect.provideService(Scope.Scope, workerScope), Effect.provide(dependencies));
|
|
46
42
|
return service;
|
|
47
43
|
}),
|
|
48
44
|
methods: (service) => ({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAe,QAAQ,EAAe,KAAK,EAAE,MAAM,QAAQ,CAAC;AAEjG,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;AACpE,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAC;
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"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"}
|
package/dist/server/testing.d.ts
CHANGED
|
@@ -10,6 +10,9 @@ export interface UnfinishedJob<TQueue extends SnapshotQueue> {
|
|
|
10
10
|
readonly startAfter: DateTime.Utc;
|
|
11
11
|
readonly state: "active" | "created" | "failed" | "retry";
|
|
12
12
|
}
|
|
13
|
+
export interface PgBossTestingWaitOptions {
|
|
14
|
+
readonly within?: Duration.Input;
|
|
15
|
+
}
|
|
13
16
|
export declare const testRetryPolicy: Readonly<{
|
|
14
17
|
retryBackoff: false;
|
|
15
18
|
retryDelay: 0;
|
|
@@ -29,6 +32,8 @@ export declare function makePgBossTesting(options?: PgBossTestingOptions): {
|
|
|
29
32
|
timezone: string;
|
|
30
33
|
}[], PgBossTestingError, never>;
|
|
31
34
|
trigger: (schedule: Pick<import("#definition.ts").ScheduleDefinition, "name" | "scheduleOptions">) => Effect.Effect<void, PgBossTestingError, never>;
|
|
35
|
+
fetchesPass: (queue: Pick<QueueDefinition, "name">, count: number, options?: PgBossTestingWaitOptions) => Effect.Effect<unknown, PgBossTestingError | import("#error.ts").PgBossWaitError, never>;
|
|
36
|
+
untilJob: <TName extends string, TPayload extends JobPayloadSchema>(queue: Pick<QueueDefinition<TName, TPayload>, "name" | "schema">, id: string, state: import("pg-boss").JobWithMetadata["state"], options?: PgBossTestingWaitOptions) => Effect.Effect<import("pg-boss").JobWithMetadata<TPayload["Type"]>, import("#error.ts").PgBossPayloadError | PgBossTestingError | import("#error.ts").PgBossWaitError, Exclude<TPayload["DecodingServices"], never>>;
|
|
32
37
|
checkBatchWindow: (queue: Pick<QueueDefinition, "name" | "workerOptions">, millisecondsPerJob: number) => Effect.Effect<void, PgBossTestingError, never>;
|
|
33
38
|
checkSchemaVersion: (expected: number) => Effect.Effect<void, PgBossTestingError, never>;
|
|
34
39
|
clientFactory: PgBossClientFactory;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/server/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAY,MAAM,QAAQ,CAAC;AAEnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../../src/server/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAY,MAAM,QAAQ,CAAC;AAEnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AACxE,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAW/C,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,eAAe,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC,CAAC;AAE/F,MAAM,WAAW,aAAa,CAAC,MAAM,SAAS,aAAa;IAC1D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;IACxC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC;IAC/B,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,CAAC;IAClC,QAAQ,CAAC,KAAK,EAAE,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;CAC1D;AAED,MAAM,WAAW,wBAAwB;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC;CACjC;AAED,eAAO,MAAM,eAAe;;;EAAwD,CAAC;AAErF,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,eAAe,CAAC;CAC9C;AAED,wBAAgB,iBAAiB,CAAC,OAAO,GAAE,oBAAyB;;;;;;;;;;;;;;8BAiCxC,IAAI,CAAC,eAAe,EAAE,MAAM,GAAG,eAAe,CAAC,sBAAsB,MAAM;mCAqBtE,MAAM;;yBAOhB,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,YAAY,MAAM;sBAUlD,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,YAAY,QAAQ,CAAC,KAAK;0BAWnD,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAQpD;AAED,MAAM,MAAM,aAAa,GAAG,UAAU,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
|
package/dist/server/testing.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Duration, Effect, Redacted } from "effect";
|
|
2
|
+
import { defaultClientFactory } from "#defaultClientFactory.ts";
|
|
2
3
|
import { PgBossTestingError } from "#error.ts";
|
|
3
|
-
import {
|
|
4
|
+
import { testingFetches } from "#internal/adapters/testingFetches.ts";
|
|
4
5
|
import { testingClient } from "#internal/testingClient.ts";
|
|
5
6
|
import { testingDeferrals } from "#internal/testingDeferrals.ts";
|
|
6
7
|
import { testingIdle } from "#internal/testingIdle.ts";
|
|
@@ -8,9 +9,11 @@ import { testingOperation, testingReadout } from "#internal/testingReadout.ts";
|
|
|
8
9
|
import { testingSchedules } from "#internal/testingSchedules.ts";
|
|
9
10
|
import { testingSignal } from "#internal/testingSignal.ts";
|
|
10
11
|
import { testingSnapshot } from "#internal/testingSnapshot.ts";
|
|
12
|
+
import { testingWaits } from "#internal/testingWaits.ts";
|
|
11
13
|
export const testRetryPolicy = Object.freeze({ retryBackoff: false, retryDelay: 0 });
|
|
12
14
|
export function makePgBossTesting(options = {}) {
|
|
13
15
|
let current;
|
|
16
|
+
let fetches;
|
|
14
17
|
let schema = "pgboss";
|
|
15
18
|
const signal = testingSignal();
|
|
16
19
|
const work = { batches: new Set(), queues: new Set() };
|
|
@@ -33,12 +36,14 @@ export function makePgBossTesting(options = {}) {
|
|
|
33
36
|
throw new Error("A testing controller owns one jobs client; create a controller per runtime");
|
|
34
37
|
}
|
|
35
38
|
schema = clientOptions.schema ?? "pgboss";
|
|
36
|
-
current =
|
|
39
|
+
current = defaultClientFactory({ ...clientOptions, "__test__enableSpies": true });
|
|
40
|
+
fetches = testingFetches(current.getDb(), signal.changed);
|
|
37
41
|
return testingClient(current, clientOptions.schema ?? "pgboss", controls);
|
|
38
42
|
};
|
|
39
43
|
return {
|
|
40
44
|
...testingDeferrals(client, () => schema, signal.changed),
|
|
41
45
|
...testingSchedules(client, signal.changed),
|
|
46
|
+
...testingWaits(client, () => fetches, signal),
|
|
42
47
|
checkBatchWindow: (queue, millisecondsPerJob) => testingOperation("checkBatchWindow", async () => {
|
|
43
48
|
const stored = await client().getQueue(queue.name);
|
|
44
49
|
if (stored === null) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../../src/server/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"testing.js","sourceRoot":"","sources":["../../src/server/testing.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/jobs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Effect-native jobs and lifecycle management for pg-boss",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"effect"
|
|
@@ -48,16 +48,16 @@
|
|
|
48
48
|
"README.md"
|
|
49
49
|
],
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@shivaedev/errors": "0.
|
|
51
|
+
"@shivaedev/errors": "0.2.0",
|
|
52
52
|
"@shivaedev/service": "0.6.0",
|
|
53
53
|
"@shivaedev/sql": "0.27.0"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@shivaedev/http": "0.3.1",
|
|
57
|
-
"@shivaedev/quality": "0.
|
|
57
|
+
"@shivaedev/quality": "0.49.0",
|
|
58
58
|
"@shivaedev/test": "0.12.0",
|
|
59
59
|
"@shivaedev/test-sql": "0.15.0",
|
|
60
|
-
"@shivaedev/test-story": "0.
|
|
60
|
+
"@shivaedev/test-story": "0.29.0",
|
|
61
61
|
"@types/node": "24.19.2",
|
|
62
62
|
"effect": "4.0.6",
|
|
63
63
|
"pg-boss": "12.38.0",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Clock, Effect } from "effect";
|
|
2
|
+
import type { PgBossClient } from "#client.ts";
|
|
3
|
+
import { toPgBossError } from "#error.ts";
|
|
4
|
+
import { quotedSchema } from "#internal/storedRows.ts";
|
|
5
|
+
|
|
6
|
+
export const batchClockOffset = Effect.fnUntraced(function* (client: PgBossClient, schema: string, name: string) {
|
|
7
|
+
const { rows } = yield* Effect.tryPromise({
|
|
8
|
+
catch: (error) => toPgBossError("settle", error, name),
|
|
9
|
+
try: () => {
|
|
10
|
+
if (client.getDb === undefined) {
|
|
11
|
+
throw new TypeError("This pg-boss client does not expose its database clock");
|
|
12
|
+
}
|
|
13
|
+
return client.getDb().executeSql(`SELECT EXTRACT(EPOCH FROM ${quotedSchema(schema)}.job_now()) * 1000 AS now`);
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
return (yield* Clock.currentTimeMillis) - Number(rows[0].now);
|
|
17
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Effect } from "effect";
|
|
2
|
+
|
|
3
|
+
export const clientErrorReporter = Effect.fnUntraced(function* <R>(report: (error: Error) => Effect.Effect<unknown, never, R>) {
|
|
4
|
+
const run = Effect.runPromiseWith(yield* Effect.context<R>());
|
|
5
|
+
return (error: Error): void => {
|
|
6
|
+
void run(Effect.exit(report(error)));
|
|
7
|
+
};
|
|
8
|
+
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { Effect, Schema } from "effect";
|
|
2
|
+
import { SqlClient } from "effect/sql";
|
|
3
|
+
import type { Connection } from "effect/sql/SqlConnection";
|
|
4
|
+
import { getConstructionPlans, getMigrationPlans, PgBoss } from "pg-boss";
|
|
5
|
+
import pgBossPackage from "pg-boss/package.json" with { type: "json" };
|
|
6
|
+
import { jobStorePlans } from "#internal/jobStorePlans.ts";
|
|
7
|
+
import type { PgBossSchemaOptions } from "#schema.ts";
|
|
8
|
+
import { runSchemaPlan } from "./runSchemaPlan.ts";
|
|
9
|
+
import { resumeSchemaIndexes } from "./schemaIndexBuilds.ts";
|
|
10
|
+
|
|
11
|
+
const Partitions = Schema.Array(
|
|
12
|
+
Schema.Struct({
|
|
13
|
+
policy: Schema.String,
|
|
14
|
+
tableName: Schema.String,
|
|
15
|
+
}),
|
|
16
|
+
);
|
|
17
|
+
async function installNativeSchema(
|
|
18
|
+
connection: Connection,
|
|
19
|
+
options: PgBossSchemaOptions,
|
|
20
|
+
run: <A, E>(effect: Effect.Effect<A, E>) => Promise<A>,
|
|
21
|
+
): Promise<void> {
|
|
22
|
+
const schema = options.schema ?? "pgboss";
|
|
23
|
+
const construction = getConstructionPlans(schema);
|
|
24
|
+
const client = new PgBoss({
|
|
25
|
+
db: { executeSql: async (text, values = []) => ({ rows: Array.from(await run(connection.executeUnprepared(text, values, undefined))) }) },
|
|
26
|
+
schema,
|
|
27
|
+
});
|
|
28
|
+
if (!(await client.isInstalled())) {
|
|
29
|
+
await runSchemaPlan(connection, construction, run);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const version = await client.schemaVersion();
|
|
33
|
+
if (version === null) {
|
|
34
|
+
throw new Error("The pg-boss version table is empty");
|
|
35
|
+
}
|
|
36
|
+
if (version > pgBossPackage.pgboss.schema) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
if (version >= 27) {
|
|
40
|
+
await resumeSchemaIndexes(client, schema);
|
|
41
|
+
}
|
|
42
|
+
if (version < pgBossPackage.pgboss.schema) {
|
|
43
|
+
const partitionTables = Schema.decodeUnknownSync(Partitions)(
|
|
44
|
+
await run(connection.executeUnprepared(`SELECT table_name AS "tableName", policy FROM ${schema}.queue WHERE partition = true`, [], undefined)),
|
|
45
|
+
);
|
|
46
|
+
await runSchemaPlan(connection, getMigrationPlans(schema, version, { partitionTables: Array.from(partitionTables) }), run, {
|
|
47
|
+
schema,
|
|
48
|
+
version: pgBossPackage.pgboss.schema,
|
|
49
|
+
});
|
|
50
|
+
await resumeSchemaIndexes(client, schema);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const installSchema = Effect.fnUntraced(function* (options: PgBossSchemaOptions) {
|
|
55
|
+
const sql = yield* SqlClient.SqlClient;
|
|
56
|
+
const connection = yield* sql.reserve;
|
|
57
|
+
const run = Effect.runPromiseWith(yield* Effect.context<never>());
|
|
58
|
+
yield* Effect.tryPromise({
|
|
59
|
+
catch: (error) => error,
|
|
60
|
+
try: async (signal) => {
|
|
61
|
+
const execute: typeof run = (effect) => run(effect, { signal });
|
|
62
|
+
await installNativeSchema(connection, options, execute);
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
for (const plan of jobStorePlans(options.schema ?? "pgboss")) {
|
|
66
|
+
yield* connection.executeRaw(plan, []);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
@@ -1,34 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Effect, Fiber, Scope } from "effect";
|
|
2
2
|
import type { Job, WorkOptions } from "pg-boss";
|
|
3
3
|
import type { PgBossClient } from "#client.ts";
|
|
4
4
|
import { toPgBossError } from "#error.ts";
|
|
5
|
-
import { batchHandler
|
|
5
|
+
import { batchHandler } from "#internal/batchHandler.ts";
|
|
6
6
|
import { deferrableAttempt } from "#internal/deferrableAttempt.ts";
|
|
7
7
|
import { retryWake } from "#internal/retryWake.ts";
|
|
8
8
|
import { traceJob } from "#internal/tracing.ts";
|
|
9
9
|
|
|
10
|
-
export const registerWorker = Effect.fnUntraced(function* (
|
|
10
|
+
export const registerWorker = Effect.fnUntraced(function* <R>(
|
|
11
11
|
client: PgBossClient,
|
|
12
12
|
name: string,
|
|
13
13
|
options: WorkOptions,
|
|
14
|
-
run: (job: Job<unknown
|
|
14
|
+
run: (job: Job<unknown>, deadline: number) => Effect.Effect<unknown, unknown, R>,
|
|
15
15
|
scope: Scope.Scope,
|
|
16
16
|
schema: string,
|
|
17
17
|
maxDeferrals: number,
|
|
18
|
-
context: Context.Context<unknown>,
|
|
19
|
-
execute: PgBossRunner,
|
|
20
18
|
graceful: boolean,
|
|
21
19
|
) {
|
|
20
|
+
const execute = Effect.runPromiseWith(yield* Effect.context<R>());
|
|
22
21
|
const fibers = yield* Scope.fork(scope);
|
|
23
|
-
function runJob(job: Job<unknown
|
|
24
|
-
return
|
|
22
|
+
function runJob(job: Job<unknown>, deadline: number) {
|
|
23
|
+
return traceJob(deferrableAttempt(run(job, deadline), client, schema, job, maxDeferrals), job, name);
|
|
25
24
|
}
|
|
26
25
|
function afterFailure(attempt: Job<unknown>) {
|
|
27
26
|
return retryWake(client, name, attempt.id, registration).pipe(Effect.forkIn(fibers), Effect.asVoid);
|
|
28
27
|
}
|
|
29
28
|
const handle = yield* Effect.try({
|
|
30
29
|
catch: (error) => toPgBossError("register", error, name),
|
|
31
|
-
try: () => batchHandler(client, name, runJob, afterFailure),
|
|
30
|
+
try: () => batchHandler(client, name, schema, runJob, afterFailure),
|
|
32
31
|
});
|
|
33
32
|
const registration: Promise<string> = yield* Effect.try({
|
|
34
33
|
catch: (error) => toPgBossError("register", error, name),
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Context, Effect, Logger, References } from "effect";
|
|
2
|
+
import { redact } from "@shivaedev/errors/redact.ts";
|
|
3
|
+
import { redactDiagnosticCause } from "@shivaedev/errors/redactCause.ts";
|
|
4
|
+
import { isSensitiveKey, REDACTED } from "@shivaedev/errors/sensitive.ts";
|
|
5
|
+
|
|
6
|
+
function maskedLogger(logger: Logger.Logger<unknown, unknown>) {
|
|
7
|
+
return Logger.make((options) => {
|
|
8
|
+
const context = options.fiber.context;
|
|
9
|
+
const annotations = Object.fromEntries(
|
|
10
|
+
Object.entries(options.fiber.getRef(References.CurrentLogAnnotations)).map(([key, value]) => [
|
|
11
|
+
key,
|
|
12
|
+
isSensitiveKey(key) ? REDACTED : redact(value),
|
|
13
|
+
]),
|
|
14
|
+
);
|
|
15
|
+
options.fiber.setContext(Context.add(context, References.CurrentLogAnnotations, annotations));
|
|
16
|
+
try {
|
|
17
|
+
return logger.log({ ...options, cause: redactDiagnosticCause(options.cause), message: redact(options.message) });
|
|
18
|
+
} finally {
|
|
19
|
+
options.fiber.setContext(context);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function workerDiagnostics<A, E, R>(effect: Effect.Effect<A, E, R>) {
|
|
25
|
+
return Effect.flatMap(Logger.CurrentLoggers, (loggers) =>
|
|
26
|
+
Effect.provideService(effect, Logger.CurrentLoggers, new Set([...loggers].map(maskedLogger))),
|
|
27
|
+
);
|
|
28
|
+
}
|
package/src/server/client.ts
CHANGED
|
@@ -11,7 +11,6 @@ import type {
|
|
|
11
11
|
StopOptions,
|
|
12
12
|
WorkOptions,
|
|
13
13
|
} from "pg-boss";
|
|
14
|
-
import { nativeClient } from "#internal/nativeClient.ts";
|
|
15
14
|
|
|
16
15
|
export interface PgBossClient {
|
|
17
16
|
readonly complete?: PgBoss["complete"];
|
|
@@ -36,5 +35,3 @@ export interface PgBossClient {
|
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
export type PgBossClientFactory = (options: ConstructorOptions) => PgBossClient;
|
|
39
|
-
|
|
40
|
-
export const defaultClientFactory: PgBossClientFactory = nativeClient;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Socket } from "node:net";
|
|
2
2
|
import { type ConstructorOptions, PgBoss } from "pg-boss";
|
|
3
3
|
|
|
4
|
-
export function
|
|
4
|
+
export function defaultClientFactory(options: ConstructorOptions) {
|
|
5
5
|
const disconnected = new Set<Promise<void>>();
|
|
6
6
|
const config = {
|
|
7
7
|
...options,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { DefineScheduleOptions, ScheduleDefinition } from "#definition.ts";
|
|
2
2
|
import { maxDeferrals, nativeQueueOptions } from "#internal/definitionOptions.ts";
|
|
3
3
|
|
|
4
|
-
export function
|
|
4
|
+
export function defineSchedule<const TName extends string>(options: DefineScheduleOptions<TName>): ScheduleDefinition<TName> {
|
|
5
5
|
const definition: ScheduleDefinition<TName> = {
|
|
6
6
|
_tag: "ScheduleDefinition",
|
|
7
7
|
cron: options.cron,
|
package/src/server/definition.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { Clock, Duration, Effect, Schema } from "effect";
|
|
2
2
|
import type { Job, policies, Queue, ScheduleOptions, WorkOptions } from "pg-boss";
|
|
3
|
-
import { deadlineOf } from "#internal/deadlineOf.ts";
|
|
4
3
|
import { maxDeferrals, nativeQueueOptions } from "#internal/definitionOptions.ts";
|
|
5
|
-
import { scheduleDefinition } from "#internal/scheduleDefinition.ts";
|
|
6
4
|
|
|
7
5
|
export class RetryAt extends Schema.TaggedError<RetryAt>()("RetryAt", {
|
|
8
6
|
at: Schema.DateTimeUtc,
|
|
@@ -124,17 +122,13 @@ export interface DefineScheduleOptions<TName extends string> {
|
|
|
124
122
|
readonly worker?: WorkOptions;
|
|
125
123
|
}
|
|
126
124
|
|
|
127
|
-
export function
|
|
128
|
-
return scheduleDefinition(options);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export function jobContext(job: Job<unknown>): JobContext {
|
|
125
|
+
export function jobContext(job: Job<unknown>, deadline: number): JobContext {
|
|
132
126
|
return {
|
|
133
127
|
expireInSeconds: job.expireInSeconds,
|
|
134
128
|
heartbeatSeconds: job.heartbeatSeconds,
|
|
135
129
|
id: job.id,
|
|
136
130
|
name: job.name,
|
|
137
|
-
remainingBudget: Effect.map(Clock.currentTimeMillis, (now) => Duration.millis(Math.max(0,
|
|
131
|
+
remainingBudget: Effect.map(Clock.currentTimeMillis, (now) => Duration.millis(Math.max(0, deadline - now))),
|
|
138
132
|
signal: job.signal,
|
|
139
133
|
};
|
|
140
134
|
}
|
package/src/server/error.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { Data, Redacted } from "effect";
|
|
1
|
+
import { Cause, Data, Duration, Redacted } from "effect";
|
|
2
|
+
import { failureOutput } from "#internal/failureOutput.ts";
|
|
2
3
|
|
|
3
4
|
export type PgBossOperation =
|
|
4
5
|
| "counts"
|
|
@@ -19,10 +20,12 @@ export class PgBossError extends Data.TaggedError("PgBossError")<{
|
|
|
19
20
|
readonly queue?: string | undefined;
|
|
20
21
|
readonly original: Redacted.Redacted<unknown>;
|
|
21
22
|
}> {
|
|
22
|
-
//
|
|
23
|
+
// Non-settlement originals can quote payloads or connection strings, so only their class and code are shown.
|
|
23
24
|
override get message(): string {
|
|
24
25
|
const target = this.queue === undefined ? "" : ` for queue ${this.queue}`;
|
|
25
|
-
|
|
26
|
+
const original = Redacted.value(this.original);
|
|
27
|
+
const detail = this.operation === "settle" ? failureOutput(Cause.fail(original)) : undefined;
|
|
28
|
+
return `pg-boss ${this.operation}${target} failed: ${detail === undefined ? originalKind(original) : `${detail._tag}: ${detail.message}`}`;
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
31
|
|
|
@@ -73,6 +76,16 @@ export class PgBossTestingError extends Data.TaggedError("PgBossTestingError")<{
|
|
|
73
76
|
readonly queue?: string | undefined;
|
|
74
77
|
}> {}
|
|
75
78
|
|
|
79
|
+
export class PgBossWaitError extends Data.TaggedError("PgBossWaitError")<{
|
|
80
|
+
readonly lastSeen: string;
|
|
81
|
+
readonly waitingFor: string;
|
|
82
|
+
readonly within: Duration.Duration;
|
|
83
|
+
}> {
|
|
84
|
+
override get message(): string {
|
|
85
|
+
return `Gave up after ${Duration.format(this.within)} waiting until ${this.waitingFor}; last seen: ${this.lastSeen}`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
76
89
|
export function toPgBossError(operation: PgBossOperation, error: unknown, queue?: string): PgBossError {
|
|
77
90
|
return new PgBossError({
|
|
78
91
|
operation,
|
package/src/server/health.ts
CHANGED
|
@@ -48,6 +48,7 @@ export interface ListedJob<TData> {
|
|
|
48
48
|
export interface DeadLetteredJob<TData> extends ListedJob<TData> {
|
|
49
49
|
readonly sourceId: Option.Option<string>;
|
|
50
50
|
readonly sourceOutput: JobFailure | null;
|
|
51
|
+
readonly sourceQueue: string;
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
export interface JobPageOptions {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import type { PgBoss } from "pg-boss";
|
|
2
|
+
|
|
3
|
+
// pg-boss offers no hook for a worker fetch that finds nothing, so the controller recognizes its claim statement on the client's database.
|
|
4
|
+
const claim = /UPDATE\s+\S+\s+j\s+SET\s+state\s*=\s*'active'/u;
|
|
5
|
+
const claimedQueue = /\bj\.name\s*=\s*'(?<queue>[^']+)'/u;
|
|
6
|
+
|
|
7
|
+
export interface TestingFetches {
|
|
8
|
+
readonly begun: (queue: string) => number;
|
|
9
|
+
readonly passedSince: (queue: string, begun: number) => number;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function testingFetches(db: ReturnType<PgBoss["getDb"]>, changed: () => void): TestingFetches {
|
|
13
|
+
const begun = new Map<string, number>();
|
|
14
|
+
const passed = new Map<string, number[]>();
|
|
15
|
+
const executeSql = db.executeSql.bind(db);
|
|
16
|
+
db.executeSql = async (text: string, values?: unknown[]) => {
|
|
17
|
+
const queue = claim.test(text) ? claimedQueue.exec(text)?.groups?.queue : undefined;
|
|
18
|
+
if (queue === undefined) {
|
|
19
|
+
return executeSql(text, values);
|
|
20
|
+
}
|
|
21
|
+
const ordinal = (begun.get(queue) ?? 0) + 1;
|
|
22
|
+
begun.set(queue, ordinal);
|
|
23
|
+
try {
|
|
24
|
+
return await executeSql(text, values);
|
|
25
|
+
} finally {
|
|
26
|
+
const ordinals = passed.get(queue) ?? [];
|
|
27
|
+
ordinals.push(ordinal);
|
|
28
|
+
passed.set(queue, ordinals);
|
|
29
|
+
changed();
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
return {
|
|
33
|
+
begun: (queue) => begun.get(queue) ?? 0,
|
|
34
|
+
passedSince: (queue, mark) => (passed.get(queue) ?? []).filter((ordinal) => ordinal > mark).length,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Cause, Clock, Effect, Exit } from "effect";
|
|
2
2
|
import type { Job } from "pg-boss";
|
|
3
|
+
import { batchClockOffset } from "#adapters/batchClockOffset.ts";
|
|
3
4
|
import type { PgBossClient } from "#client.ts";
|
|
4
5
|
import { toPgBossError } from "#error.ts";
|
|
5
6
|
import { deadlineOf } from "#internal/deadlineOf.ts";
|
|
@@ -23,7 +24,14 @@ function settleAttempt(
|
|
|
23
24
|
});
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
const interruptAt = Effect.fnUntraced(function* (deadline: number) {
|
|
28
|
+
let remaining = deadline - (yield* Clock.currentTimeMillis);
|
|
29
|
+
while (remaining > 0) {
|
|
30
|
+
yield* Effect.sleep(remaining);
|
|
31
|
+
remaining = deadline - (yield* Clock.currentTimeMillis);
|
|
32
|
+
}
|
|
33
|
+
return yield* Effect.interrupt;
|
|
34
|
+
});
|
|
27
35
|
|
|
28
36
|
function aborted(signal: AbortSignal) {
|
|
29
37
|
return Effect.callback<never>((resume) => {
|
|
@@ -39,10 +47,11 @@ function aborted(signal: AbortSignal) {
|
|
|
39
47
|
});
|
|
40
48
|
}
|
|
41
49
|
|
|
42
|
-
export function batchHandler(
|
|
50
|
+
export function batchHandler<R>(
|
|
43
51
|
client: PgBossClient,
|
|
44
52
|
name: string,
|
|
45
|
-
|
|
53
|
+
schema: string,
|
|
54
|
+
run: (job: Job<unknown>, deadline: number) => Effect.Effect<unknown, unknown, R>,
|
|
46
55
|
afterFailure: (job: Job<unknown>) => Effect.Effect<void, unknown> = () => Effect.void,
|
|
47
56
|
) {
|
|
48
57
|
const complete = client.complete?.bind(client);
|
|
@@ -50,19 +59,16 @@ export function batchHandler(
|
|
|
50
59
|
if (complete === undefined || fail === undefined) {
|
|
51
60
|
throw new TypeError("This pg-boss client does not support complete and fail");
|
|
52
61
|
}
|
|
53
|
-
const handle = Effect.fnUntraced(function* (job: Job<unknown
|
|
62
|
+
const handle = Effect.fnUntraced(function* (job: Job<unknown>, offset: number) {
|
|
54
63
|
if (job.signal.aborted) {
|
|
55
64
|
return "interrupted";
|
|
56
65
|
}
|
|
57
|
-
const
|
|
66
|
+
const deadline = deadlineOf(job) + offset;
|
|
67
|
+
const remaining = deadline - (yield* Clock.currentTimeMillis);
|
|
58
68
|
if (remaining <= 0) {
|
|
59
69
|
return "expired";
|
|
60
70
|
}
|
|
61
|
-
const exit = yield* run(job).pipe(
|
|
62
|
-
Effect.timeoutOrElse({ duration: remaining, orElse: () => Effect.interrupt }),
|
|
63
|
-
Effect.raceFirst(aborted(job.signal)),
|
|
64
|
-
Effect.exit,
|
|
65
|
-
);
|
|
71
|
+
const exit = yield* run(job, deadline).pipe(Effect.raceFirst(interruptAt(deadline)), Effect.raceFirst(aborted(job.signal)), Effect.exit);
|
|
66
72
|
if (job.signal.aborted || (Exit.isFailure(exit) && Cause.hasInterrupts(exit.cause))) {
|
|
67
73
|
return "interrupted";
|
|
68
74
|
}
|
|
@@ -74,10 +80,12 @@ export function batchHandler(
|
|
|
74
80
|
});
|
|
75
81
|
return (jobs: readonly Job<unknown>[]) =>
|
|
76
82
|
Effect.gen(function* () {
|
|
77
|
-
const
|
|
83
|
+
const offset = yield* batchClockOffset(client, schema, name);
|
|
84
|
+
const deadline = Math.max(...jobs.map((job) => deadlineOf(job) + offset));
|
|
85
|
+
const waitForExpiry = waitForBatchExpiry(client, name, jobs, deadline).pipe(Effect.tapError(Effect.logError));
|
|
78
86
|
let expired = false;
|
|
79
87
|
for (const job of jobs) {
|
|
80
|
-
const outcome = yield* handle(job);
|
|
88
|
+
const outcome = yield* handle(job, offset);
|
|
81
89
|
if (outcome === "interrupted") {
|
|
82
90
|
return yield* waitForExpiry;
|
|
83
91
|
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { quotedSchema } from "#internal/storedRows.ts";
|
|
2
|
+
|
|
3
|
+
export function deadLetterResolutionPlan(schema: string): string {
|
|
4
|
+
const quoted = quotedSchema(schema);
|
|
5
|
+
const jobTable = `${quoted}.job`.replaceAll("'", "''");
|
|
6
|
+
const body = `BEGIN
|
|
7
|
+
PERFORM pg_advisory_xact_lock(hashtextextended('${jobTable}.effect_dead_letter_resolution', 0));
|
|
8
|
+
CREATE OR REPLACE FUNCTION ${quoted}.effect_dead_letter_resolution() RETURNS trigger LANGUAGE plpgsql AS $resolve$
|
|
9
|
+
BEGIN
|
|
10
|
+
DELETE FROM ${quoted}.job
|
|
11
|
+
WHERE name = NEW.name || '-dlq' AND source_name = NEW.name AND source_id = NEW.id
|
|
12
|
+
AND source_created_on = NEW.created_on;
|
|
13
|
+
RETURN NEW;
|
|
14
|
+
END
|
|
15
|
+
$resolve$;
|
|
16
|
+
IF NOT EXISTS (
|
|
17
|
+
SELECT 1 FROM pg_trigger WHERE tgrelid = '${jobTable}'::regclass AND tgname = 'effect_dead_letter_resolution'
|
|
18
|
+
) THEN
|
|
19
|
+
CREATE TRIGGER effect_dead_letter_resolution AFTER UPDATE OF state ON ${quoted}.job
|
|
20
|
+
FOR EACH ROW WHEN (NEW.state = 'completed' AND OLD.state IS DISTINCT FROM NEW.state)
|
|
21
|
+
EXECUTE FUNCTION ${quoted}.effect_dead_letter_resolution();
|
|
22
|
+
DELETE FROM ${quoted}.job AS copy USING ${quoted}.job AS source
|
|
23
|
+
WHERE source.state = 'completed' AND copy.name = source.name || '-dlq'
|
|
24
|
+
AND copy.source_name = source.name AND copy.source_id = source.id AND copy.source_created_on = source.created_on;
|
|
25
|
+
END IF;
|
|
26
|
+
END`;
|
|
27
|
+
return `DO '${body.replaceAll("'", "''")}'`;
|
|
28
|
+
}
|