@effect/workflow 0.13.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/DurableQueue/package.json +6 -0
- package/dist/cjs/DurableQueue.js +186 -0
- package/dist/cjs/DurableQueue.js.map +1 -0
- package/dist/cjs/index.js +3 -1
- package/dist/dts/DurableQueue.d.ts +116 -0
- package/dist/dts/DurableQueue.d.ts.map +1 -0
- package/dist/dts/index.d.ts +4 -0
- package/dist/dts/index.d.ts.map +1 -1
- package/dist/esm/DurableQueue.js +176 -0
- package/dist/esm/DurableQueue.js.map +1 -0
- package/dist/esm/index.js +4 -0
- package/dist/esm/index.js.map +1 -1
- package/package.json +12 -4
- package/src/DurableQueue.ts +332 -0
- package/src/index.ts +5 -0
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.worker = exports.process = exports.makeWorker = exports.make = exports.TypeId = void 0;
|
|
7
|
+
var PersistedQueue = _interopRequireWildcard(require("@effect/experimental/PersistedQueue"));
|
|
8
|
+
var Context = _interopRequireWildcard(require("effect/Context"));
|
|
9
|
+
var Effect = _interopRequireWildcard(require("effect/Effect"));
|
|
10
|
+
var Layer = _interopRequireWildcard(require("effect/Layer"));
|
|
11
|
+
var Schedule = _interopRequireWildcard(require("effect/Schedule"));
|
|
12
|
+
var Schema = _interopRequireWildcard(require("effect/Schema"));
|
|
13
|
+
var Activity = _interopRequireWildcard(require("./Activity.js"));
|
|
14
|
+
var DurableDeferred = _interopRequireWildcard(require("./DurableDeferred.js"));
|
|
15
|
+
var _crypto = require("./internal/crypto.js");
|
|
16
|
+
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
17
|
+
/**
|
|
18
|
+
* @since 1.0.0
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @since 1.0.0
|
|
23
|
+
* @category Type IDs
|
|
24
|
+
*/
|
|
25
|
+
const TypeId = exports.TypeId = "~@effect/workflow/DurableQueue";
|
|
26
|
+
/**
|
|
27
|
+
* A `DurableQueue` wraps a `PersistedQueue`, providing a way to wait for items
|
|
28
|
+
* to finish processing using a `DurableDeferred`.
|
|
29
|
+
*
|
|
30
|
+
* ```ts
|
|
31
|
+
* import { DurableQueue, Workflow } from "@effect/workflow"
|
|
32
|
+
* import { Effect, Schema } from "effect"
|
|
33
|
+
*
|
|
34
|
+
* // Define a DurableQueue that can be used to derive workers and offer items for
|
|
35
|
+
* // processing.
|
|
36
|
+
* const ApiQueue = DurableQueue.make({
|
|
37
|
+
* name: "ApiQueue",
|
|
38
|
+
* payload: {
|
|
39
|
+
* id: Schema.String
|
|
40
|
+
* },
|
|
41
|
+
* success: Schema.Void,
|
|
42
|
+
* error: Schema.Never,
|
|
43
|
+
* idempotencyKey(payload) {
|
|
44
|
+
* return payload.id
|
|
45
|
+
* }
|
|
46
|
+
* })
|
|
47
|
+
*
|
|
48
|
+
* const MyWorkflow = Workflow.make({
|
|
49
|
+
* name: "MyWorkflow",
|
|
50
|
+
* payload: {
|
|
51
|
+
* id: Schema.String
|
|
52
|
+
* },
|
|
53
|
+
* idempotencyKey: ({ id }) => id
|
|
54
|
+
* })
|
|
55
|
+
*
|
|
56
|
+
* const MyWorkflowLayer = MyWorkflow.toLayer(
|
|
57
|
+
* Effect.fn(function*() {
|
|
58
|
+
* // Add an item to the DurableQueue defined above.
|
|
59
|
+
* //
|
|
60
|
+
* // When the worker has finished processing the item, the workflow will
|
|
61
|
+
* // resume.
|
|
62
|
+
* //
|
|
63
|
+
* yield* DurableQueue.process(ApiQueue, { id: "api-call-1" })
|
|
64
|
+
*
|
|
65
|
+
* yield* Effect.log("Workflow succeeded!")
|
|
66
|
+
* })
|
|
67
|
+
* )
|
|
68
|
+
*
|
|
69
|
+
* // Define a worker layer that can process items from the DurableQueue.
|
|
70
|
+
* const ApiWorker = DurableQueue.worker(
|
|
71
|
+
* ApiQueue,
|
|
72
|
+
* Effect.fn(function*({ id }) {
|
|
73
|
+
* yield* Effect.log(`Worker processing API call with id: ${id}`)
|
|
74
|
+
* }),
|
|
75
|
+
* { concurrency: 5 } // Process up to 5 items concurrently
|
|
76
|
+
* )
|
|
77
|
+
* ```
|
|
78
|
+
*
|
|
79
|
+
* @since 1.0.0
|
|
80
|
+
* @category Constructors
|
|
81
|
+
*/
|
|
82
|
+
const make = options => ({
|
|
83
|
+
[TypeId]: TypeId,
|
|
84
|
+
name: options.name,
|
|
85
|
+
payloadSchema: Schema.isSchema(options.payload) ? options.payload : Schema.Struct(options.payload),
|
|
86
|
+
idempotencyKey: options.idempotencyKey,
|
|
87
|
+
deferred: DurableDeferred.make(`DurableQueue/${options.name}`, {
|
|
88
|
+
success: options.success,
|
|
89
|
+
error: options.error
|
|
90
|
+
})
|
|
91
|
+
});
|
|
92
|
+
exports.make = make;
|
|
93
|
+
const queueSchemas = /*#__PURE__*/new WeakMap();
|
|
94
|
+
const getQueueSchema = payload => {
|
|
95
|
+
let schema = queueSchemas.get(payload);
|
|
96
|
+
if (!schema) {
|
|
97
|
+
schema = Schema.Struct({
|
|
98
|
+
token: Schema.String,
|
|
99
|
+
traceId: Schema.String,
|
|
100
|
+
spanId: Schema.String,
|
|
101
|
+
sampled: Schema.Boolean,
|
|
102
|
+
payload
|
|
103
|
+
});
|
|
104
|
+
queueSchemas.set(payload, schema);
|
|
105
|
+
}
|
|
106
|
+
return schema;
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* @since 1.0.0
|
|
110
|
+
* @category Processing
|
|
111
|
+
*/
|
|
112
|
+
const process = exports.process = /*#__PURE__*/Effect.fnUntraced(function* (self, payload, options) {
|
|
113
|
+
const key = yield* (0, _crypto.makeHashDigest)(self.idempotencyKey(payload));
|
|
114
|
+
const deferred = DurableDeferred.make(`${self.deferred.name}/${key}`, {
|
|
115
|
+
success: self.deferred.successSchema,
|
|
116
|
+
error: self.deferred.errorSchema
|
|
117
|
+
});
|
|
118
|
+
yield* Activity.make({
|
|
119
|
+
name: `DurableQueue/${self.name}/${key}`,
|
|
120
|
+
execute: Effect.gen(function* () {
|
|
121
|
+
const span = yield* Effect.orDie(Effect.currentSpan);
|
|
122
|
+
const queue = yield* PersistedQueue.make({
|
|
123
|
+
name: `DurableQueue/${self.name}`,
|
|
124
|
+
schema: getQueueSchema(self.payloadSchema)
|
|
125
|
+
});
|
|
126
|
+
const token = yield* DurableDeferred.token(deferred);
|
|
127
|
+
yield* queue.offer({
|
|
128
|
+
token,
|
|
129
|
+
payload,
|
|
130
|
+
traceId: span.traceId,
|
|
131
|
+
spanId: span.spanId,
|
|
132
|
+
sampled: span.sampled
|
|
133
|
+
}).pipe(Effect.tapErrorCause(Effect.logWarning), Effect.catchTag("ParseError", Effect.die), Effect.retry(options?.retrySchedule ?? defaultRetrySchedule), Effect.orDie, Effect.annotateLogs({
|
|
134
|
+
package: "@effect/workflow",
|
|
135
|
+
module: "DurableQueue",
|
|
136
|
+
fiber: "process",
|
|
137
|
+
queueName: self.name
|
|
138
|
+
}));
|
|
139
|
+
})
|
|
140
|
+
});
|
|
141
|
+
return yield* DurableDeferred.await(deferred);
|
|
142
|
+
});
|
|
143
|
+
const defaultRetrySchedule = /*#__PURE__*/Schedule.exponential(500, 1.5).pipe(/*#__PURE__*/Schedule.union(/*#__PURE__*/Schedule.spaced("1 minute")));
|
|
144
|
+
/**
|
|
145
|
+
* @since 1.0.0
|
|
146
|
+
* @category Worker
|
|
147
|
+
*/
|
|
148
|
+
const makeWorker = exports.makeWorker = /*#__PURE__*/Effect.fnUntraced(function* (self, f, options) {
|
|
149
|
+
const queue = yield* PersistedQueue.make({
|
|
150
|
+
name: `DurableQueue/${self.name}`,
|
|
151
|
+
schema: getQueueSchema(self.payloadSchema)
|
|
152
|
+
});
|
|
153
|
+
const concurrency = options?.concurrency ?? 1;
|
|
154
|
+
const worker = queue.take(item_ => {
|
|
155
|
+
const item = item_;
|
|
156
|
+
return f(item.payload).pipe(Effect.exit, Effect.flatMap(exit => DurableDeferred.done(self.deferred, {
|
|
157
|
+
token: item.token,
|
|
158
|
+
exit
|
|
159
|
+
})), Effect.asVoid, Effect.withSpan(`DurableQueue/${self.name}/worker`, {
|
|
160
|
+
captureStackTrace: false,
|
|
161
|
+
parent: {
|
|
162
|
+
_tag: "ExternalSpan",
|
|
163
|
+
traceId: item.traceId,
|
|
164
|
+
spanId: item.spanId,
|
|
165
|
+
sampled: item.sampled,
|
|
166
|
+
context: Context.empty()
|
|
167
|
+
}
|
|
168
|
+
}));
|
|
169
|
+
}).pipe(Effect.catchAllCause(Effect.logWarning), Effect.forever, Effect.annotateLogs({
|
|
170
|
+
package: "@effect/workflow",
|
|
171
|
+
module: "DurableQueue",
|
|
172
|
+
fiber: "worker"
|
|
173
|
+
}));
|
|
174
|
+
yield* Effect.replicateEffect(worker, concurrency, {
|
|
175
|
+
concurrency,
|
|
176
|
+
discard: true
|
|
177
|
+
});
|
|
178
|
+
return yield* Effect.never;
|
|
179
|
+
});
|
|
180
|
+
/**
|
|
181
|
+
* @since 1.0.0
|
|
182
|
+
* @category Worker
|
|
183
|
+
*/
|
|
184
|
+
const worker = (self, f, options) => Layer.scopedDiscard(Effect.forkScoped(makeWorker(self, f, options)));
|
|
185
|
+
exports.worker = worker;
|
|
186
|
+
//# sourceMappingURL=DurableQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DurableQueue.js","names":["PersistedQueue","_interopRequireWildcard","require","Context","Effect","Layer","Schedule","Schema","Activity","DurableDeferred","_crypto","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","TypeId","exports","make","options","name","payloadSchema","isSchema","payload","Struct","idempotencyKey","deferred","success","error","queueSchemas","getQueueSchema","schema","token","String","traceId","spanId","sampled","Boolean","process","fnUntraced","self","key","makeHashDigest","successSchema","errorSchema","execute","gen","span","orDie","currentSpan","queue","offer","pipe","tapErrorCause","logWarning","catchTag","die","retry","retrySchedule","defaultRetrySchedule","annotateLogs","package","module","fiber","queueName","await","exponential","union","spaced","makeWorker","concurrency","worker","take","item_","item","exit","flatMap","done","asVoid","withSpan","captureStackTrace","parent","_tag","context","empty","catchAllCause","forever","replicateEffect","discard","never","scopedDiscard","forkScoped"],"sources":["../../src/DurableQueue.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
package/dist/cjs/index.js
CHANGED
|
@@ -3,13 +3,15 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.WorkflowProxyServer = exports.WorkflowProxy = exports.WorkflowEngine = exports.Workflow = exports.DurableRateLimiter = exports.DurableDeferred = exports.DurableClock = exports.Activity = void 0;
|
|
6
|
+
exports.WorkflowProxyServer = exports.WorkflowProxy = exports.WorkflowEngine = exports.Workflow = exports.DurableRateLimiter = exports.DurableQueue = exports.DurableDeferred = exports.DurableClock = exports.Activity = void 0;
|
|
7
7
|
var _Activity = _interopRequireWildcard(require("./Activity.js"));
|
|
8
8
|
exports.Activity = _Activity;
|
|
9
9
|
var _DurableClock = _interopRequireWildcard(require("./DurableClock.js"));
|
|
10
10
|
exports.DurableClock = _DurableClock;
|
|
11
11
|
var _DurableDeferred = _interopRequireWildcard(require("./DurableDeferred.js"));
|
|
12
12
|
exports.DurableDeferred = _DurableDeferred;
|
|
13
|
+
var _DurableQueue = _interopRequireWildcard(require("./DurableQueue.js"));
|
|
14
|
+
exports.DurableQueue = _DurableQueue;
|
|
13
15
|
var _DurableRateLimiter = _interopRequireWildcard(require("./DurableRateLimiter.js"));
|
|
14
16
|
exports.DurableRateLimiter = _DurableRateLimiter;
|
|
15
17
|
var _Workflow = _interopRequireWildcard(require("./Workflow.js"));
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import * as PersistedQueue from "@effect/experimental/PersistedQueue";
|
|
5
|
+
import * as Effect from "effect/Effect";
|
|
6
|
+
import * as Layer from "effect/Layer";
|
|
7
|
+
import * as Schedule from "effect/Schedule";
|
|
8
|
+
import * as Schema from "effect/Schema";
|
|
9
|
+
import * as DurableDeferred from "./DurableDeferred.js";
|
|
10
|
+
import type * as WorkflowEngine from "./WorkflowEngine.js";
|
|
11
|
+
/**
|
|
12
|
+
* @since 1.0.0
|
|
13
|
+
* @category Type IDs
|
|
14
|
+
*/
|
|
15
|
+
export type TypeId = "~@effect/workflow/DurableQueue";
|
|
16
|
+
/**
|
|
17
|
+
* @since 1.0.0
|
|
18
|
+
* @category Type IDs
|
|
19
|
+
*/
|
|
20
|
+
export declare const TypeId: TypeId;
|
|
21
|
+
/**
|
|
22
|
+
* @since 1.0.0
|
|
23
|
+
* @category Models
|
|
24
|
+
*/
|
|
25
|
+
export interface DurableQueue<Payload extends Schema.Schema.Any, Success extends Schema.Schema.Any = typeof Schema.Void, Error extends Schema.Schema.All = typeof Schema.Never> {
|
|
26
|
+
readonly [TypeId]: TypeId;
|
|
27
|
+
readonly name: string;
|
|
28
|
+
readonly payloadSchema: Payload;
|
|
29
|
+
readonly idempotencyKey: (payload: Payload["Type"]) => string;
|
|
30
|
+
readonly deferred: DurableDeferred.DurableDeferred<Success, Error>;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* A `DurableQueue` wraps a `PersistedQueue`, providing a way to wait for items
|
|
34
|
+
* to finish processing using a `DurableDeferred`.
|
|
35
|
+
*
|
|
36
|
+
* ```ts
|
|
37
|
+
* import { DurableQueue, Workflow } from "@effect/workflow"
|
|
38
|
+
* import { Effect, Schema } from "effect"
|
|
39
|
+
*
|
|
40
|
+
* // Define a DurableQueue that can be used to derive workers and offer items for
|
|
41
|
+
* // processing.
|
|
42
|
+
* const ApiQueue = DurableQueue.make({
|
|
43
|
+
* name: "ApiQueue",
|
|
44
|
+
* payload: {
|
|
45
|
+
* id: Schema.String
|
|
46
|
+
* },
|
|
47
|
+
* success: Schema.Void,
|
|
48
|
+
* error: Schema.Never,
|
|
49
|
+
* idempotencyKey(payload) {
|
|
50
|
+
* return payload.id
|
|
51
|
+
* }
|
|
52
|
+
* })
|
|
53
|
+
*
|
|
54
|
+
* const MyWorkflow = Workflow.make({
|
|
55
|
+
* name: "MyWorkflow",
|
|
56
|
+
* payload: {
|
|
57
|
+
* id: Schema.String
|
|
58
|
+
* },
|
|
59
|
+
* idempotencyKey: ({ id }) => id
|
|
60
|
+
* })
|
|
61
|
+
*
|
|
62
|
+
* const MyWorkflowLayer = MyWorkflow.toLayer(
|
|
63
|
+
* Effect.fn(function*() {
|
|
64
|
+
* // Add an item to the DurableQueue defined above.
|
|
65
|
+
* //
|
|
66
|
+
* // When the worker has finished processing the item, the workflow will
|
|
67
|
+
* // resume.
|
|
68
|
+
* //
|
|
69
|
+
* yield* DurableQueue.process(ApiQueue, { id: "api-call-1" })
|
|
70
|
+
*
|
|
71
|
+
* yield* Effect.log("Workflow succeeded!")
|
|
72
|
+
* })
|
|
73
|
+
* )
|
|
74
|
+
*
|
|
75
|
+
* // Define a worker layer that can process items from the DurableQueue.
|
|
76
|
+
* const ApiWorker = DurableQueue.worker(
|
|
77
|
+
* ApiQueue,
|
|
78
|
+
* Effect.fn(function*({ id }) {
|
|
79
|
+
* yield* Effect.log(`Worker processing API call with id: ${id}`)
|
|
80
|
+
* }),
|
|
81
|
+
* { concurrency: 5 } // Process up to 5 items concurrently
|
|
82
|
+
* )
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @since 1.0.0
|
|
86
|
+
* @category Constructors
|
|
87
|
+
*/
|
|
88
|
+
export declare const make: <Payload extends Schema.Schema.Any | Schema.Struct.Fields, Success extends Schema.Schema.Any = typeof Schema.Void, Error extends Schema.Schema.All = typeof Schema.Never>(options: {
|
|
89
|
+
readonly name: string;
|
|
90
|
+
readonly payload: Payload;
|
|
91
|
+
readonly idempotencyKey: (payload: Payload extends Schema.Struct.Fields ? Schema.Struct<Payload>["Type"] : Payload["Type"]) => string;
|
|
92
|
+
readonly success?: Success | undefined;
|
|
93
|
+
readonly error?: Error | undefined;
|
|
94
|
+
}) => DurableQueue<Payload extends Schema.Struct.Fields ? Schema.Struct<Payload> : Payload, Success, Error>;
|
|
95
|
+
/**
|
|
96
|
+
* @since 1.0.0
|
|
97
|
+
* @category Processing
|
|
98
|
+
*/
|
|
99
|
+
export declare const process: <Payload extends Schema.Schema.Any, Success extends Schema.Schema.Any, Error extends Schema.Schema.All>(self: DurableQueue<Payload, Success, Error>, payload: Payload["Type"], options?: {
|
|
100
|
+
readonly retrySchedule?: Schedule.Schedule<any, PersistedQueue.PersistedQueueError> | undefined;
|
|
101
|
+
}) => Effect.Effect<Success["Type"], Error["Type"], WorkflowEngine.WorkflowEngine | WorkflowEngine.WorkflowInstance | PersistedQueue.PersistedQueueFactory | Success["Context"] | Error["Context"] | Payload["Context"]>;
|
|
102
|
+
/**
|
|
103
|
+
* @since 1.0.0
|
|
104
|
+
* @category Worker
|
|
105
|
+
*/
|
|
106
|
+
export declare const makeWorker: <Payload extends Schema.Schema.Any, Success extends Schema.Schema.Any, Error extends Schema.Schema.All, R>(self: DurableQueue<Payload, Success, Error>, f: (payload: Payload["Type"]) => Effect.Effect<Success["Type"], Error["Type"], R>, options?: {
|
|
107
|
+
readonly concurrency?: number | undefined;
|
|
108
|
+
} | undefined) => Effect.Effect<never, never, WorkflowEngine.WorkflowEngine | PersistedQueue.PersistedQueueFactory | R | Payload["Context"] | Success["Context"] | Error["Context"]>;
|
|
109
|
+
/**
|
|
110
|
+
* @since 1.0.0
|
|
111
|
+
* @category Worker
|
|
112
|
+
*/
|
|
113
|
+
export declare const worker: <Payload extends Schema.Schema.Any, Success extends Schema.Schema.Any, Error extends Schema.Schema.All, R>(self: DurableQueue<Payload, Success, Error>, f: (payload: Payload["Type"]) => Effect.Effect<Success["Type"], Error["Type"], R>, options?: {
|
|
114
|
+
readonly concurrency?: number | undefined;
|
|
115
|
+
} | undefined) => Layer.Layer<never, never, WorkflowEngine.WorkflowEngine | PersistedQueue.PersistedQueueFactory | R | Payload["Context"] | Success["Context"] | Error["Context"]>;
|
|
116
|
+
//# sourceMappingURL=DurableQueue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DurableQueue.d.ts","sourceRoot":"","sources":["../../src/DurableQueue.ts"],"names":[],"mappings":"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"}
|
package/dist/dts/index.d.ts
CHANGED
package/dist/dts/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAEzC;;GAEG;AACH,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA;AAEjD;;GAEG;AACH,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAA;AAEvD;;GAEG;AACH,OAAO,KAAK,kBAAkB,MAAM,yBAAyB,CAAA;AAE7D;;GAEG;AACH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAEzC;;GAEG;AACH,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AAErD;;GAEG;AACH,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAEnD;;GAEG;AACH,OAAO,KAAK,mBAAmB,MAAM,0BAA0B,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAEzC;;GAEG;AACH,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA;AAEjD;;GAEG;AACH,OAAO,KAAK,eAAe,MAAM,sBAAsB,CAAA;AAEvD;;GAEG;AACH,OAAO,KAAK,YAAY,MAAM,mBAAmB,CAAA;AAEjD;;GAEG;AACH,OAAO,KAAK,kBAAkB,MAAM,yBAAyB,CAAA;AAE7D;;GAEG;AACH,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAA;AAEzC;;GAEG;AACH,OAAO,KAAK,cAAc,MAAM,qBAAqB,CAAA;AAErD;;GAEG;AACH,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAA;AAEnD;;GAEG;AACH,OAAO,KAAK,mBAAmB,MAAM,0BAA0B,CAAA"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import * as PersistedQueue from "@effect/experimental/PersistedQueue";
|
|
5
|
+
import * as Context from "effect/Context";
|
|
6
|
+
import * as Effect from "effect/Effect";
|
|
7
|
+
import * as Layer from "effect/Layer";
|
|
8
|
+
import * as Schedule from "effect/Schedule";
|
|
9
|
+
import * as Schema from "effect/Schema";
|
|
10
|
+
import * as Activity from "./Activity.js";
|
|
11
|
+
import * as DurableDeferred from "./DurableDeferred.js";
|
|
12
|
+
import { makeHashDigest } from "./internal/crypto.js";
|
|
13
|
+
/**
|
|
14
|
+
* @since 1.0.0
|
|
15
|
+
* @category Type IDs
|
|
16
|
+
*/
|
|
17
|
+
export const TypeId = "~@effect/workflow/DurableQueue";
|
|
18
|
+
/**
|
|
19
|
+
* A `DurableQueue` wraps a `PersistedQueue`, providing a way to wait for items
|
|
20
|
+
* to finish processing using a `DurableDeferred`.
|
|
21
|
+
*
|
|
22
|
+
* ```ts
|
|
23
|
+
* import { DurableQueue, Workflow } from "@effect/workflow"
|
|
24
|
+
* import { Effect, Schema } from "effect"
|
|
25
|
+
*
|
|
26
|
+
* // Define a DurableQueue that can be used to derive workers and offer items for
|
|
27
|
+
* // processing.
|
|
28
|
+
* const ApiQueue = DurableQueue.make({
|
|
29
|
+
* name: "ApiQueue",
|
|
30
|
+
* payload: {
|
|
31
|
+
* id: Schema.String
|
|
32
|
+
* },
|
|
33
|
+
* success: Schema.Void,
|
|
34
|
+
* error: Schema.Never,
|
|
35
|
+
* idempotencyKey(payload) {
|
|
36
|
+
* return payload.id
|
|
37
|
+
* }
|
|
38
|
+
* })
|
|
39
|
+
*
|
|
40
|
+
* const MyWorkflow = Workflow.make({
|
|
41
|
+
* name: "MyWorkflow",
|
|
42
|
+
* payload: {
|
|
43
|
+
* id: Schema.String
|
|
44
|
+
* },
|
|
45
|
+
* idempotencyKey: ({ id }) => id
|
|
46
|
+
* })
|
|
47
|
+
*
|
|
48
|
+
* const MyWorkflowLayer = MyWorkflow.toLayer(
|
|
49
|
+
* Effect.fn(function*() {
|
|
50
|
+
* // Add an item to the DurableQueue defined above.
|
|
51
|
+
* //
|
|
52
|
+
* // When the worker has finished processing the item, the workflow will
|
|
53
|
+
* // resume.
|
|
54
|
+
* //
|
|
55
|
+
* yield* DurableQueue.process(ApiQueue, { id: "api-call-1" })
|
|
56
|
+
*
|
|
57
|
+
* yield* Effect.log("Workflow succeeded!")
|
|
58
|
+
* })
|
|
59
|
+
* )
|
|
60
|
+
*
|
|
61
|
+
* // Define a worker layer that can process items from the DurableQueue.
|
|
62
|
+
* const ApiWorker = DurableQueue.worker(
|
|
63
|
+
* ApiQueue,
|
|
64
|
+
* Effect.fn(function*({ id }) {
|
|
65
|
+
* yield* Effect.log(`Worker processing API call with id: ${id}`)
|
|
66
|
+
* }),
|
|
67
|
+
* { concurrency: 5 } // Process up to 5 items concurrently
|
|
68
|
+
* )
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @since 1.0.0
|
|
72
|
+
* @category Constructors
|
|
73
|
+
*/
|
|
74
|
+
export const make = options => ({
|
|
75
|
+
[TypeId]: TypeId,
|
|
76
|
+
name: options.name,
|
|
77
|
+
payloadSchema: Schema.isSchema(options.payload) ? options.payload : Schema.Struct(options.payload),
|
|
78
|
+
idempotencyKey: options.idempotencyKey,
|
|
79
|
+
deferred: DurableDeferred.make(`DurableQueue/${options.name}`, {
|
|
80
|
+
success: options.success,
|
|
81
|
+
error: options.error
|
|
82
|
+
})
|
|
83
|
+
});
|
|
84
|
+
const queueSchemas = /*#__PURE__*/new WeakMap();
|
|
85
|
+
const getQueueSchema = payload => {
|
|
86
|
+
let schema = queueSchemas.get(payload);
|
|
87
|
+
if (!schema) {
|
|
88
|
+
schema = Schema.Struct({
|
|
89
|
+
token: Schema.String,
|
|
90
|
+
traceId: Schema.String,
|
|
91
|
+
spanId: Schema.String,
|
|
92
|
+
sampled: Schema.Boolean,
|
|
93
|
+
payload
|
|
94
|
+
});
|
|
95
|
+
queueSchemas.set(payload, schema);
|
|
96
|
+
}
|
|
97
|
+
return schema;
|
|
98
|
+
};
|
|
99
|
+
/**
|
|
100
|
+
* @since 1.0.0
|
|
101
|
+
* @category Processing
|
|
102
|
+
*/
|
|
103
|
+
export const process = /*#__PURE__*/Effect.fnUntraced(function* (self, payload, options) {
|
|
104
|
+
const key = yield* makeHashDigest(self.idempotencyKey(payload));
|
|
105
|
+
const deferred = DurableDeferred.make(`${self.deferred.name}/${key}`, {
|
|
106
|
+
success: self.deferred.successSchema,
|
|
107
|
+
error: self.deferred.errorSchema
|
|
108
|
+
});
|
|
109
|
+
yield* Activity.make({
|
|
110
|
+
name: `DurableQueue/${self.name}/${key}`,
|
|
111
|
+
execute: Effect.gen(function* () {
|
|
112
|
+
const span = yield* Effect.orDie(Effect.currentSpan);
|
|
113
|
+
const queue = yield* PersistedQueue.make({
|
|
114
|
+
name: `DurableQueue/${self.name}`,
|
|
115
|
+
schema: getQueueSchema(self.payloadSchema)
|
|
116
|
+
});
|
|
117
|
+
const token = yield* DurableDeferred.token(deferred);
|
|
118
|
+
yield* queue.offer({
|
|
119
|
+
token,
|
|
120
|
+
payload,
|
|
121
|
+
traceId: span.traceId,
|
|
122
|
+
spanId: span.spanId,
|
|
123
|
+
sampled: span.sampled
|
|
124
|
+
}).pipe(Effect.tapErrorCause(Effect.logWarning), Effect.catchTag("ParseError", Effect.die), Effect.retry(options?.retrySchedule ?? defaultRetrySchedule), Effect.orDie, Effect.annotateLogs({
|
|
125
|
+
package: "@effect/workflow",
|
|
126
|
+
module: "DurableQueue",
|
|
127
|
+
fiber: "process",
|
|
128
|
+
queueName: self.name
|
|
129
|
+
}));
|
|
130
|
+
})
|
|
131
|
+
});
|
|
132
|
+
return yield* DurableDeferred.await(deferred);
|
|
133
|
+
});
|
|
134
|
+
const defaultRetrySchedule = /*#__PURE__*/Schedule.exponential(500, 1.5).pipe(/*#__PURE__*/Schedule.union(/*#__PURE__*/Schedule.spaced("1 minute")));
|
|
135
|
+
/**
|
|
136
|
+
* @since 1.0.0
|
|
137
|
+
* @category Worker
|
|
138
|
+
*/
|
|
139
|
+
export const makeWorker = /*#__PURE__*/Effect.fnUntraced(function* (self, f, options) {
|
|
140
|
+
const queue = yield* PersistedQueue.make({
|
|
141
|
+
name: `DurableQueue/${self.name}`,
|
|
142
|
+
schema: getQueueSchema(self.payloadSchema)
|
|
143
|
+
});
|
|
144
|
+
const concurrency = options?.concurrency ?? 1;
|
|
145
|
+
const worker = queue.take(item_ => {
|
|
146
|
+
const item = item_;
|
|
147
|
+
return f(item.payload).pipe(Effect.exit, Effect.flatMap(exit => DurableDeferred.done(self.deferred, {
|
|
148
|
+
token: item.token,
|
|
149
|
+
exit
|
|
150
|
+
})), Effect.asVoid, Effect.withSpan(`DurableQueue/${self.name}/worker`, {
|
|
151
|
+
captureStackTrace: false,
|
|
152
|
+
parent: {
|
|
153
|
+
_tag: "ExternalSpan",
|
|
154
|
+
traceId: item.traceId,
|
|
155
|
+
spanId: item.spanId,
|
|
156
|
+
sampled: item.sampled,
|
|
157
|
+
context: Context.empty()
|
|
158
|
+
}
|
|
159
|
+
}));
|
|
160
|
+
}).pipe(Effect.catchAllCause(Effect.logWarning), Effect.forever, Effect.annotateLogs({
|
|
161
|
+
package: "@effect/workflow",
|
|
162
|
+
module: "DurableQueue",
|
|
163
|
+
fiber: "worker"
|
|
164
|
+
}));
|
|
165
|
+
yield* Effect.replicateEffect(worker, concurrency, {
|
|
166
|
+
concurrency,
|
|
167
|
+
discard: true
|
|
168
|
+
});
|
|
169
|
+
return yield* Effect.never;
|
|
170
|
+
});
|
|
171
|
+
/**
|
|
172
|
+
* @since 1.0.0
|
|
173
|
+
* @category Worker
|
|
174
|
+
*/
|
|
175
|
+
export const worker = (self, f, options) => Layer.scopedDiscard(Effect.forkScoped(makeWorker(self, f, options)));
|
|
176
|
+
//# sourceMappingURL=DurableQueue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DurableQueue.js","names":["PersistedQueue","Context","Effect","Layer","Schedule","Schema","Activity","DurableDeferred","makeHashDigest","TypeId","make","options","name","payloadSchema","isSchema","payload","Struct","idempotencyKey","deferred","success","error","queueSchemas","WeakMap","getQueueSchema","schema","get","token","String","traceId","spanId","sampled","Boolean","set","process","fnUntraced","self","key","successSchema","errorSchema","execute","gen","span","orDie","currentSpan","queue","offer","pipe","tapErrorCause","logWarning","catchTag","die","retry","retrySchedule","defaultRetrySchedule","annotateLogs","package","module","fiber","queueName","await","exponential","union","spaced","makeWorker","f","concurrency","worker","take","item_","item","exit","flatMap","done","asVoid","withSpan","captureStackTrace","parent","_tag","context","empty","catchAllCause","forever","replicateEffect","discard","never","scopedDiscard","forkScoped"],"sources":["../../src/DurableQueue.ts"],"sourcesContent":[null],"mappings":"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","ignoreList":[]}
|
package/dist/esm/index.js
CHANGED
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["Activity","DurableClock","DurableDeferred","DurableRateLimiter","Workflow","WorkflowEngine","WorkflowProxy","WorkflowProxyServer"],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA;;;AAGA,OAAO,KAAKA,QAAQ,MAAM,eAAe;AAEzC;;;AAGA,OAAO,KAAKC,YAAY,MAAM,mBAAmB;AAEjD;;;AAGA,OAAO,KAAKC,eAAe,MAAM,sBAAsB;AAEvD;;;AAGA,OAAO,KAAKC,kBAAkB,MAAM,yBAAyB;AAE7D;;;AAGA,OAAO,KAAKC,QAAQ,MAAM,eAAe;AAEzC;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,mBAAmB,MAAM,0BAA0B","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"index.js","names":["Activity","DurableClock","DurableDeferred","DurableQueue","DurableRateLimiter","Workflow","WorkflowEngine","WorkflowProxy","WorkflowProxyServer"],"sources":["../../src/index.ts"],"sourcesContent":[null],"mappings":"AAAA;;;AAGA,OAAO,KAAKA,QAAQ,MAAM,eAAe;AAEzC;;;AAGA,OAAO,KAAKC,YAAY,MAAM,mBAAmB;AAEjD;;;AAGA,OAAO,KAAKC,eAAe,MAAM,sBAAsB;AAEvD;;;AAGA,OAAO,KAAKC,YAAY,MAAM,mBAAmB;AAEjD;;;AAGA,OAAO,KAAKC,kBAAkB,MAAM,yBAAyB;AAE7D;;;AAGA,OAAO,KAAKC,QAAQ,MAAM,eAAe;AAEzC;;;AAGA,OAAO,KAAKC,cAAc,MAAM,qBAAqB;AAErD;;;AAGA,OAAO,KAAKC,aAAa,MAAM,oBAAoB;AAEnD;;;AAGA,OAAO,KAAKC,mBAAmB,MAAM,0BAA0B","ignoreList":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@effect/workflow",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.1",
|
|
4
4
|
"description": "Durable workflows for Effect",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -11,9 +11,9 @@
|
|
|
11
11
|
"sideEffects": [],
|
|
12
12
|
"homepage": "https://effect.website",
|
|
13
13
|
"peerDependencies": {
|
|
14
|
-
"effect": "^3.19.
|
|
15
|
-
"@effect/experimental": "^0.57.
|
|
16
|
-
"@effect/platform": "^0.93.
|
|
14
|
+
"effect": "^3.19.8",
|
|
15
|
+
"@effect/experimental": "^0.57.7",
|
|
16
|
+
"@effect/platform": "^0.93.5",
|
|
17
17
|
"@effect/rpc": "^0.72.2"
|
|
18
18
|
},
|
|
19
19
|
"publishConfig": {
|
|
@@ -44,6 +44,11 @@
|
|
|
44
44
|
"import": "./dist/esm/DurableDeferred.js",
|
|
45
45
|
"default": "./dist/cjs/DurableDeferred.js"
|
|
46
46
|
},
|
|
47
|
+
"./DurableQueue": {
|
|
48
|
+
"types": "./dist/dts/DurableQueue.d.ts",
|
|
49
|
+
"import": "./dist/esm/DurableQueue.js",
|
|
50
|
+
"default": "./dist/cjs/DurableQueue.js"
|
|
51
|
+
},
|
|
47
52
|
"./DurableRateLimiter": {
|
|
48
53
|
"types": "./dist/dts/DurableRateLimiter.d.ts",
|
|
49
54
|
"import": "./dist/esm/DurableRateLimiter.js",
|
|
@@ -81,6 +86,9 @@
|
|
|
81
86
|
"DurableDeferred": [
|
|
82
87
|
"./dist/dts/DurableDeferred.d.ts"
|
|
83
88
|
],
|
|
89
|
+
"DurableQueue": [
|
|
90
|
+
"./dist/dts/DurableQueue.d.ts"
|
|
91
|
+
],
|
|
84
92
|
"DurableRateLimiter": [
|
|
85
93
|
"./dist/dts/DurableRateLimiter.d.ts"
|
|
86
94
|
],
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @since 1.0.0
|
|
3
|
+
*/
|
|
4
|
+
import * as PersistedQueue from "@effect/experimental/PersistedQueue"
|
|
5
|
+
import * as Context from "effect/Context"
|
|
6
|
+
import * as Effect from "effect/Effect"
|
|
7
|
+
import * as Layer from "effect/Layer"
|
|
8
|
+
import * as Schedule from "effect/Schedule"
|
|
9
|
+
import * as Schema from "effect/Schema"
|
|
10
|
+
import * as Activity from "./Activity.js"
|
|
11
|
+
import * as DurableDeferred from "./DurableDeferred.js"
|
|
12
|
+
import { makeHashDigest } from "./internal/crypto.js"
|
|
13
|
+
import type * as WorkflowEngine from "./WorkflowEngine.js"
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @since 1.0.0
|
|
17
|
+
* @category Type IDs
|
|
18
|
+
*/
|
|
19
|
+
export type TypeId = "~@effect/workflow/DurableQueue"
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @since 1.0.0
|
|
23
|
+
* @category Type IDs
|
|
24
|
+
*/
|
|
25
|
+
export const TypeId: TypeId = "~@effect/workflow/DurableQueue"
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @since 1.0.0
|
|
29
|
+
* @category Models
|
|
30
|
+
*/
|
|
31
|
+
export interface DurableQueue<
|
|
32
|
+
Payload extends Schema.Schema.Any,
|
|
33
|
+
Success extends Schema.Schema.Any = typeof Schema.Void,
|
|
34
|
+
Error extends Schema.Schema.All = typeof Schema.Never
|
|
35
|
+
> {
|
|
36
|
+
readonly [TypeId]: TypeId
|
|
37
|
+
readonly name: string
|
|
38
|
+
readonly payloadSchema: Payload
|
|
39
|
+
readonly idempotencyKey: (payload: Payload["Type"]) => string
|
|
40
|
+
readonly deferred: DurableDeferred.DurableDeferred<Success, Error>
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A `DurableQueue` wraps a `PersistedQueue`, providing a way to wait for items
|
|
45
|
+
* to finish processing using a `DurableDeferred`.
|
|
46
|
+
*
|
|
47
|
+
* ```ts
|
|
48
|
+
* import { DurableQueue, Workflow } from "@effect/workflow"
|
|
49
|
+
* import { Effect, Schema } from "effect"
|
|
50
|
+
*
|
|
51
|
+
* // Define a DurableQueue that can be used to derive workers and offer items for
|
|
52
|
+
* // processing.
|
|
53
|
+
* const ApiQueue = DurableQueue.make({
|
|
54
|
+
* name: "ApiQueue",
|
|
55
|
+
* payload: {
|
|
56
|
+
* id: Schema.String
|
|
57
|
+
* },
|
|
58
|
+
* success: Schema.Void,
|
|
59
|
+
* error: Schema.Never,
|
|
60
|
+
* idempotencyKey(payload) {
|
|
61
|
+
* return payload.id
|
|
62
|
+
* }
|
|
63
|
+
* })
|
|
64
|
+
*
|
|
65
|
+
* const MyWorkflow = Workflow.make({
|
|
66
|
+
* name: "MyWorkflow",
|
|
67
|
+
* payload: {
|
|
68
|
+
* id: Schema.String
|
|
69
|
+
* },
|
|
70
|
+
* idempotencyKey: ({ id }) => id
|
|
71
|
+
* })
|
|
72
|
+
*
|
|
73
|
+
* const MyWorkflowLayer = MyWorkflow.toLayer(
|
|
74
|
+
* Effect.fn(function*() {
|
|
75
|
+
* // Add an item to the DurableQueue defined above.
|
|
76
|
+
* //
|
|
77
|
+
* // When the worker has finished processing the item, the workflow will
|
|
78
|
+
* // resume.
|
|
79
|
+
* //
|
|
80
|
+
* yield* DurableQueue.process(ApiQueue, { id: "api-call-1" })
|
|
81
|
+
*
|
|
82
|
+
* yield* Effect.log("Workflow succeeded!")
|
|
83
|
+
* })
|
|
84
|
+
* )
|
|
85
|
+
*
|
|
86
|
+
* // Define a worker layer that can process items from the DurableQueue.
|
|
87
|
+
* const ApiWorker = DurableQueue.worker(
|
|
88
|
+
* ApiQueue,
|
|
89
|
+
* Effect.fn(function*({ id }) {
|
|
90
|
+
* yield* Effect.log(`Worker processing API call with id: ${id}`)
|
|
91
|
+
* }),
|
|
92
|
+
* { concurrency: 5 } // Process up to 5 items concurrently
|
|
93
|
+
* )
|
|
94
|
+
* ```
|
|
95
|
+
*
|
|
96
|
+
* @since 1.0.0
|
|
97
|
+
* @category Constructors
|
|
98
|
+
*/
|
|
99
|
+
export const make = <
|
|
100
|
+
Payload extends Schema.Schema.Any | Schema.Struct.Fields,
|
|
101
|
+
Success extends Schema.Schema.Any = typeof Schema.Void,
|
|
102
|
+
Error extends Schema.Schema.All = typeof Schema.Never
|
|
103
|
+
>(
|
|
104
|
+
options: {
|
|
105
|
+
readonly name: string
|
|
106
|
+
readonly payload: Payload
|
|
107
|
+
readonly idempotencyKey: (
|
|
108
|
+
payload: Payload extends Schema.Struct.Fields ? Schema.Struct<Payload>["Type"] : Payload["Type"]
|
|
109
|
+
) => string
|
|
110
|
+
readonly success?: Success | undefined
|
|
111
|
+
readonly error?: Error | undefined
|
|
112
|
+
}
|
|
113
|
+
): DurableQueue<
|
|
114
|
+
Payload extends Schema.Struct.Fields ? Schema.Struct<Payload> : Payload,
|
|
115
|
+
Success,
|
|
116
|
+
Error
|
|
117
|
+
> => ({
|
|
118
|
+
[TypeId]: TypeId,
|
|
119
|
+
name: options.name,
|
|
120
|
+
payloadSchema: Schema.isSchema(options.payload) ? options.payload : Schema.Struct(options.payload) as any,
|
|
121
|
+
idempotencyKey: options.idempotencyKey as any,
|
|
122
|
+
deferred: DurableDeferred.make(`DurableQueue/${options.name}`, {
|
|
123
|
+
success: options.success,
|
|
124
|
+
error: options.error
|
|
125
|
+
})
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const queueSchemas = new WeakMap<Schema.Schema.Any, Schema.Schema.Any>()
|
|
129
|
+
const getQueueSchema = <Payload extends Schema.Schema.Any>(
|
|
130
|
+
payload: Payload
|
|
131
|
+
): Schema.Struct<{
|
|
132
|
+
token: typeof Schema.String
|
|
133
|
+
payload: Payload
|
|
134
|
+
traceId: typeof Schema.String
|
|
135
|
+
spanId: typeof Schema.String
|
|
136
|
+
sampled: typeof Schema.Boolean
|
|
137
|
+
}> => {
|
|
138
|
+
let schema = queueSchemas.get(payload)
|
|
139
|
+
if (!schema) {
|
|
140
|
+
schema = Schema.Struct({
|
|
141
|
+
token: Schema.String,
|
|
142
|
+
traceId: Schema.String,
|
|
143
|
+
spanId: Schema.String,
|
|
144
|
+
sampled: Schema.Boolean,
|
|
145
|
+
payload
|
|
146
|
+
})
|
|
147
|
+
queueSchemas.set(payload, schema)
|
|
148
|
+
}
|
|
149
|
+
return schema as any
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @since 1.0.0
|
|
154
|
+
* @category Processing
|
|
155
|
+
*/
|
|
156
|
+
export const process: <
|
|
157
|
+
Payload extends Schema.Schema.Any,
|
|
158
|
+
Success extends Schema.Schema.Any,
|
|
159
|
+
Error extends Schema.Schema.All
|
|
160
|
+
>(
|
|
161
|
+
self: DurableQueue<Payload, Success, Error>,
|
|
162
|
+
payload: Payload["Type"],
|
|
163
|
+
options?: {
|
|
164
|
+
readonly retrySchedule?: Schedule.Schedule<any, PersistedQueue.PersistedQueueError> | undefined
|
|
165
|
+
}
|
|
166
|
+
) => Effect.Effect<
|
|
167
|
+
Success["Type"],
|
|
168
|
+
Error["Type"],
|
|
169
|
+
| WorkflowEngine.WorkflowEngine
|
|
170
|
+
| WorkflowEngine.WorkflowInstance
|
|
171
|
+
| PersistedQueue.PersistedQueueFactory
|
|
172
|
+
| Success["Context"]
|
|
173
|
+
| Error["Context"]
|
|
174
|
+
| Payload["Context"]
|
|
175
|
+
> = Effect.fnUntraced(function*<
|
|
176
|
+
Payload extends Schema.Schema.Any,
|
|
177
|
+
Success extends Schema.Schema.Any,
|
|
178
|
+
Error extends Schema.Schema.All
|
|
179
|
+
>(self: DurableQueue<Payload, Success, Error>, payload: Payload["Type"], options?: {
|
|
180
|
+
readonly retrySchedule?: Schedule.Schedule<any, PersistedQueue.PersistedQueueError> | undefined
|
|
181
|
+
}) {
|
|
182
|
+
const key = yield* makeHashDigest(self.idempotencyKey(payload))
|
|
183
|
+
|
|
184
|
+
const deferred = DurableDeferred.make(`${self.deferred.name}/${key}`, {
|
|
185
|
+
success: self.deferred.successSchema,
|
|
186
|
+
error: self.deferred.errorSchema
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
yield* Activity.make({
|
|
190
|
+
name: `DurableQueue/${self.name}/${key}`,
|
|
191
|
+
execute: Effect.gen(function*() {
|
|
192
|
+
const span = yield* Effect.orDie(Effect.currentSpan)
|
|
193
|
+
const queue = yield* PersistedQueue.make({
|
|
194
|
+
name: `DurableQueue/${self.name}`,
|
|
195
|
+
schema: getQueueSchema(self.payloadSchema)
|
|
196
|
+
})
|
|
197
|
+
const token = yield* DurableDeferred.token(deferred)
|
|
198
|
+
yield* queue.offer({
|
|
199
|
+
token,
|
|
200
|
+
payload,
|
|
201
|
+
traceId: span.traceId,
|
|
202
|
+
spanId: span.spanId,
|
|
203
|
+
sampled: span.sampled
|
|
204
|
+
} as any).pipe(
|
|
205
|
+
Effect.tapErrorCause(Effect.logWarning),
|
|
206
|
+
Effect.catchTag("ParseError", Effect.die),
|
|
207
|
+
Effect.retry(options?.retrySchedule ?? defaultRetrySchedule),
|
|
208
|
+
Effect.orDie,
|
|
209
|
+
Effect.annotateLogs({
|
|
210
|
+
package: "@effect/workflow",
|
|
211
|
+
module: "DurableQueue",
|
|
212
|
+
fiber: "process",
|
|
213
|
+
queueName: self.name
|
|
214
|
+
})
|
|
215
|
+
)
|
|
216
|
+
})
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
return yield* DurableDeferred.await(deferred)
|
|
220
|
+
})
|
|
221
|
+
|
|
222
|
+
const defaultRetrySchedule = Schedule.exponential(500, 1.5).pipe(
|
|
223
|
+
Schedule.union(Schedule.spaced("1 minute"))
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* @since 1.0.0
|
|
228
|
+
* @category Worker
|
|
229
|
+
*/
|
|
230
|
+
export const makeWorker: <
|
|
231
|
+
Payload extends Schema.Schema.Any,
|
|
232
|
+
Success extends Schema.Schema.Any,
|
|
233
|
+
Error extends Schema.Schema.All,
|
|
234
|
+
R
|
|
235
|
+
>(
|
|
236
|
+
self: DurableQueue<Payload, Success, Error>,
|
|
237
|
+
f: (payload: Payload["Type"]) => Effect.Effect<Success["Type"], Error["Type"], R>,
|
|
238
|
+
options?: { readonly concurrency?: number | undefined } | undefined
|
|
239
|
+
) => Effect.Effect<
|
|
240
|
+
never,
|
|
241
|
+
never,
|
|
242
|
+
| WorkflowEngine.WorkflowEngine
|
|
243
|
+
| PersistedQueue.PersistedQueueFactory
|
|
244
|
+
| R
|
|
245
|
+
| Payload["Context"]
|
|
246
|
+
| Success["Context"]
|
|
247
|
+
| Error["Context"]
|
|
248
|
+
> = Effect.fnUntraced(function*<
|
|
249
|
+
Payload extends Schema.Schema.Any,
|
|
250
|
+
Success extends Schema.Schema.Any,
|
|
251
|
+
Error extends Schema.Schema.All,
|
|
252
|
+
R
|
|
253
|
+
>(
|
|
254
|
+
self: DurableQueue<Payload, Success, Error>,
|
|
255
|
+
f: (payload: Payload["Type"]) => Effect.Effect<Success["Type"], Error["Type"], R>,
|
|
256
|
+
options?: {
|
|
257
|
+
readonly concurrency?: number | undefined
|
|
258
|
+
}
|
|
259
|
+
) {
|
|
260
|
+
const queue = yield* PersistedQueue.make({
|
|
261
|
+
name: `DurableQueue/${self.name}`,
|
|
262
|
+
schema: getQueueSchema(self.payloadSchema)
|
|
263
|
+
})
|
|
264
|
+
const concurrency = options?.concurrency ?? 1
|
|
265
|
+
|
|
266
|
+
const worker = queue.take((item_) => {
|
|
267
|
+
const item = item_ as any as {
|
|
268
|
+
token: DurableDeferred.Token
|
|
269
|
+
payload: Payload["Type"]
|
|
270
|
+
traceId: string
|
|
271
|
+
spanId: string
|
|
272
|
+
sampled: boolean
|
|
273
|
+
}
|
|
274
|
+
return f(item.payload).pipe(
|
|
275
|
+
Effect.exit,
|
|
276
|
+
Effect.flatMap((exit) =>
|
|
277
|
+
DurableDeferred.done(self.deferred, {
|
|
278
|
+
token: item.token,
|
|
279
|
+
exit
|
|
280
|
+
})
|
|
281
|
+
),
|
|
282
|
+
Effect.asVoid,
|
|
283
|
+
Effect.withSpan(`DurableQueue/${self.name}/worker`, {
|
|
284
|
+
captureStackTrace: false,
|
|
285
|
+
parent: {
|
|
286
|
+
_tag: "ExternalSpan",
|
|
287
|
+
traceId: item.traceId,
|
|
288
|
+
spanId: item.spanId,
|
|
289
|
+
sampled: item.sampled,
|
|
290
|
+
context: Context.empty()
|
|
291
|
+
}
|
|
292
|
+
})
|
|
293
|
+
)
|
|
294
|
+
}).pipe(
|
|
295
|
+
Effect.catchAllCause(Effect.logWarning),
|
|
296
|
+
Effect.forever,
|
|
297
|
+
Effect.annotateLogs({
|
|
298
|
+
package: "@effect/workflow",
|
|
299
|
+
module: "DurableQueue",
|
|
300
|
+
fiber: "worker"
|
|
301
|
+
})
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
yield* Effect.replicateEffect(worker, concurrency, { concurrency, discard: true })
|
|
305
|
+
return yield* Effect.never
|
|
306
|
+
})
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* @since 1.0.0
|
|
310
|
+
* @category Worker
|
|
311
|
+
*/
|
|
312
|
+
export const worker: <
|
|
313
|
+
Payload extends Schema.Schema.Any,
|
|
314
|
+
Success extends Schema.Schema.Any,
|
|
315
|
+
Error extends Schema.Schema.All,
|
|
316
|
+
R
|
|
317
|
+
>(
|
|
318
|
+
self: DurableQueue<Payload, Success, Error>,
|
|
319
|
+
f: (payload: Payload["Type"]) => Effect.Effect<Success["Type"], Error["Type"], R>,
|
|
320
|
+
options?: {
|
|
321
|
+
readonly concurrency?: number | undefined
|
|
322
|
+
} | undefined
|
|
323
|
+
) => Layer.Layer<
|
|
324
|
+
never,
|
|
325
|
+
never,
|
|
326
|
+
| WorkflowEngine.WorkflowEngine
|
|
327
|
+
| PersistedQueue.PersistedQueueFactory
|
|
328
|
+
| R
|
|
329
|
+
| Payload["Context"]
|
|
330
|
+
| Success["Context"]
|
|
331
|
+
| Error["Context"]
|
|
332
|
+
> = (self, f, options) => Layer.scopedDiscard(Effect.forkScoped(makeWorker(self, f, options)))
|
package/src/index.ts
CHANGED