@shivaedev/effect-pg-boss 0.6.0 → 0.7.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 +17 -0
- package/README.md +53 -1
- package/dist/server/definition.js +2 -2
- package/dist/server/definition.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/register.d.ts +1 -1
- package/dist/server/internal/register.d.ts.map +1 -1
- package/dist/server/internal/register.js +29 -21
- package/dist/server/internal/register.js.map +1 -1
- package/dist/server/internal/service.d.ts.map +1 -1
- package/dist/server/internal/service.js +4 -3
- package/dist/server/internal/service.js.map +1 -1
- package/dist/server/internal/tracing.d.ts +6 -0
- package/dist/server/internal/tracing.d.ts.map +1 -0
- package/dist/server/internal/tracing.js +37 -0
- package/dist/server/internal/tracing.js.map +1 -0
- package/dist/server/service.d.ts +1 -1
- package/dist/server/service.d.ts.map +1 -1
- package/dist/server/service.js +5 -3
- package/dist/server/service.js.map +1 -1
- package/docs/roadmap.md +2 -0
- package/package.json +1 -1
- package/src/server/definition.ts +2 -2
- package/src/server/internal/jobControl.ts +2 -1
- package/src/server/internal/register.ts +38 -30
- package/src/server/internal/service.ts +10 -4
- package/src/server/internal/tracing.ts +44 -0
- package/src/server/service.ts +17 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.0 - 2026-10-17
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Queue and schedule workers default to native pg-boss `batchSize: 10` and `burstWhenBatchFull: true`, draining full batches immediately instead of waiting for the notification polling backstop. Native worker options still override the defaults, including batch size and local concurrency. Handlers run sequentially within each batch; pg-boss retries a rejected batch together.
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- `PgBossDefinition.layer` supplies its own jobs service to queue handlers and scheduled effects through the scoped Effect context. Handlers can enqueue follow-ups to worker or producer-only queues using that same client. Only the owning identifier is removed from the Layer's input requirements; other services remain typed. Startup creates every declared queue before starting consumers.
|
|
12
|
+
|
|
13
|
+
## 0.6.1 - 2026-10-17
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- Run each queue and scheduled job attempt inside a root consumer span named for its queue, with job identity, attempt and retry-count attributes and handler failures. Handler steps share that job trace.
|
|
18
|
+
- Persist the enqueueing Effect span context in pg-boss job data and link the job span to it. Handlers and `getJob` still decode the original payload Schema; direct pg-boss data readers see an envelope for traced sends.
|
|
19
|
+
|
|
3
20
|
## 0.6.0 - 2026-10-17
|
|
4
21
|
|
|
5
22
|
### Added
|
package/README.md
CHANGED
|
@@ -99,6 +99,23 @@ The schedule runs a payload-free Effect. This schedule registers its cron expres
|
|
|
99
99
|
|
|
100
100
|
The Layer requires the workers' Effect services. Leaving `Mail` unprovided leaves that requirement in the resulting Effect's type. A primitive Schema such as `Schema.String` is rejected as a queue definition because the durable contract must be an object.
|
|
101
101
|
|
|
102
|
+
### Backlog throughput and concurrency
|
|
103
|
+
|
|
104
|
+
Queue and schedule workers default to `batchSize: 10` and `burstWhenBatchFull: true`. pg-boss fetches again immediately after a full batch completes, until a fetch returns fewer than ten jobs. The adapter runs handlers sequentially within each batch, and pg-boss defaults `localConcurrency` to one. Set native `localConcurrency` to run multiple batches at once:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
const Compare = defineQueue({
|
|
108
|
+
name: "compare",
|
|
109
|
+
schema: Schema.Struct({ id: Schema.Number }),
|
|
110
|
+
queue: { notify: true },
|
|
111
|
+
worker: { batchSize: 2, localConcurrency: 5 },
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
This worker runs up to five handlers at once. `worker` accepts pg-boss 12's `WorkOptions`, including `batchSize`, `localConcurrency`, `pollingIntervalSeconds`, `notifyPollingIntervalSeconds` and the burst options. Explicit options override the adapter defaults. pg-boss ignores `burstWhenBatchFull` at `batchSize: 1`; keep a batch size of at least two for immediate backlog draining. A failed handler rejects its batch, so pg-boss can retry earlier jobs in that batch; handlers must tolerate repeated side effects.
|
|
116
|
+
|
|
117
|
+
Apps do not need to shorten the polling backstop to drain a backlog. With `notify: true` and an active LISTEN/NOTIFY listener, pg-boss wakes workers on notifications and defaults to a 30-second backstop (`notifyPollingIntervalSeconds`). Without active notifications its base poll defaults to two seconds (`pollingIntervalSeconds`). Bursting takes precedence over both intervals. Override the backstop only when the application needs a different bound for work that becomes runnable without a notification, such as delayed jobs or retries. The PostgreSQL backlog tests insert 30 jobs at once and verify completion within 2.5 seconds with both defaults and configured batches and concurrency.
|
|
118
|
+
|
|
102
119
|
### Once per runtime: provide the workers
|
|
103
120
|
|
|
104
121
|
Build the jobs Layer in `runtime.ts`:
|
|
@@ -154,6 +171,14 @@ Provide `Jobs` through the application's existing runtime. `enqueue` takes the d
|
|
|
154
171
|
|
|
155
172
|
`PgBossPayloadError` names a payload boundary failure; malformed durable input is rejected before the worker's domain code runs. The PostgreSQL test also checks that invalid data sent directly to pg-boss reaches the dead-letter queue without reaching the handler.
|
|
156
173
|
|
|
174
|
+
### Trace jobs
|
|
175
|
+
|
|
176
|
+
Each queue or scheduled job attempt runs in one root consumer span named for its queue. The span encloses payload decoding and the whole handler, so top-level `Effect.fn` steps are children of the job span. Its attributes include `job.id`, `job.queue`, `job.name`, the one-based `job.attempt` and pg-boss's zero-based `job.retry_count`. A failed handler ends the span with its failure.
|
|
177
|
+
|
|
178
|
+
`enqueue` captures the current Effect span, including an external parent span, when present. It stores that context in a typed envelope in pg-boss's existing job `data`; the adapter unwraps the envelope before Schema decoding in the handler and `getJob`, so the application's payload Schema stays unchanged. Jobs sent without a span keep their ordinary encoded payload, and workers also accept those unwrapped jobs. Direct pg-boss data readers see the envelope for traced sends.
|
|
179
|
+
|
|
180
|
+
The consumer span links to the sender rather than sharing its parent trace, following OpenTelemetry messaging conventions for asynchronous processing. Each retry gets its own job trace. Provide the application's Effect tracer when building the worker Layer; the worker uses that captured tracer. The PostgreSQL tracing tests use Effect's in-memory tracer and a producer that shuts down before its worker starts; the scheduled-worker test checks the same span boundary with a fake client.
|
|
181
|
+
|
|
157
182
|
### Read health
|
|
158
183
|
|
|
159
184
|
```ts
|
|
@@ -235,6 +260,33 @@ export const producerLayer = (databaseUrl: string) =>
|
|
|
235
260
|
|
|
236
261
|
Provide this Layer to the request runtime and call `jobs.enqueue(SendEmail, { userId })` as above. A separate worker process registers `SendEmail.handle(...)` against the same database. Producer queues use the same retry options and appear in `health`; only worker registrations capture handler services. The PostgreSQL producer test creates both queues through this Layer, enqueues a transformed payload, and observes a failing worker exhaust two retries before the job reaches its dead-letter queue.
|
|
237
262
|
|
|
263
|
+
### Enqueue follow-ups from a handler
|
|
264
|
+
|
|
265
|
+
A handler can yield the same `Jobs` service that owns it. The Layer supplies that service from its scoped client; other handler and Schema service requirements remain required inputs to the Layer.
|
|
266
|
+
|
|
267
|
+
```ts
|
|
268
|
+
const Receipt = defineQueue({
|
|
269
|
+
name: "receipt",
|
|
270
|
+
schema: Schema.Struct({ userId: Schema.Number }),
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
const sendAndQueueReceipt = SendEmail.handle(({ userId }) =>
|
|
274
|
+
Effect.gen(function* () {
|
|
275
|
+
const mail = yield* Mail;
|
|
276
|
+
yield* mail.send(userId);
|
|
277
|
+
const jobs = yield* Jobs;
|
|
278
|
+
yield* jobs.enqueue(Receipt, { userId });
|
|
279
|
+
}),
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
const live = Jobs.layer({
|
|
283
|
+
connectionString: databaseUrl,
|
|
284
|
+
jobs: [sendAndQueueReceipt, Receipt],
|
|
285
|
+
}).pipe(Layer.provide(MailLive));
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
Include every target queue in `jobs`, as either a worker or a producer-only definition. Startup creates all queues before starting consumers. The example registers `Receipt` for another process to consume. A follow-up target can also have a worker in the same Layer. Scheduled effects can yield `Jobs` too. Keep one jobs Layer in the process's native Layer graph; that Layer owns one client for enqueueing and handling. The PostgreSQL follow-up test completes a chain across two workers, enqueues to a producer-only queue and asserts one client acquisition and shutdown.
|
|
289
|
+
|
|
238
290
|
### API
|
|
239
291
|
|
|
240
292
|
Import the module that defines the name; the package has no root entry.
|
|
@@ -259,7 +311,7 @@ The application's service has these operations:
|
|
|
259
311
|
|
|
260
312
|
`Jobs.layer(options)` accepts pg-boss `ConstructorOptions` plus `jobs`, optional `clientCacheKey`, optional `clientFactory`, optional `onError` and optional `stop`. The factory type accepts constructor options and returns `PgBossClient`. Custom clients can supply the optional native capabilities `findJobs`, `retry`, `update`, `getDb` and `updateQueue` for lookup, retry, live counts and reconciliation; a requested operation fails with `PgBossError` if its required capability is absent. `onError` has the type `(error: Error) => Effect.Effect<unknown, never, R>`; `stop` uses pg-boss `StopOptions`.
|
|
261
313
|
|
|
262
|
-
`onError` handles pg-boss client `error` events using the services captured by the Layer. The Layer's requirement type includes the workers' services
|
|
314
|
+
`onError` handles pg-boss client `error` events using the services captured by the Layer. The Layer's requirement type includes the workers' services and their payload Schemas' `DecodingServices`, excluding its own jobs identifier, plus the callback's services. Only the owning jobs service is supplied automatically; a handler using another jobs definition still requires that definition's Layer. `enqueue` includes the payload Schema's `EncodingServices` in its requirement type.
|
|
263
315
|
|
|
264
316
|
`JobContext` has `id`, `name`, `signal`, `expireInSeconds` and `heartbeatSeconds`. `QueueHealth` has `name`, `activeCount`, `deadLetteredCount`, `failedCount`, `queuedCount` and `readyCount`. `JobsHealth` has `jobs` and the corresponding `activeTotal`, `deadLetteredTotal`, `failedTotal`, `queuedTotal` and `readyTotal` fields.
|
|
265
317
|
|
|
@@ -9,7 +9,7 @@ export const defineQueue = (options) => {
|
|
|
9
9
|
name: options.name,
|
|
10
10
|
queueOptions: Object.freeze({ ...options.queue }),
|
|
11
11
|
schema: options.schema,
|
|
12
|
-
workerOptions: Object.freeze({ batchSize:
|
|
12
|
+
workerOptions: Object.freeze({ batchSize: 10, burstWhenBatchFull: true, ...options.worker }),
|
|
13
13
|
};
|
|
14
14
|
return Object.freeze(definition);
|
|
15
15
|
};
|
|
@@ -25,7 +25,7 @@ export const defineSchedule = (options) => {
|
|
|
25
25
|
schedule: definition,
|
|
26
26
|
}),
|
|
27
27
|
scheduleOptions: Object.freeze({ tz: "UTC", ...options.schedule }),
|
|
28
|
-
workerOptions: Object.freeze({ batchSize:
|
|
28
|
+
workerOptions: Object.freeze({ batchSize: 10, burstWhenBatchFull: true, ...options.worker }),
|
|
29
29
|
};
|
|
30
30
|
return Object.freeze(definition);
|
|
31
31
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"definition.js","sourceRoot":"","sources":["../../src/server/definition.ts"],"names":[],"mappings":"AAoEA,MAAM,CAAC,MAAM,WAAW,GAAG,CAC1B,OAA0C,EACT,EAAE;IACnC,MAAM,UAAU,GAAmC;QAClD,IAAI,EAAE,iBAAiB;QACvB,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CACnB,MAAM,CAAC,MAAM,CAAC;YACb,IAAI,EAAE,aAAsB;YAC5B,OAAO;YACP,KAAK,EAAE,UAAU;SACjB,CAAC;QACH,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;QACjD,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,
|
|
1
|
+
{"version":3,"file":"definition.js","sourceRoot":"","sources":["../../src/server/definition.ts"],"names":[],"mappings":"AAoEA,MAAM,CAAC,MAAM,WAAW,GAAG,CAC1B,OAA0C,EACT,EAAE;IACnC,MAAM,UAAU,GAAmC;QAClD,IAAI,EAAE,iBAAiB;QACvB,MAAM,EAAE,CAAC,OAAO,EAAE,EAAE,CACnB,MAAM,CAAC,MAAM,CAAC;YACb,IAAI,EAAE,aAAsB;YAC5B,OAAO;YACP,KAAK,EAAE,UAAU;SACjB,CAAC;QACH,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;QACjD,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;KAC5F,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC,CAAC;AAUF,MAAM,CAAC,MAAM,cAAc,GAAG,CAA4B,OAAoC,EAA4B,EAAE;IAC3H,MAAM,UAAU,GAA6B;QAC5C,IAAI,EAAE,oBAAoB;QAC1B,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,YAAY,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC;QACjD,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,CACf,MAAM,CAAC,MAAM,CAAC;YACb,IAAI,EAAE,iBAA0B;YAChC,MAAM;YACN,QAAQ,EAAE,UAAU;SACpB,CAAC;QACH,eAAe,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC;QAClE,aAAa,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;KAC5F,CAAC;IACF,OAAO,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAiB,EAAc,EAAE,CAAC,CAAC;IAC7D,eAAe,EAAE,GAAG,CAAC,eAAe;IACpC,gBAAgB,EAAE,GAAG,CAAC,gBAAgB;IACtC,EAAE,EAAE,GAAG,CAAC,EAAE;IACV,IAAI,EAAE,GAAG,CAAC,IAAI;IACd,MAAM,EAAE,GAAG,CAAC,MAAM;CAClB,CAAC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jobControl.d.ts","sourceRoot":"","sources":["../../../src/server/internal/jobControl.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"jobControl.d.ts","sourceRoot":"","sources":["../../../src/server/internal/jobControl.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,wBAAgB,SAAS,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,CAAC,QAAQ,CAAC,CAoBvE;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,CAAC,OAAO,CAAC,CAkBrE"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Effect, Option, Redacted, Schema } from "effect";
|
|
2
2
|
import { PgBossPayloadError, toPgBossError } from "#error.ts";
|
|
3
|
+
import { payloadOf } from "#internal/tracing.ts";
|
|
3
4
|
export function getJobFor(client) {
|
|
4
5
|
return (queue, id) => Effect.tryPromise({
|
|
5
6
|
catch: (error) => toPgBossError("getJob", error, queue.name),
|
|
@@ -11,7 +12,7 @@ export function getJobFor(client) {
|
|
|
11
12
|
},
|
|
12
13
|
}).pipe(Effect.flatMap((job) => job === undefined
|
|
13
14
|
? Effect.succeed(Option.none())
|
|
14
|
-
: Schema.decodeUnknownEffect(queue.schema)(job.data).pipe(Effect.mapError((error) => new PgBossPayloadError({ direction: "decode", original: Redacted.make(error), queue: queue.name })), Effect.map((data) => Option.some({ ...job, data })))));
|
|
15
|
+
: Schema.decodeUnknownEffect(queue.schema)(payloadOf(job.data)).pipe(Effect.mapError((error) => new PgBossPayloadError({ direction: "decode", original: Redacted.make(error), queue: queue.name })), Effect.map((data) => Option.some({ ...job, data })))));
|
|
15
16
|
}
|
|
16
17
|
export function retryFor(client) {
|
|
17
18
|
return (queue, id) => Effect.tryPromise({
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jobControl.js","sourceRoot":"","sources":["../../../src/server/internal/jobControl.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE1D,OAAO,EAAE,kBAAkB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"jobControl.js","sourceRoot":"","sources":["../../../src/server/internal/jobControl.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import { type Context, Effect } from "effect";
|
|
|
2
2
|
import type { PgBossClient } from "#client.ts";
|
|
3
3
|
import { type JobRegistration } from "#definition.ts";
|
|
4
4
|
import { PgBossQueueOptionsError } from "#error.ts";
|
|
5
|
-
export declare
|
|
5
|
+
export declare function registrationName(registration: JobRegistration): string;
|
|
6
6
|
export declare const registrationNames: (registrations: readonly JobRegistration[]) => readonly string[];
|
|
7
7
|
export declare const registerJobs: <R>(client: PgBossClient, registrations: readonly JobRegistration[], context: Context.Context<R>, replaceWorkers: boolean) => Effect.Effect<void, import("#error.ts").PgBossError | PgBossQueueOptionsError>;
|
|
8
8
|
//# sourceMappingURL=register.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/server/internal/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,EAAoB,MAAM,QAAQ,CAAC;AAEhE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,
|
|
1
|
+
{"version":3,"file":"register.d.ts","sourceRoot":"","sources":["../../../src/server/internal/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,OAAO,EAAE,MAAM,EAAoB,MAAM,QAAQ,CAAC;AAEhE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EACN,KAAK,eAAe,EAMpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAsB,uBAAuB,EAAiB,MAAM,WAAW,CAAC;AA6BvF,wBAAgB,gBAAgB,CAAC,YAAY,EAAE,eAAe,GAAG,MAAM,CAEtE;AAgED,eAAO,MAAM,iBAAiB,kBAAmB,SAAS,eAAe,EAAE,KAAG,SAAS,MAAM,EAM5F,CAAC;AAEF,eAAO,MAAM,YAAY,GAAI,CAAC,UACrB,YAAY,iBACL,SAAS,eAAe,EAAE,WAChC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,kBACX,OAAO,KACrB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,WAAW,EAAE,WAAW,GAAG,uBAAuB,CAe7E,CAAC"}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { Effect, Redacted, Schema } from "effect";
|
|
2
|
-
import { jobContext } from "#definition.ts";
|
|
2
|
+
import { jobContext, } from "#definition.ts";
|
|
3
3
|
import { PgBossPayloadError, PgBossQueueOptionsError, toPgBossError } from "#error.ts";
|
|
4
4
|
import { ensureQueue } from "#internal/ensureQueue.ts";
|
|
5
|
+
import { payloadOf, traceJob } from "#internal/tracing.ts";
|
|
5
6
|
function isQueueWorker(registration) {
|
|
6
7
|
return registration._tag === "QueueWorker";
|
|
7
8
|
}
|
|
@@ -11,35 +12,45 @@ function isScheduledWorker(registration) {
|
|
|
11
12
|
function isQueueDefinition(registration) {
|
|
12
13
|
return registration._tag === "QueueDefinition";
|
|
13
14
|
}
|
|
14
|
-
|
|
15
|
+
function registrationDefinition(registration) {
|
|
15
16
|
if (isQueueDefinition(registration)) {
|
|
16
|
-
return registration
|
|
17
|
+
return registration;
|
|
17
18
|
}
|
|
18
19
|
if (isQueueWorker(registration)) {
|
|
19
|
-
return registration.queue
|
|
20
|
+
return registration.queue;
|
|
20
21
|
}
|
|
21
22
|
if (isScheduledWorker(registration)) {
|
|
22
|
-
return registration.schedule
|
|
23
|
+
return registration.schedule;
|
|
23
24
|
}
|
|
24
25
|
throw new TypeError(`Unknown pg-boss registration: ${registration._tag}`);
|
|
25
|
-
}
|
|
26
|
+
}
|
|
27
|
+
export function registrationName(registration) {
|
|
28
|
+
return registrationDefinition(registration).name;
|
|
29
|
+
}
|
|
30
|
+
async function prepareQueues(client, registrations, replaceWorkers) {
|
|
31
|
+
for (const registration of registrations) {
|
|
32
|
+
if (replaceWorkers && !isQueueDefinition(registration)) {
|
|
33
|
+
await client.offWork(registrationName(registration));
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
for (const registration of registrations) {
|
|
37
|
+
const definition = registrationDefinition(registration);
|
|
38
|
+
await ensureQueue(client, definition.name, definition.queueOptions);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
26
41
|
function workerContext(context) {
|
|
27
42
|
return context;
|
|
28
43
|
}
|
|
29
44
|
function runQueueWorker(worker, job, context) {
|
|
30
|
-
return Effect.runPromise(Schema.decodeUnknownEffect(worker.queue.schema)(job.data).pipe(Effect.mapError((error) => new PgBossPayloadError({
|
|
45
|
+
return Effect.runPromise(Schema.decodeUnknownEffect(worker.queue.schema)(payloadOf(job.data)).pipe(Effect.mapError((error) => new PgBossPayloadError({
|
|
31
46
|
direction: "decode",
|
|
32
47
|
original: Redacted.make(error),
|
|
33
48
|
queue: worker.queue.name,
|
|
34
|
-
})), Effect.flatMap((payload) => worker.handler(payload, jobContext(job))), Effect.provide(context)), { signal: job.signal });
|
|
49
|
+
})), Effect.flatMap((payload) => worker.handler(payload, jobContext(job))), (effect) => traceJob(effect, job, worker.queue.name), Effect.provide(context)), { signal: job.signal });
|
|
35
50
|
}
|
|
36
|
-
async function registerQueue(client, registration, context
|
|
51
|
+
async function registerQueue(client, registration, context) {
|
|
37
52
|
const queue = registration._tag === "QueueWorker" ? registration.queue : registration;
|
|
38
53
|
const name = queue.name;
|
|
39
|
-
if (replaceWorker && registration._tag === "QueueWorker") {
|
|
40
|
-
await client.offWork(name);
|
|
41
|
-
}
|
|
42
|
-
await ensureQueue(client, name, queue.queueOptions);
|
|
43
54
|
if (registration._tag === "QueueDefinition") {
|
|
44
55
|
return;
|
|
45
56
|
}
|
|
@@ -49,15 +60,11 @@ async function registerQueue(client, registration, context, replaceWorker) {
|
|
|
49
60
|
}
|
|
50
61
|
});
|
|
51
62
|
}
|
|
52
|
-
async function registerSchedule(client, worker, context
|
|
63
|
+
async function registerSchedule(client, worker, context) {
|
|
53
64
|
const { schedule } = worker;
|
|
54
|
-
if (replaceWorker) {
|
|
55
|
-
await client.offWork(schedule.name);
|
|
56
|
-
}
|
|
57
|
-
await ensureQueue(client, schedule.name, schedule.queueOptions);
|
|
58
65
|
await client.work(schedule.name, schedule.workerOptions, async (jobs) => {
|
|
59
66
|
for (const job of jobs) {
|
|
60
|
-
await Effect.runPromise(Effect.provide(worker.effect, context), {
|
|
67
|
+
await Effect.runPromise(Effect.provide(traceJob(worker.effect, job, schedule.name), context), {
|
|
61
68
|
signal: job.signal,
|
|
62
69
|
});
|
|
63
70
|
}
|
|
@@ -75,13 +82,14 @@ export const registerJobs = (client, registrations, context, replaceWorkers) =>
|
|
|
75
82
|
catch: (error) => (error instanceof PgBossQueueOptionsError ? error : toPgBossError("register", error)),
|
|
76
83
|
try: async () => {
|
|
77
84
|
registrationNames(registrations);
|
|
85
|
+
await prepareQueues(client, registrations, replaceWorkers);
|
|
78
86
|
const provided = workerContext(context);
|
|
79
87
|
for (const registration of registrations) {
|
|
80
88
|
if (isQueueWorker(registration) || isQueueDefinition(registration)) {
|
|
81
|
-
await registerQueue(client, registration, provided
|
|
89
|
+
await registerQueue(client, registration, provided);
|
|
82
90
|
}
|
|
83
91
|
else if (isScheduledWorker(registration)) {
|
|
84
|
-
await registerSchedule(client, registration, provided
|
|
92
|
+
await registerSchedule(client, registration, provided);
|
|
85
93
|
}
|
|
86
94
|
}
|
|
87
95
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../../src/server/internal/register.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGhE,OAAO,
|
|
1
|
+
{"version":3,"file":"register.js","sourceRoot":"","sources":["../../../src/server/internal/register.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/server/internal/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../../src/server/internal/service.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAM/C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEjD,eAAO,MAAM,WAAW,WAAY,YAAY,SAAS,SAAS,MAAM,EAAE,sBAAsB,aAoC9F,CAAC"}
|
|
@@ -3,6 +3,7 @@ import { PgBossPayloadError, toPgBossError } from "#error.ts";
|
|
|
3
3
|
import { countsFor } from "#internal/countsFor.ts";
|
|
4
4
|
import { healthFor } from "#internal/health.ts";
|
|
5
5
|
import { getJobFor, retryFor } from "#internal/jobControl.ts";
|
|
6
|
+
import { capturePayload } from "#internal/tracing.ts";
|
|
6
7
|
export const makeService = (client, names, schema = "pgboss") => ({
|
|
7
8
|
counts: (queue) => countsFor(client, schema, queue.name),
|
|
8
9
|
enqueue: (queue, payload, options) => Schema.encodeUnknownEffect(queue.schema)(payload).pipe(Effect.mapError((error) => new PgBossPayloadError({
|
|
@@ -17,10 +18,10 @@ export const makeService = (client, names, schema = "pgboss") => ({
|
|
|
17
18
|
queue: queue.name,
|
|
18
19
|
}));
|
|
19
20
|
}
|
|
20
|
-
return Effect.tryPromise({
|
|
21
|
+
return capturePayload(encoded).pipe(Effect.flatMap((data) => Effect.tryPromise({
|
|
21
22
|
catch: (error) => toPgBossError("enqueue", error, queue.name),
|
|
22
|
-
try: () => client.send(queue.name,
|
|
23
|
-
})
|
|
23
|
+
try: () => client.send(queue.name, data, options),
|
|
24
|
+
})), Effect.map(Option.fromNullishOr));
|
|
24
25
|
})),
|
|
25
26
|
getJob: getJobFor(client),
|
|
26
27
|
health: healthFor(client, names),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/server/internal/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE1D,OAAO,EAAoB,kBAAkB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../../src/server/internal/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAE1D,OAAO,EAAoB,kBAAkB,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAChF,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAoB,EAAE,KAAwB,EAAE,MAAM,GAAG,QAAQ,EAAiB,EAAE,CAAC,CAAC;IACjH,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,IAAI,CAAC;IACxD,OAAO,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,CACpC,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CACrD,MAAM,CAAC,QAAQ,CACd,CAAC,KAAK,EAAE,EAAE,CACT,IAAI,kBAAkB,CAAC;QACtB,SAAS,EAAE,QAAQ;QACnB,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;QAC9B,KAAK,EAAE,KAAK,CAAC,IAAI;KACjB,CAAC,CACH,EACD,MAAM,CAAC,OAAO,CAAC,CAAC,OAAO,EAA0E,EAAE;QAClG,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrD,OAAO,MAAM,CAAC,IAAI,CACjB,IAAI,kBAAkB,CAAC;gBACtB,SAAS,EAAE,QAAQ;gBACnB,QAAQ,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,SAAS,CAAC,yCAAyC,CAAC,CAAC;gBACjF,KAAK,EAAE,KAAK,CAAC,IAAI;aACjB,CAAC,CACF,CAAC;QACH,CAAC;QACD,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC,IAAI,CAClC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CACvB,MAAM,CAAC,UAAU,CAAC;YACjB,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,CAAC;YAC7D,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC;SACjD,CAAC,CACF,EACD,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC,CAChC,CAAC;IACH,CAAC,CAAC,CACF;IACF,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC;IACzB,MAAM,EAAE,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC;IAChC,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC;CACvB,CAAC,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { Effect, Tracer } from "effect";
|
|
2
|
+
import type { Job } from "pg-boss";
|
|
3
|
+
export declare function capturePayload(payload: object): Effect.Effect<object>;
|
|
4
|
+
export declare function payloadOf(data: unknown): unknown;
|
|
5
|
+
export declare function traceJob<A, E, R>(effect: Effect.Effect<A, E, R>, job: Job<unknown>, queue: string): Effect.Effect<A, E, Exclude<R, Tracer.ParentSpan>>;
|
|
6
|
+
//# sourceMappingURL=tracing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../../../src/server/internal/tracing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAkB,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxD,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,SAAS,CAAC;AAUnC,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,CAUrE;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,CAEhD;AAED,wBAAgB,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,sDAgBjG"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Effect, Option, Schema, Tracer } from "effect";
|
|
2
|
+
const TracedPayload = Schema.Struct({
|
|
3
|
+
_tag: Schema.Literal("@shivaedev/effect-pg-boss/TracedPayload"),
|
|
4
|
+
payload: Schema.Unknown,
|
|
5
|
+
span: Schema.Struct({ sampled: Schema.Boolean, spanId: Schema.String, traceId: Schema.String }),
|
|
6
|
+
});
|
|
7
|
+
const isTracedPayload = Schema.is(TracedPayload);
|
|
8
|
+
export function capturePayload(payload) {
|
|
9
|
+
return Effect.map(Effect.option(Effect.currentParentSpan), (span) => Option.isNone(span)
|
|
10
|
+
? payload
|
|
11
|
+
: {
|
|
12
|
+
_tag: "@shivaedev/effect-pg-boss/TracedPayload",
|
|
13
|
+
payload,
|
|
14
|
+
span: { sampled: span.value.sampled, spanId: span.value.spanId, traceId: span.value.traceId },
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
export function payloadOf(data) {
|
|
18
|
+
return isTracedPayload(data) ? data.payload : data;
|
|
19
|
+
}
|
|
20
|
+
export function traceJob(effect, job, queue) {
|
|
21
|
+
return Effect.withSpan(effect, queue, {
|
|
22
|
+
attributes: {
|
|
23
|
+
"job.attempt": job.retryCount + 1,
|
|
24
|
+
"job.id": job.id,
|
|
25
|
+
"job.name": job.name,
|
|
26
|
+
"job.queue": queue,
|
|
27
|
+
"job.retry_count": job.retryCount,
|
|
28
|
+
"messaging.destination.name": queue,
|
|
29
|
+
"messaging.message.id": job.id,
|
|
30
|
+
"messaging.system": "pg-boss",
|
|
31
|
+
},
|
|
32
|
+
kind: "consumer",
|
|
33
|
+
links: isTracedPayload(job.data) ? [{ attributes: {}, span: Tracer.externalSpan(job.data.span) }] : [],
|
|
34
|
+
root: true,
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=tracing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tracing.js","sourceRoot":"","sources":["../../../src/server/internal/tracing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAGxD,MAAM,aAAa,GAAG,MAAM,CAAC,MAAM,CAAC;IACnC,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,yCAAyC,CAAC;IAC/D,OAAO,EAAE,MAAM,CAAC,OAAO;IACvB,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;CAC/F,CAAC,CAAC;AAEH,MAAM,eAAe,GAAG,MAAM,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC;AAEjD,MAAM,UAAU,cAAc,CAAC,OAAe;IAC7C,OAAO,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CACnE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC;QAClB,CAAC,CAAC,OAAO;QACT,CAAC,CAAE;YACD,IAAI,EAAE,yCAAyC;YAC/C,OAAO;YACP,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE;SACxD,CACxC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAa;IACtC,OAAO,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,QAAQ,CAAU,MAA8B,EAAE,GAAiB,EAAE,KAAa;IACjG,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,KAAK,EAAE;QACrC,UAAU,EAAE;YACX,aAAa,EAAE,GAAG,CAAC,UAAU,GAAG,CAAC;YACjC,QAAQ,EAAE,GAAG,CAAC,EAAE;YAChB,UAAU,EAAE,GAAG,CAAC,IAAI;YACpB,WAAW,EAAE,KAAK;YAClB,iBAAiB,EAAE,GAAG,CAAC,UAAU;YACjC,4BAA4B,EAAE,KAAK;YACnC,sBAAsB,EAAE,GAAG,CAAC,EAAE;YAC9B,kBAAkB,EAAE,SAAS;SAC7B;QACD,IAAI,EAAE,UAAU;QAChB,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE;QACtG,IAAI,EAAE,IAAI;KACV,CAAC,CAAC;AACJ,CAAC"}
|
package/dist/server/service.d.ts
CHANGED
|
@@ -19,7 +19,7 @@ export type PgBossLayerOptions<TRegistrations extends readonly JobRegistration[]
|
|
|
19
19
|
readonly stop?: StopOptions;
|
|
20
20
|
};
|
|
21
21
|
export interface PgBossDefinition<TIdentifier extends string = string> extends Context.Service<TIdentifier, PgBossService> {
|
|
22
|
-
readonly layer: <const TRegistrations extends readonly JobRegistration[], TErrorRequirements = never>(options: PgBossLayerOptions<TRegistrations, TErrorRequirements>) => Layer.Layer<TIdentifier, PgBossError | PgBossQueueOptionsError, RegistrationRequirements<TRegistrations> | TErrorRequirements>;
|
|
22
|
+
readonly layer: <const TRegistrations extends readonly JobRegistration[], TErrorRequirements = never>(options: PgBossLayerOptions<TRegistrations, TErrorRequirements>) => Layer.Layer<TIdentifier, PgBossError | PgBossQueueOptionsError, Exclude<RegistrationRequirements<TRegistrations>, TIdentifier> | TErrorRequirements>;
|
|
23
23
|
}
|
|
24
24
|
export declare const makePgBoss: <const TIdentifier extends string>(identifier: TIdentifier) => PgBossDefinition<TIdentifier>;
|
|
25
25
|
//# sourceMappingURL=service.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"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
|
|
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
|
@@ -11,19 +11,21 @@ export const makePgBoss = (identifier) => {
|
|
|
11
11
|
const names = options.jobs.map(registrationName);
|
|
12
12
|
const { clientFactory, clientCacheKey, jobs, onError, stop, ...constructorOptions } = options;
|
|
13
13
|
const acquire = Effect.acquireRelease(Effect.gen(function* () {
|
|
14
|
-
const
|
|
14
|
+
const dependencies = yield* Effect.context();
|
|
15
15
|
const acquired = yield* acquireClient({
|
|
16
16
|
clientCacheKey,
|
|
17
17
|
clientFactory,
|
|
18
18
|
constructor: constructorOptions,
|
|
19
19
|
});
|
|
20
|
+
const service = makeService(acquired.client, names, constructorOptions.schema);
|
|
21
|
+
const context = Context.add(dependencies, Service, service);
|
|
20
22
|
const reportError = onError ?? logClientError;
|
|
21
23
|
const errorListener = (error) => {
|
|
22
24
|
void Effect.runPromise(Effect.exit(Effect.provide(reportError(error), context)));
|
|
23
25
|
};
|
|
24
26
|
acquired.client.on("error", errorListener);
|
|
25
27
|
const registration = registerJobs(acquired.client, jobs, context, acquired.reused);
|
|
26
|
-
return yield* registration.pipe(Effect.as({ acquired,
|
|
28
|
+
return yield* registration.pipe(Effect.as({ acquired, errorListener, service }), Effect.onError(() => {
|
|
27
29
|
acquired.client.off("error", errorListener);
|
|
28
30
|
return releaseClient(acquired, stop);
|
|
29
31
|
}));
|
|
@@ -31,7 +33,7 @@ export const makePgBoss = (identifier) => {
|
|
|
31
33
|
resource.acquired.client.off("error", resource.errorListener);
|
|
32
34
|
return releaseClient(resource.acquired, stop);
|
|
33
35
|
});
|
|
34
|
-
return Layer.effect(Service, Effect.map(acquire, ({
|
|
36
|
+
return Layer.effect(Service, Effect.map(acquire, ({ service }) => service));
|
|
35
37
|
};
|
|
36
38
|
return Object.assign(Service, { layer });
|
|
37
39
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAA4B,MAAM,QAAQ,CAAC;AAM1E,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../../src/server/service.ts"],"names":[],"mappings":"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"}
|
package/docs/roadmap.md
CHANGED
|
@@ -9,6 +9,8 @@ This file owns the package's implementation inventory and open API choices. The
|
|
|
9
9
|
- [x] Encoding at enqueue and decoding before the handler. The PostgreSQL test round-trips a transformed payload and checks that malformed stored data reaches the dead-letter queue without calling the domain handler.
|
|
10
10
|
- [x] Queue and schedule registration, with dead-letter queues, default retry settings and UTC schedule registration. Fake-client tests verify the supplied settings and a retry-limit override.
|
|
11
11
|
- [x] Producer-only queue definitions create queues and dead-letter queues through `Jobs.layer({ jobs: [Queue] })`, without starting a consumer. The [PostgreSQL producer test](../src/server/producerPostgres.spec.ts) enqueues a transformed payload and observes worker retry exhaustion and dead-letter delivery. The [starter producer](../../../examples/starter/src/server/noteProducer.ts) uses this API.
|
|
12
|
+
- [x] Native pg-boss burst defaults (`batchSize: 10`, `burstWhenBatchFull: true`) and configurable batches and local concurrency. [PostgreSQL backlog evidence](../src/server/throughputPostgres.spec.ts) drains 30 jobs within 2.5 seconds.
|
|
13
|
+
- [x] Handlers and scheduled effects can yield their owning jobs service without an external self requirement. [PostgreSQL follow-up evidence](../src/server/followUpPostgres.spec.ts) completes a worker chain and enqueues to a producer-only queue using one scoped client. All declared queues exist before consumers start.
|
|
12
14
|
- [x] Worker and scheduled Effect service capture. Runtime and packed-consumer type tests preserve handler requirements.
|
|
13
15
|
- [x] Scoped startup and shutdown, including cleanup when startup or registration fails.
|
|
14
16
|
- [x] Explicit keyed client reuse and unkeyed per-build acquisition. Lifecycle tests cover two builds sharing one client and replacing the registered worker.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shivaedev/effect-pg-boss",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Effect-native jobs and lifecycle management for pg-boss",
|
|
5
5
|
"homepage": "https://github.com/ShivaeDev/platform/tree/main/packages/effect-pg-boss#readme",
|
|
6
6
|
"bugs": {
|
package/src/server/definition.ts
CHANGED
|
@@ -80,7 +80,7 @@ export const defineQueue = <const Name extends string, const Payload extends Job
|
|
|
80
80
|
name: options.name,
|
|
81
81
|
queueOptions: Object.freeze({ ...options.queue }),
|
|
82
82
|
schema: options.schema,
|
|
83
|
-
workerOptions: Object.freeze({ batchSize:
|
|
83
|
+
workerOptions: Object.freeze({ batchSize: 10, burstWhenBatchFull: true, ...options.worker }),
|
|
84
84
|
};
|
|
85
85
|
return Object.freeze(definition);
|
|
86
86
|
};
|
|
@@ -106,7 +106,7 @@ export const defineSchedule = <const Name extends string>(options: DefineSchedul
|
|
|
106
106
|
schedule: definition,
|
|
107
107
|
}),
|
|
108
108
|
scheduleOptions: Object.freeze({ tz: "UTC", ...options.schedule }),
|
|
109
|
-
workerOptions: Object.freeze({ batchSize:
|
|
109
|
+
workerOptions: Object.freeze({ batchSize: 10, burstWhenBatchFull: true, ...options.worker }),
|
|
110
110
|
};
|
|
111
111
|
return Object.freeze(definition);
|
|
112
112
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Effect, Option, Redacted, Schema } from "effect";
|
|
2
2
|
import type { PgBossClient } from "#client.ts";
|
|
3
3
|
import { PgBossPayloadError, toPgBossError } from "#error.ts";
|
|
4
|
+
import { payloadOf } from "#internal/tracing.ts";
|
|
4
5
|
import type { PgBossService } from "#service.ts";
|
|
5
6
|
|
|
6
7
|
export function getJobFor(client: PgBossClient): PgBossService["getJob"] {
|
|
@@ -17,7 +18,7 @@ export function getJobFor(client: PgBossClient): PgBossService["getJob"] {
|
|
|
17
18
|
Effect.flatMap((job) =>
|
|
18
19
|
job === undefined
|
|
19
20
|
? Effect.succeed(Option.none())
|
|
20
|
-
: Schema.decodeUnknownEffect(queue.schema)(job.data).pipe(
|
|
21
|
+
: Schema.decodeUnknownEffect(queue.schema)(payloadOf(job.data)).pipe(
|
|
21
22
|
Effect.mapError((error) => new PgBossPayloadError({ direction: "decode", original: Redacted.make(error), queue: queue.name })),
|
|
22
23
|
Effect.map((data) => Option.some({ ...job, data })),
|
|
23
24
|
),
|
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
import { type Context, Effect, Redacted, Schema } from "effect";
|
|
2
2
|
import type { Job } from "pg-boss";
|
|
3
3
|
import type { PgBossClient } from "#client.ts";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
type JobRegistration,
|
|
6
|
+
jobContext,
|
|
7
|
+
type QueueDefinition,
|
|
8
|
+
type QueueWorker,
|
|
9
|
+
type ScheduleDefinition,
|
|
10
|
+
type ScheduledWorker,
|
|
11
|
+
} from "#definition.ts";
|
|
5
12
|
import { PgBossPayloadError, PgBossQueueOptionsError, toPgBossError } from "#error.ts";
|
|
6
13
|
import { ensureQueue } from "#internal/ensureQueue.ts";
|
|
14
|
+
import { payloadOf, traceJob } from "#internal/tracing.ts";
|
|
7
15
|
|
|
8
16
|
function isQueueWorker(registration: JobRegistration): registration is QueueWorker {
|
|
9
17
|
return registration._tag === "QueueWorker";
|
|
@@ -17,18 +25,34 @@ function isQueueDefinition(registration: JobRegistration): registration is Queue
|
|
|
17
25
|
return registration._tag === "QueueDefinition";
|
|
18
26
|
}
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
function registrationDefinition(registration: JobRegistration): QueueDefinition | ScheduleDefinition {
|
|
21
29
|
if (isQueueDefinition(registration)) {
|
|
22
|
-
return registration
|
|
30
|
+
return registration;
|
|
23
31
|
}
|
|
24
32
|
if (isQueueWorker(registration)) {
|
|
25
|
-
return registration.queue
|
|
33
|
+
return registration.queue;
|
|
26
34
|
}
|
|
27
35
|
if (isScheduledWorker(registration)) {
|
|
28
|
-
return registration.schedule
|
|
36
|
+
return registration.schedule;
|
|
29
37
|
}
|
|
30
38
|
throw new TypeError(`Unknown pg-boss registration: ${registration._tag}`);
|
|
31
|
-
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function registrationName(registration: JobRegistration): string {
|
|
42
|
+
return registrationDefinition(registration).name;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function prepareQueues(client: PgBossClient, registrations: readonly JobRegistration[], replaceWorkers: boolean): Promise<void> {
|
|
46
|
+
for (const registration of registrations) {
|
|
47
|
+
if (replaceWorkers && !isQueueDefinition(registration)) {
|
|
48
|
+
await client.offWork(registrationName(registration));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
for (const registration of registrations) {
|
|
52
|
+
const definition = registrationDefinition(registration);
|
|
53
|
+
await ensureQueue(client, definition.name, definition.queueOptions);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
32
56
|
|
|
33
57
|
// The layer requires RegistrationRequirements of its jobs, so its context provides every registered worker.
|
|
34
58
|
function workerContext<R>(context: Context.Context<R>): Context.Context<unknown>;
|
|
@@ -38,7 +62,7 @@ function workerContext(context: unknown): unknown {
|
|
|
38
62
|
|
|
39
63
|
function runQueueWorker(worker: QueueWorker, job: Job<unknown>, context: Context.Context<unknown>): Promise<unknown> {
|
|
40
64
|
return Effect.runPromise(
|
|
41
|
-
Schema.decodeUnknownEffect(worker.queue.schema)(job.data).pipe(
|
|
65
|
+
Schema.decodeUnknownEffect(worker.queue.schema)(payloadOf(job.data)).pipe(
|
|
42
66
|
Effect.mapError(
|
|
43
67
|
(error) =>
|
|
44
68
|
new PgBossPayloadError({
|
|
@@ -48,24 +72,16 @@ function runQueueWorker(worker: QueueWorker, job: Job<unknown>, context: Context
|
|
|
48
72
|
}),
|
|
49
73
|
),
|
|
50
74
|
Effect.flatMap((payload) => worker.handler(payload, jobContext(job))),
|
|
75
|
+
(effect) => traceJob(effect, job, worker.queue.name),
|
|
51
76
|
Effect.provide(context),
|
|
52
77
|
),
|
|
53
78
|
{ signal: job.signal },
|
|
54
79
|
);
|
|
55
80
|
}
|
|
56
81
|
|
|
57
|
-
async function registerQueue(
|
|
58
|
-
client: PgBossClient,
|
|
59
|
-
registration: QueueWorker | QueueDefinition,
|
|
60
|
-
context: Context.Context<unknown>,
|
|
61
|
-
replaceWorker: boolean,
|
|
62
|
-
): Promise<void> {
|
|
82
|
+
async function registerQueue(client: PgBossClient, registration: QueueWorker | QueueDefinition, context: Context.Context<unknown>): Promise<void> {
|
|
63
83
|
const queue = registration._tag === "QueueWorker" ? registration.queue : registration;
|
|
64
84
|
const name = queue.name;
|
|
65
|
-
if (replaceWorker && registration._tag === "QueueWorker") {
|
|
66
|
-
await client.offWork(name);
|
|
67
|
-
}
|
|
68
|
-
await ensureQueue(client, name, queue.queueOptions);
|
|
69
85
|
if (registration._tag === "QueueDefinition") {
|
|
70
86
|
return;
|
|
71
87
|
}
|
|
@@ -76,20 +92,11 @@ async function registerQueue(
|
|
|
76
92
|
});
|
|
77
93
|
}
|
|
78
94
|
|
|
79
|
-
async function registerSchedule(
|
|
80
|
-
client: PgBossClient,
|
|
81
|
-
worker: ScheduledWorker,
|
|
82
|
-
context: Context.Context<unknown>,
|
|
83
|
-
replaceWorker: boolean,
|
|
84
|
-
): Promise<void> {
|
|
95
|
+
async function registerSchedule(client: PgBossClient, worker: ScheduledWorker, context: Context.Context<unknown>): Promise<void> {
|
|
85
96
|
const { schedule } = worker;
|
|
86
|
-
if (replaceWorker) {
|
|
87
|
-
await client.offWork(schedule.name);
|
|
88
|
-
}
|
|
89
|
-
await ensureQueue(client, schedule.name, schedule.queueOptions);
|
|
90
97
|
await client.work(schedule.name, schedule.workerOptions, async (jobs: readonly Job<unknown>[]) => {
|
|
91
98
|
for (const job of jobs) {
|
|
92
|
-
await Effect.runPromise(Effect.provide(worker.effect, context), {
|
|
99
|
+
await Effect.runPromise(Effect.provide(traceJob(worker.effect, job, schedule.name), context), {
|
|
93
100
|
signal: job.signal,
|
|
94
101
|
});
|
|
95
102
|
}
|
|
@@ -115,12 +122,13 @@ export const registerJobs = <R>(
|
|
|
115
122
|
catch: (error) => (error instanceof PgBossQueueOptionsError ? error : toPgBossError("register", error)),
|
|
116
123
|
try: async () => {
|
|
117
124
|
registrationNames(registrations);
|
|
125
|
+
await prepareQueues(client, registrations, replaceWorkers);
|
|
118
126
|
const provided = workerContext(context);
|
|
119
127
|
for (const registration of registrations) {
|
|
120
128
|
if (isQueueWorker(registration) || isQueueDefinition(registration)) {
|
|
121
|
-
await registerQueue(client, registration, provided
|
|
129
|
+
await registerQueue(client, registration, provided);
|
|
122
130
|
} else if (isScheduledWorker(registration)) {
|
|
123
|
-
await registerSchedule(client, registration, provided
|
|
131
|
+
await registerSchedule(client, registration, provided);
|
|
124
132
|
}
|
|
125
133
|
}
|
|
126
134
|
},
|
|
@@ -4,6 +4,7 @@ import { type PgBossError, PgBossPayloadError, toPgBossError } from "#error.ts";
|
|
|
4
4
|
import { countsFor } from "#internal/countsFor.ts";
|
|
5
5
|
import { healthFor } from "#internal/health.ts";
|
|
6
6
|
import { getJobFor, retryFor } from "#internal/jobControl.ts";
|
|
7
|
+
import { capturePayload } from "#internal/tracing.ts";
|
|
7
8
|
import type { PgBossService } from "#service.ts";
|
|
8
9
|
|
|
9
10
|
export const makeService = (client: PgBossClient, names: readonly string[], schema = "pgboss"): PgBossService => ({
|
|
@@ -28,10 +29,15 @@ export const makeService = (client: PgBossClient, names: readonly string[], sche
|
|
|
28
29
|
}),
|
|
29
30
|
);
|
|
30
31
|
}
|
|
31
|
-
return
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
return capturePayload(encoded).pipe(
|
|
33
|
+
Effect.flatMap((data) =>
|
|
34
|
+
Effect.tryPromise({
|
|
35
|
+
catch: (error) => toPgBossError("enqueue", error, queue.name),
|
|
36
|
+
try: () => client.send(queue.name, data, options),
|
|
37
|
+
}),
|
|
38
|
+
),
|
|
39
|
+
Effect.map(Option.fromNullishOr),
|
|
40
|
+
);
|
|
35
41
|
}),
|
|
36
42
|
),
|
|
37
43
|
getJob: getJobFor(client),
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Effect, Option, Schema, Tracer } from "effect";
|
|
2
|
+
import type { Job } from "pg-boss";
|
|
3
|
+
|
|
4
|
+
const TracedPayload = Schema.Struct({
|
|
5
|
+
_tag: Schema.Literal("@shivaedev/effect-pg-boss/TracedPayload"),
|
|
6
|
+
payload: Schema.Unknown,
|
|
7
|
+
span: Schema.Struct({ sampled: Schema.Boolean, spanId: Schema.String, traceId: Schema.String }),
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
const isTracedPayload = Schema.is(TracedPayload);
|
|
11
|
+
|
|
12
|
+
export function capturePayload(payload: object): Effect.Effect<object> {
|
|
13
|
+
return Effect.map(Effect.option(Effect.currentParentSpan), (span) =>
|
|
14
|
+
Option.isNone(span)
|
|
15
|
+
? payload
|
|
16
|
+
: ({
|
|
17
|
+
_tag: "@shivaedev/effect-pg-boss/TracedPayload",
|
|
18
|
+
payload,
|
|
19
|
+
span: { sampled: span.value.sampled, spanId: span.value.spanId, traceId: span.value.traceId },
|
|
20
|
+
} satisfies typeof TracedPayload.Type),
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function payloadOf(data: unknown): unknown {
|
|
25
|
+
return isTracedPayload(data) ? data.payload : data;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function traceJob<A, E, R>(effect: Effect.Effect<A, E, R>, job: Job<unknown>, queue: string) {
|
|
29
|
+
return Effect.withSpan(effect, queue, {
|
|
30
|
+
attributes: {
|
|
31
|
+
"job.attempt": job.retryCount + 1,
|
|
32
|
+
"job.id": job.id,
|
|
33
|
+
"job.name": job.name,
|
|
34
|
+
"job.queue": queue,
|
|
35
|
+
"job.retry_count": job.retryCount,
|
|
36
|
+
"messaging.destination.name": queue,
|
|
37
|
+
"messaging.message.id": job.id,
|
|
38
|
+
"messaging.system": "pg-boss",
|
|
39
|
+
},
|
|
40
|
+
kind: "consumer",
|
|
41
|
+
links: isTracedPayload(job.data) ? [{ attributes: {}, span: Tracer.externalSpan(job.data.span) }] : [],
|
|
42
|
+
root: true,
|
|
43
|
+
});
|
|
44
|
+
}
|
package/src/server/service.ts
CHANGED
|
@@ -35,7 +35,11 @@ export type PgBossLayerOptions<TRegistrations extends readonly JobRegistration[]
|
|
|
35
35
|
export interface PgBossDefinition<TIdentifier extends string = string> extends Context.Service<TIdentifier, PgBossService> {
|
|
36
36
|
readonly layer: <const TRegistrations extends readonly JobRegistration[], TErrorRequirements = never>(
|
|
37
37
|
options: PgBossLayerOptions<TRegistrations, TErrorRequirements>,
|
|
38
|
-
) => Layer.Layer<
|
|
38
|
+
) => Layer.Layer<
|
|
39
|
+
TIdentifier,
|
|
40
|
+
PgBossError | PgBossQueueOptionsError,
|
|
41
|
+
Exclude<RegistrationRequirements<TRegistrations>, TIdentifier> | TErrorRequirements
|
|
42
|
+
>;
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
function logClientError(error: Error): Effect.Effect<void> {
|
|
@@ -47,27 +51,33 @@ export const makePgBoss = <const TIdentifier extends string>(identifier: TIdenti
|
|
|
47
51
|
|
|
48
52
|
const layer = <const TRegistrations extends readonly JobRegistration[], TErrorRequirements = never>(
|
|
49
53
|
options: PgBossLayerOptions<TRegistrations, TErrorRequirements>,
|
|
50
|
-
): Layer.Layer<
|
|
51
|
-
|
|
54
|
+
): Layer.Layer<
|
|
55
|
+
TIdentifier,
|
|
56
|
+
PgBossError | PgBossQueueOptionsError,
|
|
57
|
+
Exclude<RegistrationRequirements<TRegistrations>, TIdentifier> | TErrorRequirements
|
|
58
|
+
> => {
|
|
59
|
+
type Requirements = Exclude<RegistrationRequirements<TRegistrations>, TIdentifier> | TErrorRequirements;
|
|
52
60
|
const names = options.jobs.map(registrationName);
|
|
53
61
|
const { clientFactory, clientCacheKey, jobs, onError, stop, ...constructorOptions } = options;
|
|
54
62
|
|
|
55
63
|
const acquire = Effect.acquireRelease(
|
|
56
64
|
Effect.gen(function* () {
|
|
57
|
-
const
|
|
65
|
+
const dependencies = yield* Effect.context<Requirements>();
|
|
58
66
|
const acquired = yield* acquireClient({
|
|
59
67
|
clientCacheKey,
|
|
60
68
|
clientFactory,
|
|
61
69
|
constructor: constructorOptions,
|
|
62
70
|
});
|
|
63
|
-
const
|
|
71
|
+
const service = makeService(acquired.client, names, constructorOptions.schema);
|
|
72
|
+
const context = Context.add(dependencies, Service, service);
|
|
73
|
+
const reportError: (error: Error) => Effect.Effect<unknown, never, Requirements | TIdentifier> = onError ?? logClientError;
|
|
64
74
|
const errorListener = (error: Error) => {
|
|
65
75
|
void Effect.runPromise(Effect.exit(Effect.provide(reportError(error), context)));
|
|
66
76
|
};
|
|
67
77
|
acquired.client.on("error", errorListener);
|
|
68
78
|
const registration = registerJobs(acquired.client, jobs, context, acquired.reused);
|
|
69
79
|
return yield* registration.pipe(
|
|
70
|
-
Effect.as({ acquired,
|
|
80
|
+
Effect.as({ acquired, errorListener, service }),
|
|
71
81
|
Effect.onError(() => {
|
|
72
82
|
acquired.client.off("error", errorListener);
|
|
73
83
|
return releaseClient(acquired, stop);
|
|
@@ -82,7 +92,7 @@ export const makePgBoss = <const TIdentifier extends string>(identifier: TIdenti
|
|
|
82
92
|
|
|
83
93
|
return Layer.effect(
|
|
84
94
|
Service,
|
|
85
|
-
Effect.map(acquire, ({
|
|
95
|
+
Effect.map(acquire, ({ service }) => service),
|
|
86
96
|
);
|
|
87
97
|
};
|
|
88
98
|
|