@owlmeans/queue 0.1.18-rc.7 → 0.1.18-rc.8
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/README.md +111 -3
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/queue/SKILL.md +125 -12
- package/build/bridge.d.ts +27 -0
- package/build/bridge.d.ts.map +1 -0
- package/build/bridge.js +93 -0
- package/build/bridge.js.map +1 -0
- package/build/config.d.ts +22 -0
- package/build/config.d.ts.map +1 -0
- package/build/config.js +41 -0
- package/build/config.js.map +1 -0
- package/build/consts.d.ts +33 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +35 -0
- package/build/consts.js.map +1 -0
- package/build/errors.d.ts +59 -0
- package/build/errors.d.ts.map +1 -0
- package/build/errors.js +90 -0
- package/build/errors.js.map +1 -0
- package/build/index.d.ts +7 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +7 -1
- package/build/index.js.map +1 -1
- package/build/transport.d.ts +15 -0
- package/build/transport.d.ts.map +1 -0
- package/build/transport.js +62 -0
- package/build/transport.js.map +1 -0
- package/build/types.d.ts +240 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/build/worker.d.ts +27 -0
- package/build/worker.d.ts.map +1 -0
- package/build/worker.js +58 -0
- package/build/worker.js.map +1 -0
- package/package.json +10 -1
- package/src/bridge.ts +109 -0
- package/src/config.ts +56 -0
- package/src/consts.ts +39 -0
- package/src/errors.ts +106 -0
- package/src/index.ts +7 -0
- package/src/transport.ts +82 -0
- package/src/types.ts +251 -0
- package/src/worker.ts +72 -0
- package/tests/bridge.spec.ts +64 -0
- package/tests/config.spec.ts +66 -0
- package/tsconfig.json +1 -0
package/build/errors.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { ResilientError } from '@owlmeans/error';
|
|
2
|
+
export class QueueError extends ResilientError {
|
|
3
|
+
static typeName = 'QueueError';
|
|
4
|
+
constructor(message = 'error') {
|
|
5
|
+
super(QueueError.typeName, `queue:${message}`);
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* The caller stopped waiting. The job is untouched — it may still be queued, running, or already
|
|
10
|
+
* finished; only the wait ended. Read the job back to learn which.
|
|
11
|
+
*/
|
|
12
|
+
export class QueueTimeout extends QueueError {
|
|
13
|
+
static typeName = `${QueueError.typeName}Timeout`;
|
|
14
|
+
constructor(msg) {
|
|
15
|
+
super(`timeout:${msg}`);
|
|
16
|
+
this.type = QueueTimeout.typeName;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** No job with that id in this queue — it never existed, or it was removed after completing. */
|
|
20
|
+
export class UnknownJob extends QueueError {
|
|
21
|
+
static typeName = `${QueueError.typeName}UnknownJob`;
|
|
22
|
+
constructor(msg) {
|
|
23
|
+
super(`unknown-job:${msg}`);
|
|
24
|
+
this.type = UnknownJob.typeName;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* A job name the queue does not declare. Declaring every job a queue accepts is what keeps an
|
|
29
|
+
* enqueue from succeeding into a queue where nothing can ever process it.
|
|
30
|
+
*/
|
|
31
|
+
export class UnknownJobName extends QueueError {
|
|
32
|
+
static typeName = `${QueueError.typeName}UnknownJobName`;
|
|
33
|
+
constructor(msg) {
|
|
34
|
+
super(`unknown-job-name:${msg}`);
|
|
35
|
+
this.type = UnknownJobName.typeName;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/** A queue asked for that no declaration names. */
|
|
39
|
+
export class UnknownQueue extends QueueError {
|
|
40
|
+
static typeName = `${QueueError.typeName}UnknownQueue`;
|
|
41
|
+
constructor(msg) {
|
|
42
|
+
super(`unknown-queue:${msg}`);
|
|
43
|
+
this.type = UnknownQueue.typeName;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* This process was asked to consume a queue it does not listen to. Which queues a process
|
|
48
|
+
* consumes is configuration (`cfg.queue.listen`), so this is a deployment question, not a code
|
|
49
|
+
* one — the same binary is a producer or a worker depending on it.
|
|
50
|
+
*/
|
|
51
|
+
export class QueueNotListening extends QueueError {
|
|
52
|
+
static typeName = `${QueueError.typeName}NotListening`;
|
|
53
|
+
constructor(msg) {
|
|
54
|
+
super(`not-listening:${msg}`);
|
|
55
|
+
this.type = QueueNotListening.typeName;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* The job named an entrypoint this process does not serve. Unlike a missing queue this is not a
|
|
60
|
+
* declaration problem: the alias exists, but nothing here elevated it, so the job was taken by a
|
|
61
|
+
* worker that cannot run it.
|
|
62
|
+
*/
|
|
63
|
+
export class JobNotServed extends QueueError {
|
|
64
|
+
static typeName = `${QueueError.typeName}JobNotServed`;
|
|
65
|
+
constructor(msg) {
|
|
66
|
+
super(`job-not-served:${msg}`);
|
|
67
|
+
this.type = JobNotServed.typeName;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* The signed envelope is older than the configured lifetime. Freshness is judged from when the
|
|
72
|
+
* producer enqueued the job, not from when a worker picked it up, so a long backlog does not
|
|
73
|
+
* invalidate work that was legitimate when it was submitted.
|
|
74
|
+
*/
|
|
75
|
+
export class EnvelopeExpired extends QueueError {
|
|
76
|
+
static typeName = `${QueueError.typeName}EnvelopeExpired`;
|
|
77
|
+
constructor(msg) {
|
|
78
|
+
super(`envelope-expired:${msg}`);
|
|
79
|
+
this.type = EnvelopeExpired.typeName;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
ResilientError.registerErrorClass(QueueError);
|
|
83
|
+
ResilientError.registerErrorClass(QueueTimeout);
|
|
84
|
+
ResilientError.registerErrorClass(UnknownJob);
|
|
85
|
+
ResilientError.registerErrorClass(UnknownJobName);
|
|
86
|
+
ResilientError.registerErrorClass(UnknownQueue);
|
|
87
|
+
ResilientError.registerErrorClass(QueueNotListening);
|
|
88
|
+
ResilientError.registerErrorClass(JobNotServed);
|
|
89
|
+
ResilientError.registerErrorClass(EnvelopeExpired);
|
|
90
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
package/build/index.d.ts
CHANGED
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
|
package/build/index.js
CHANGED
|
@@ -1,2 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
export * from './types.js';
|
|
2
|
+
export * from './consts.js';
|
|
3
|
+
export * from './errors.js';
|
|
4
|
+
export * from './config.js';
|
|
5
|
+
export * from './transport.js';
|
|
6
|
+
export * from './bridge.js';
|
|
7
|
+
export * from './worker.js';
|
|
2
8
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAA;AAC1B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { EntrypointTransport } from '@owlmeans/entrypoint';
|
|
2
|
+
import type { Config, Context } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The producing half of the bridge: it turns a call on a QUEUE entrypoint into a job, and the
|
|
5
|
+
* job's outcome back into a reply.
|
|
6
|
+
*
|
|
7
|
+
* Registering it is the whole opt-in. `apiHandler` looks up a transport by the route's protocol,
|
|
8
|
+
* so a consumer writes `ep.call(...)` against a declaration it shares with every other kind of
|
|
9
|
+
* entrypoint and never learns which one carried it. That is also why the envelope is built from
|
|
10
|
+
* the request rather than from the entrypoint: the far side rebuilds a request, and anything only
|
|
11
|
+
* HTTP understands would not survive the trip anyway.
|
|
12
|
+
*/
|
|
13
|
+
export declare const makeQueueTransport: (alias?: string) => EntrypointTransport;
|
|
14
|
+
export declare const appendQueueTransport: <C extends Config, T extends Context<C> = Context<C>>(context: T, alias?: string) => T;
|
|
15
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAqC,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAKlG,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAsC,MAAM,YAAY,CAAA;AAIrF;;;;;;;;;GASG;AACH,eAAO,MAAM,kBAAkB,GAAI,QAAO,MAA6C,wBAmDtF,CAAA;AAED,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,EACtF,SAAS,CAAC,EAAE,QAAQ,MAAM,KACzB,CAMF,CAAA"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { assertContext, createService } from '@owlmeans/context';
|
|
2
|
+
import { EntrypointOutcome, transportAlias } from '@owlmeans/entrypoint';
|
|
3
|
+
import { RouteProtocols } from '@owlmeans/route';
|
|
4
|
+
import { ResilientError } from '@owlmeans/error';
|
|
5
|
+
import { DEFAULT_JOB_TIMEOUT } from './consts.js';
|
|
6
|
+
import { UnknownQueue } from './errors.js';
|
|
7
|
+
/**
|
|
8
|
+
* The producing half of the bridge: it turns a call on a QUEUE entrypoint into a job, and the
|
|
9
|
+
* job's outcome back into a reply.
|
|
10
|
+
*
|
|
11
|
+
* Registering it is the whole opt-in. `apiHandler` looks up a transport by the route's protocol,
|
|
12
|
+
* so a consumer writes `ep.call(...)` against a declaration it shares with every other kind of
|
|
13
|
+
* entrypoint and never learns which one carried it. That is also why the envelope is built from
|
|
14
|
+
* the request rather than from the entrypoint: the far side rebuilds a request, and anything only
|
|
15
|
+
* HTTP understands would not survive the trip anyway.
|
|
16
|
+
*/
|
|
17
|
+
export const makeQueueTransport = (alias = transportAlias(RouteProtocols.QUEUE)) => {
|
|
18
|
+
const location = `queue-transport:${alias}`;
|
|
19
|
+
const service = createService(alias, {
|
|
20
|
+
protocol: RouteProtocols.QUEUE,
|
|
21
|
+
handle: (async (req, res) => {
|
|
22
|
+
const context = assertContext(service.ctx, location);
|
|
23
|
+
const entrypoint = context.entrypoint(req.alias);
|
|
24
|
+
const route = entrypoint.route.route;
|
|
25
|
+
if (route.queue == null) {
|
|
26
|
+
throw new UnknownQueue(`${req.alias}: route declares no queue`);
|
|
27
|
+
}
|
|
28
|
+
const jobs = context.jobs(route.queue);
|
|
29
|
+
const envelope = {
|
|
30
|
+
alias: req.alias,
|
|
31
|
+
params: req.params,
|
|
32
|
+
body: req.body,
|
|
33
|
+
query: req.query,
|
|
34
|
+
headers: req.headers,
|
|
35
|
+
enqueuedAt: new Date().toISOString(),
|
|
36
|
+
};
|
|
37
|
+
const job = await jobs.create({ queue: route.queue, name: req.alias, data: envelope });
|
|
38
|
+
// `reply: false` says the caller is not waiting for the work, only for the promise that it
|
|
39
|
+
// was taken. `Accepted` plus the job's identity is the whole answer — enough to watch it.
|
|
40
|
+
if (route.reply === false) {
|
|
41
|
+
res.resolve({ id: job.id, queue: route.queue }, EntrypointOutcome.Accepted);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const reply = await jobs.wait(job.id, {
|
|
45
|
+
timeout: req.timeout ?? route.timeout ?? DEFAULT_JOB_TIMEOUT
|
|
46
|
+
});
|
|
47
|
+
// A processor reports a domain failure by returning it, so it crosses the hop as the class
|
|
48
|
+
// it was thrown as rather than as a string a caller would have to parse.
|
|
49
|
+
if (reply?.error != null) {
|
|
50
|
+
throw ResilientError.ensure(reply.error);
|
|
51
|
+
}
|
|
52
|
+
res.resolve(reply?.value, reply?.outcome ?? EntrypointOutcome.Ok);
|
|
53
|
+
})
|
|
54
|
+
});
|
|
55
|
+
return service;
|
|
56
|
+
};
|
|
57
|
+
export const appendQueueTransport = (context, alias) => {
|
|
58
|
+
const service = makeQueueTransport(alias);
|
|
59
|
+
context.registerService(service);
|
|
60
|
+
return context;
|
|
61
|
+
};
|
|
62
|
+
//# sourceMappingURL=transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport.js","sourceRoot":"","sources":["../src/transport.ts"],"names":[],"mappings":"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"}
|
package/build/types.d.ts
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
import type { BasicConfig, BasicContext, InitializedService } from '@owlmeans/context';
|
|
2
|
+
import type { PubSubResource, Resource, ResourceRecord } from '@owlmeans/resource';
|
|
3
|
+
import type { AbstractRequest, AbstractResponse } from '@owlmeans/entrypoint';
|
|
4
|
+
import type { JobEventType, JobState } from './consts.js';
|
|
5
|
+
/**
|
|
6
|
+
* Everything the monorepo knows about its queues, declared in the shared backend package so that
|
|
7
|
+
* producer and consumer read the same list. A process becomes a worker by naming queues in
|
|
8
|
+
* `listen` — nothing in a declaration decides where it runs.
|
|
9
|
+
*/
|
|
10
|
+
export interface QueueConfig {
|
|
11
|
+
queues?: QueueDeclaration[];
|
|
12
|
+
/** The queues THIS process consumes. Absent or empty means producer only. */
|
|
13
|
+
listen?: string[];
|
|
14
|
+
/** Which db service carries the queues. Defaults to the queue service's own alias. */
|
|
15
|
+
db?: string;
|
|
16
|
+
defaults?: JobOptions;
|
|
17
|
+
/**
|
|
18
|
+
* How long a signed job envelope stays acceptable, in seconds. It bounds both the freshness
|
|
19
|
+
* check and the nonce cache, so a job delayed behind a long backlog is still admitted.
|
|
20
|
+
*/
|
|
21
|
+
envelopeTtl?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface QueueDeclaration {
|
|
24
|
+
name: string;
|
|
25
|
+
/**
|
|
26
|
+
* The job names this queue accepts. A job the queue does not declare is refused at enqueue
|
|
27
|
+
* time rather than becoming a job nothing can process.
|
|
28
|
+
*/
|
|
29
|
+
jobs: string[];
|
|
30
|
+
worker?: QueueWorkerOptions;
|
|
31
|
+
defaults?: JobOptions;
|
|
32
|
+
}
|
|
33
|
+
export interface QueueWorkerOptions {
|
|
34
|
+
concurrency?: number;
|
|
35
|
+
/**
|
|
36
|
+
* How long a job may hold its lock without reporting progress. A processor that runs longer
|
|
37
|
+
* than this between `touch()` calls is treated as stalled and its job is handed to someone else.
|
|
38
|
+
*/
|
|
39
|
+
lockDuration?: number;
|
|
40
|
+
stalledInterval?: number;
|
|
41
|
+
maxStalledCount?: number;
|
|
42
|
+
}
|
|
43
|
+
export interface JobOptions {
|
|
44
|
+
attempts?: number;
|
|
45
|
+
backoff?: {
|
|
46
|
+
type: 'fixed' | 'exponential';
|
|
47
|
+
delay: number;
|
|
48
|
+
};
|
|
49
|
+
delay?: number;
|
|
50
|
+
priority?: number;
|
|
51
|
+
/**
|
|
52
|
+
* A caller-chosen identity for the job. Enqueueing the same id twice while the first is still
|
|
53
|
+
* around is a no-op, which is what makes an admission step safe to retry.
|
|
54
|
+
*/
|
|
55
|
+
id?: string;
|
|
56
|
+
removeOnComplete?: boolean | number;
|
|
57
|
+
removeOnFail?: boolean | number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* A job as a RECORD — the same shape the resource contract reads and writes, so inspecting a
|
|
61
|
+
* queue is `list`/`get`/`count` rather than a broker-specific API.
|
|
62
|
+
*/
|
|
63
|
+
export interface JobRecord<D = unknown, R = unknown> extends ResourceRecord {
|
|
64
|
+
queue: string;
|
|
65
|
+
name: string;
|
|
66
|
+
data: D;
|
|
67
|
+
state?: JobState;
|
|
68
|
+
result?: R;
|
|
69
|
+
error?: string;
|
|
70
|
+
attempts?: number;
|
|
71
|
+
progress?: unknown;
|
|
72
|
+
parentId?: string;
|
|
73
|
+
createdAt?: string;
|
|
74
|
+
startedAt?: string;
|
|
75
|
+
finishedAt?: string;
|
|
76
|
+
opts?: JobOptions;
|
|
77
|
+
}
|
|
78
|
+
export interface JobEvent<R = unknown> {
|
|
79
|
+
type: JobEventType;
|
|
80
|
+
id: string;
|
|
81
|
+
queue: string;
|
|
82
|
+
/**
|
|
83
|
+
* Empty when the job is already gone. A broker's completion event carries only an id, so the
|
|
84
|
+
* name is read back from the job — and a queue that removes jobs on completion has nothing left
|
|
85
|
+
* to read. Subscribe for the id and the outcome; do not branch on the name.
|
|
86
|
+
*/
|
|
87
|
+
name: string;
|
|
88
|
+
result?: R;
|
|
89
|
+
error?: string;
|
|
90
|
+
progress?: unknown;
|
|
91
|
+
}
|
|
92
|
+
/** A node of a job graph: children run first, siblings run together. */
|
|
93
|
+
export interface FlowSpec<D = unknown> {
|
|
94
|
+
name: string;
|
|
95
|
+
queue?: string;
|
|
96
|
+
data: D;
|
|
97
|
+
opts?: JobOptions;
|
|
98
|
+
children?: FlowSpec[];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* One queue, addressed as a resource.
|
|
102
|
+
*
|
|
103
|
+
* `create` enqueues, `get`/`load`/`list`/`count` inspect, `delete`/`take` cancel. What the base
|
|
104
|
+
* contract cannot express — waiting for an answer, submitting a graph, releasing the connection —
|
|
105
|
+
* is added here.
|
|
106
|
+
*
|
|
107
|
+
* Three places where the base `Resource` contract reads differently against a broker, because a
|
|
108
|
+
* job is not a row:
|
|
109
|
+
* - `create` does NOT throw `RecordExists` for a repeated `opts.id`. Re-enqueueing an id that is
|
|
110
|
+
* still around returns the existing job, which is the idempotency that makes an admission step
|
|
111
|
+
* safe to retry.
|
|
112
|
+
* - `WriteOptions.ttl` is refused. A job's lifetime is `removeOnComplete`/`removeOnFail` —
|
|
113
|
+
* explicit deletion once it has finished, not expiry while it waits.
|
|
114
|
+
* - `update`/`save` reach only the job's `data`. Everything else about a job belongs to the
|
|
115
|
+
* broker, and an implementation refuses rather than pretending to write it.
|
|
116
|
+
*/
|
|
117
|
+
export interface QueueResource<D = unknown, R = unknown> extends Resource<JobRecord<D, R>>, PubSubResource<JobEvent<R>> {
|
|
118
|
+
queue: string;
|
|
119
|
+
/**
|
|
120
|
+
* Release the connections this queue holds.
|
|
121
|
+
*
|
|
122
|
+
* Watching for completions means a blocking connection, and a process that never releases it
|
|
123
|
+
* does not exit. Nothing else in the resource contract can express that, so it lives here and a
|
|
124
|
+
* producer calls it on shutdown.
|
|
125
|
+
*/
|
|
126
|
+
close: () => Promise<void>;
|
|
127
|
+
/**
|
|
128
|
+
* Wait for a job's return value.
|
|
129
|
+
* @throws {QueueTimeout} when the wait elapses — the job itself is left alone.
|
|
130
|
+
* @throws the processor's own error, rebuilt as its original class.
|
|
131
|
+
*/
|
|
132
|
+
wait: (id: string, opts?: {
|
|
133
|
+
timeout?: number;
|
|
134
|
+
}) => Promise<R>;
|
|
135
|
+
/** Submit a graph and get its root back. Children complete before their parent starts. */
|
|
136
|
+
flow: (root: FlowSpec<D>) => Promise<JobRecord<D, R>>;
|
|
137
|
+
counts: () => Promise<Record<JobState, number>>;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* What a processor is handed. `data` is the job's payload; everything else is the means to stay
|
|
141
|
+
* alive and to read what the children reported.
|
|
142
|
+
*/
|
|
143
|
+
export interface JobContext<D = unknown> {
|
|
144
|
+
id: string;
|
|
145
|
+
name: string;
|
|
146
|
+
queue: string;
|
|
147
|
+
attempt: number;
|
|
148
|
+
data: D;
|
|
149
|
+
signal: AbortSignal;
|
|
150
|
+
/**
|
|
151
|
+
* Renew the lock. A processor that runs a long loop MUST call it as it goes: the broker judges
|
|
152
|
+
* liveness by the lock, so silence for longer than `lockDuration` is indistinguishable from a
|
|
153
|
+
* dead worker, and the job is re-run somewhere else while this one is still working on it.
|
|
154
|
+
*/
|
|
155
|
+
touch: () => Promise<void>;
|
|
156
|
+
progress: (value: unknown) => Promise<void>;
|
|
157
|
+
/**
|
|
158
|
+
* What the children returned, keyed by child job id.
|
|
159
|
+
*
|
|
160
|
+
* A broker keys them by its own fully qualified job key; an implementation re-keys to the bare
|
|
161
|
+
* id, so two children of one parent that live in DIFFERENT queues and happen to share an id
|
|
162
|
+
* collide here. Give the children of a cross-queue flow distinct ids.
|
|
163
|
+
*/
|
|
164
|
+
children: <T = unknown>() => Promise<Record<string, T>>;
|
|
165
|
+
failedChildren: () => Promise<Record<string, string>>;
|
|
166
|
+
}
|
|
167
|
+
export interface JobProcessor<D = unknown, R = unknown> {
|
|
168
|
+
(job: JobContext<D>): Promise<R>;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* The consuming half. It is registered only in processes that consume: `start()` binds the
|
|
172
|
+
* queues named in `cfg.listen` and dispatches by job name, to a processor registered here or to
|
|
173
|
+
* an entrypoint served in this process.
|
|
174
|
+
*/
|
|
175
|
+
export interface QueueWorkerService extends InitializedService {
|
|
176
|
+
process: <D, R>(queue: string, name: string, processor: JobProcessor<D, R>) => void;
|
|
177
|
+
start: () => Promise<void>;
|
|
178
|
+
stop: () => Promise<void>;
|
|
179
|
+
/** The queues this process actually consumes. Empty in a producer. */
|
|
180
|
+
listening: () => string[];
|
|
181
|
+
/**
|
|
182
|
+
* Bind the application's reactions to job lifecycle events. Registering more than once merges,
|
|
183
|
+
* so an application composed of several parts can each contribute what it cares about.
|
|
184
|
+
*
|
|
185
|
+
* It belongs on the contract rather than on a driver because `onJobDead` is where an
|
|
186
|
+
* application COMPENSATES — releases the lock the admission step took, marks the record failed —
|
|
187
|
+
* and that behaviour must not have to be rewritten when the broker changes.
|
|
188
|
+
*/
|
|
189
|
+
hooks: (hooks: QueueHooks) => void;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* The envelope a queued entrypoint call travels in. It is the request, minus everything that only
|
|
193
|
+
* means something to HTTP — the transport rebuilds a request from it on the far side.
|
|
194
|
+
*/
|
|
195
|
+
export interface JobEnvelope {
|
|
196
|
+
alias: string;
|
|
197
|
+
params?: Record<string, unknown>;
|
|
198
|
+
body?: unknown;
|
|
199
|
+
query?: Record<string, unknown>;
|
|
200
|
+
headers?: Record<string, string | undefined>;
|
|
201
|
+
/** When the producer enqueued it — freshness is judged here, not at processing time. */
|
|
202
|
+
enqueuedAt?: string;
|
|
203
|
+
}
|
|
204
|
+
/** What a processor returns for an entrypoint job, so the caller can rebuild the reply. */
|
|
205
|
+
export interface JobReply<T = unknown> {
|
|
206
|
+
value?: T;
|
|
207
|
+
outcome?: string;
|
|
208
|
+
error?: unknown;
|
|
209
|
+
}
|
|
210
|
+
export interface QueueHooks {
|
|
211
|
+
wrapHandler?: (job: JobContext, next: () => Promise<unknown>) => Promise<unknown>;
|
|
212
|
+
onJobResult?: (event: JobEvent) => void | Promise<void>;
|
|
213
|
+
onJobStalled?: (meta: {
|
|
214
|
+
id: string;
|
|
215
|
+
queue: string;
|
|
216
|
+
name: string;
|
|
217
|
+
}, phase: 'stalled' | 'failed') => void | Promise<void>;
|
|
218
|
+
/**
|
|
219
|
+
* The job is finished for good — retries exhausted, or it was dropped. This is where an
|
|
220
|
+
* application compensates: release the lock the admission step took, mark the record failed.
|
|
221
|
+
*/
|
|
222
|
+
onJobDead?: (job: JobRecord, reason: string) => void | Promise<void>;
|
|
223
|
+
}
|
|
224
|
+
export interface QueueDriver {
|
|
225
|
+
resource: <D, R>(queue: string) => QueueResource<D, R>;
|
|
226
|
+
worker: () => QueueWorkerService;
|
|
227
|
+
}
|
|
228
|
+
export interface Config extends BasicConfig {
|
|
229
|
+
queue?: QueueConfig;
|
|
230
|
+
}
|
|
231
|
+
export interface Context<C extends Config = Config> extends BasicContext<C> {
|
|
232
|
+
}
|
|
233
|
+
export interface QueueAppend {
|
|
234
|
+
jobs: <D = unknown, R = unknown>(queue?: string) => QueueResource<D, R>;
|
|
235
|
+
}
|
|
236
|
+
export interface QueueTransportRequest extends AbstractRequest {
|
|
237
|
+
}
|
|
238
|
+
export interface QueueTransportResponse extends AbstractResponse<unknown> {
|
|
239
|
+
}
|
|
240
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/build/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Middleware } from '@owlmeans/context';
|
|
2
|
+
import type { CommonEntrypoint } from '@owlmeans/entrypoint';
|
|
3
|
+
import type { Config, Context, JobEnvelope, JobProcessor } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* Every queued entrypoint this process both SERVES and LISTENS to, grouped by queue.
|
|
6
|
+
*
|
|
7
|
+
* Both halves are required and they answer different questions. Serving is about code — the alias
|
|
8
|
+
* was elevated here, so a handler exists. Listening is about deployment — this process was
|
|
9
|
+
* configured to consume that queue. A worker that bound queues by what it can serve would make
|
|
10
|
+
* every deployment of the same binary a worker for everything it happens to import.
|
|
11
|
+
*/
|
|
12
|
+
export declare const servedJobs: <C extends Config, T extends Context<C>>(context: T) => Map<string, CommonEntrypoint[]>;
|
|
13
|
+
/**
|
|
14
|
+
* A processor that runs a queued entrypoint call. The driver dispatches by job name, and an
|
|
15
|
+
* entrypoint job's name IS its alias — which is what lets one worker carry both entrypoint jobs
|
|
16
|
+
* and the internal steps an application registers with `process()`.
|
|
17
|
+
*/
|
|
18
|
+
export declare const entrypointProcessor: <C extends Config, T extends Context<C>>(context: T) => JobProcessor<JobEnvelope, unknown>;
|
|
19
|
+
/**
|
|
20
|
+
* Start the worker once the context is ready.
|
|
21
|
+
*
|
|
22
|
+
* Ready stage, not Loading: processors are registered while the application wires itself up, and
|
|
23
|
+
* binding the queues before that finished would take jobs this process cannot yet run. A process
|
|
24
|
+
* that listens to nothing registers no worker at all, so a producer pays nothing for this.
|
|
25
|
+
*/
|
|
26
|
+
export declare const queueWorkerMiddleware: (alias?: string) => Middleware;
|
|
27
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAEnD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAE5D,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAc,WAAW,EAAE,YAAY,EAAsB,MAAM,YAAY,CAAA;AAI5G;;;;;;;GAOG;AACH,eAAO,MAAM,UAAU,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,EAC/D,SAAS,CAAC,KACT,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE,CAoBhC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,GAAI,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,OAAO,CAAC,CAAC,CAAC,EACxE,SAAS,CAAC,KACT,YAAY,CAAC,WAAW,EAAE,OAAO,CACmC,CAAA;AAEvE;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAAI,QAAO,MAAsB,KAAG,UAcpE,CAAA"}
|
package/build/worker.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { MiddlewareStage, MiddlewareType, AppType } from '@owlmeans/context';
|
|
2
|
+
import { RouteProtocols } from '@owlmeans/route';
|
|
3
|
+
import { handleJob } from './bridge.js';
|
|
4
|
+
import { DEFAULT_ALIAS } from './consts.js';
|
|
5
|
+
/**
|
|
6
|
+
* Every queued entrypoint this process both SERVES and LISTENS to, grouped by queue.
|
|
7
|
+
*
|
|
8
|
+
* Both halves are required and they answer different questions. Serving is about code — the alias
|
|
9
|
+
* was elevated here, so a handler exists. Listening is about deployment — this process was
|
|
10
|
+
* configured to consume that queue. A worker that bound queues by what it can serve would make
|
|
11
|
+
* every deployment of the same binary a worker for everything it happens to import.
|
|
12
|
+
*/
|
|
13
|
+
export const servedJobs = (context) => {
|
|
14
|
+
const listen = context.cfg.queue?.listen ?? [];
|
|
15
|
+
const served = new Map();
|
|
16
|
+
context.entrypoints().forEach(entrypoint => {
|
|
17
|
+
const route = entrypoint.route.route;
|
|
18
|
+
if (route.type !== AppType.Backend || route.protocol !== RouteProtocols.QUEUE) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (route.service != null && route.service !== context.cfg.service) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (entrypoint.handle == null || route.queue == null || !listen.includes(route.queue)) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
served.set(route.queue, [...(served.get(route.queue) ?? []), entrypoint]);
|
|
28
|
+
});
|
|
29
|
+
return served;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* A processor that runs a queued entrypoint call. The driver dispatches by job name, and an
|
|
33
|
+
* entrypoint job's name IS its alias — which is what lets one worker carry both entrypoint jobs
|
|
34
|
+
* and the internal steps an application registers with `process()`.
|
|
35
|
+
*/
|
|
36
|
+
export const entrypointProcessor = (context) => async (job) => await handleJob(context, job);
|
|
37
|
+
/**
|
|
38
|
+
* Start the worker once the context is ready.
|
|
39
|
+
*
|
|
40
|
+
* Ready stage, not Loading: processors are registered while the application wires itself up, and
|
|
41
|
+
* binding the queues before that finished would take jobs this process cannot yet run. A process
|
|
42
|
+
* that listens to nothing registers no worker at all, so a producer pays nothing for this.
|
|
43
|
+
*/
|
|
44
|
+
export const queueWorkerMiddleware = (alias = DEFAULT_ALIAS) => ({
|
|
45
|
+
type: MiddlewareType.Context,
|
|
46
|
+
stage: MiddlewareStage.Ready,
|
|
47
|
+
apply: async (context) => {
|
|
48
|
+
const ctx = context;
|
|
49
|
+
if ((ctx.cfg.queue?.listen ?? []).length === 0) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!ctx.hasService(alias)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
await ctx.service(alias).start();
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAA;AAE5E,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAE3C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,UAAU,GAAG,CACxB,OAAU,EACuB,EAAE;IACnC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,CAAA;IAC9C,MAAM,MAAM,GAAG,IAAI,GAAG,EAA8B,CAAA;IAEpD,OAAO,CAAC,WAAW,EAAoB,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE;QAC3D,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,CAAA;QACpC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,OAAO,IAAI,KAAK,CAAC,QAAQ,KAAK,cAAc,CAAC,KAAK,EAAE,CAAC;YAC9E,OAAM;QACR,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,IAAI,IAAI,IAAI,KAAK,CAAC,OAAO,KAAK,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;YACnE,OAAM;QACR,CAAC;QACD,IAAI,UAAU,CAAC,MAAM,IAAI,IAAI,IAAI,KAAK,CAAC,KAAK,IAAI,IAAI,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YACtF,OAAM;QACR,CAAC;QAED,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC,CAAA;IAC3E,CAAC,CAAC,CAAA;IAEF,OAAO,MAAM,CAAA;AACf,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CACjC,OAAU,EAC0B,EAAE,CACtC,KAAK,EAAE,GAA4B,EAAE,EAAE,CAAC,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;AAEvE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,QAAgB,aAAa,EAAc,EAAE,CAAC,CAAC;IACnF,IAAI,EAAE,cAAc,CAAC,OAAO;IAC5B,KAAK,EAAE,eAAe,CAAC,KAAK;IAC5B,KAAK,EAAE,KAAK,EAAC,OAAO,EAAC,EAAE;QACrB,MAAM,GAAG,GAAG,OAAqC,CAAA;QACjD,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC/C,OAAM;QACR,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAM;QACR,CAAC;QAED,MAAM,GAAG,CAAC,OAAO,CAAqB,KAAK,CAAC,CAAC,KAAK,EAAE,CAAA;IACtD,CAAC;CACF,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/queue",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.8",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -28,5 +28,14 @@
|
|
|
28
28
|
},
|
|
29
29
|
"publishConfig": {
|
|
30
30
|
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@owlmeans/auth": "^0.1.18-rc.8",
|
|
34
|
+
"@owlmeans/auth-common": "^0.1.18-rc.12",
|
|
35
|
+
"@owlmeans/context": "^0.1.18-rc.7",
|
|
36
|
+
"@owlmeans/entrypoint": "^0.1.18-rc.10",
|
|
37
|
+
"@owlmeans/error": "^0.1.18-rc.7",
|
|
38
|
+
"@owlmeans/resource": "^0.1.18-rc.8",
|
|
39
|
+
"@owlmeans/route": "^0.1.18-rc.8"
|
|
31
40
|
}
|
|
32
41
|
}
|