@hotmeshio/hotmesh 0.0.54 → 0.0.56
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 +0 -3
- package/build/modules/enums.js +1 -10
- package/build/modules/key.d.ts +0 -38
- package/build/modules/key.js +4 -46
- package/build/modules/utils.d.ts +0 -8
- package/build/modules/utils.js +0 -14
- package/build/package.json +11 -4
- package/build/services/activities/activity.d.ts +0 -28
- package/build/services/activities/activity.js +1 -46
- package/build/services/activities/await.js +0 -4
- package/build/services/activities/cycle.d.ts +0 -7
- package/build/services/activities/cycle.js +1 -16
- package/build/services/activities/hook.d.ts +0 -6
- package/build/services/activities/hook.js +2 -12
- package/build/services/activities/interrupt.js +0 -8
- package/build/services/activities/signal.d.ts +0 -6
- package/build/services/activities/signal.js +0 -15
- package/build/services/activities/trigger.d.ts +0 -4
- package/build/services/activities/trigger.js +1 -7
- package/build/services/activities/worker.js +0 -4
- package/build/services/collator/index.d.ts +0 -70
- package/build/services/collator/index.js +1 -91
- package/build/services/compiler/deployer.js +6 -38
- package/build/services/compiler/index.d.ts +0 -15
- package/build/services/compiler/index.js +0 -20
- package/build/services/compiler/validator.d.ts +0 -3
- package/build/services/compiler/validator.js +0 -25
- package/build/services/connector/clients/ioredis.d.ts +2 -2
- package/build/services/connector/clients/ioredis.js +0 -2
- package/build/services/connector/clients/redis.d.ts +4 -4
- package/build/services/connector/clients/redis.js +1 -3
- package/build/services/connector/index.d.ts +1 -1
- package/build/services/connector/index.js +0 -2
- package/build/services/durable/client.d.ts +1 -26
- package/build/services/durable/client.js +0 -56
- package/build/services/durable/exporter.d.ts +0 -22
- package/build/services/durable/exporter.js +1 -30
- package/build/services/durable/handle.d.ts +0 -36
- package/build/services/durable/handle.js +0 -46
- package/build/services/durable/index.d.ts +0 -4
- package/build/services/durable/index.js +0 -4
- package/build/services/durable/schemas/factory.d.ts +0 -29
- package/build/services/durable/schemas/factory.js +0 -29
- package/build/services/durable/search.d.ts +1 -36
- package/build/services/durable/search.js +57 -56
- package/build/services/durable/worker.js +2 -22
- package/build/services/durable/workflow.d.ts +0 -114
- package/build/services/durable/workflow.js +4 -144
- package/build/services/engine/index.d.ts +1 -6
- package/build/services/engine/index.js +1 -43
- package/build/services/exporter/index.d.ts +0 -27
- package/build/services/exporter/index.js +0 -33
- package/build/services/hotmesh/index.d.ts +2 -2
- package/build/services/hotmesh/index.js +1 -9
- package/build/services/logger/index.js +0 -2
- package/build/services/mapper/index.d.ts +0 -14
- package/build/services/mapper/index.js +0 -14
- package/build/services/pipe/functions/date.d.ts +0 -7
- package/build/services/pipe/functions/date.js +0 -7
- package/build/services/pipe/functions/math.js +0 -2
- package/build/services/pipe/index.d.ts +0 -15
- package/build/services/pipe/index.js +2 -23
- package/build/services/quorum/index.d.ts +0 -7
- package/build/services/quorum/index.js +0 -21
- package/build/services/reporter/index.d.ts +0 -5
- package/build/services/reporter/index.js +0 -9
- package/build/services/router/index.d.ts +0 -9
- package/build/services/router/index.js +2 -38
- package/build/services/serializer/index.js +7 -26
- package/build/services/store/cache.d.ts +0 -18
- package/build/services/store/cache.js +0 -18
- package/build/services/store/clients/ioredis.d.ts +1 -1
- package/build/services/store/clients/ioredis.js +0 -1
- package/build/services/store/clients/redis.d.ts +1 -1
- package/build/services/store/index.d.ts +0 -55
- package/build/services/store/index.js +5 -81
- package/build/services/stream/clients/ioredis.d.ts +1 -1
- package/build/services/stream/clients/ioredis.js +1 -4
- package/build/services/stream/clients/redis.d.ts +1 -1
- package/build/services/sub/clients/ioredis.d.ts +1 -1
- package/build/services/sub/clients/redis.d.ts +1 -1
- package/build/services/task/index.d.ts +0 -9
- package/build/services/task/index.js +0 -31
- package/build/services/telemetry/index.d.ts +0 -7
- package/build/services/telemetry/index.js +1 -13
- package/build/services/worker/index.d.ts +0 -4
- package/build/services/worker/index.js +2 -6
- package/build/types/activity.d.ts +0 -81
- package/build/types/durable.d.ts +25 -177
- package/build/types/exporter.d.ts +0 -13
- package/build/types/hotmesh.d.ts +4 -16
- package/build/types/hotmesh.js +0 -3
- package/build/types/index.d.ts +4 -6
- package/build/types/index.js +4 -3
- package/build/types/job.d.ts +1 -86
- package/build/types/pipe.d.ts +0 -65
- package/build/types/quorum.d.ts +15 -10
- package/build/types/redis.d.ts +225 -7
- package/build/types/redis.js +9 -0
- package/build/types/stream.d.ts +0 -58
- package/build/types/stream.js +0 -4
- package/package.json +11 -4
- package/types/durable.ts +121 -3
- package/types/hotmesh.ts +3 -6
- package/types/index.ts +23 -10
- package/types/job.ts +1 -1
- package/types/quorum.ts +22 -0
- package/types/redis.ts +267 -18
- package/build/types/ioredisclient.d.ts +0 -5
- package/build/types/ioredisclient.js +0 -5
- package/build/types/redisclient.d.ts +0 -26
- package/build/types/redisclient.js +0 -2
- package/modules/enums.ts +0 -62
- package/modules/errors.ts +0 -280
- package/modules/key.ts +0 -101
- package/modules/storage.ts +0 -3
- package/modules/utils.ts +0 -242
- package/services/activities/activity.ts +0 -589
- package/services/activities/await.ts +0 -113
- package/services/activities/cycle.ts +0 -115
- package/services/activities/hook.ts +0 -197
- package/services/activities/index.ts +0 -19
- package/services/activities/interrupt.ts +0 -172
- package/services/activities/signal.ts +0 -148
- package/services/activities/trigger.ts +0 -295
- package/services/activities/worker.ts +0 -107
- package/services/collator/README.md +0 -102
- package/services/collator/index.ts +0 -291
- package/services/compiler/deployer.ts +0 -504
- package/services/compiler/index.ts +0 -98
- package/services/compiler/validator.ts +0 -158
- package/services/connector/clients/ioredis.ts +0 -57
- package/services/connector/clients/redis.ts +0 -72
- package/services/connector/index.ts +0 -42
- package/services/durable/client.ts +0 -266
- package/services/durable/connection.ts +0 -10
- package/services/durable/exporter.ts +0 -232
- package/services/durable/handle.ts +0 -160
- package/services/durable/index.ts +0 -27
- package/services/durable/schemas/factory.ts +0 -2358
- package/services/durable/search.ts +0 -196
- package/services/durable/worker.ts +0 -401
- package/services/durable/workflow.ts +0 -557
- package/services/engine/index.ts +0 -761
- package/services/exporter/index.ts +0 -146
- package/services/hotmesh/index.ts +0 -237
- package/services/logger/index.ts +0 -79
- package/services/mapper/index.ts +0 -89
- package/services/pipe/functions/array.ts +0 -78
- package/services/pipe/functions/bitwise.ts +0 -27
- package/services/pipe/functions/conditional.ts +0 -35
- package/services/pipe/functions/date.ts +0 -220
- package/services/pipe/functions/index.ts +0 -27
- package/services/pipe/functions/json.ts +0 -11
- package/services/pipe/functions/logical.ts +0 -11
- package/services/pipe/functions/math.ts +0 -217
- package/services/pipe/functions/number.ts +0 -75
- package/services/pipe/functions/object.ts +0 -98
- package/services/pipe/functions/string.ts +0 -86
- package/services/pipe/functions/symbol.ts +0 -39
- package/services/pipe/functions/unary.ts +0 -19
- package/services/pipe/index.ts +0 -216
- package/services/quorum/index.ts +0 -319
- package/services/reporter/index.ts +0 -387
- package/services/router/index.ts +0 -426
- package/services/serializer/README.md +0 -10
- package/services/serializer/index.ts +0 -285
- package/services/store/cache.ts +0 -172
- package/services/store/clients/ioredis.ts +0 -145
- package/services/store/clients/redis.ts +0 -191
- package/services/store/index.ts +0 -1091
- package/services/stream/clients/ioredis.ts +0 -157
- package/services/stream/clients/redis.ts +0 -158
- package/services/stream/index.ts +0 -58
- package/services/sub/clients/ioredis.ts +0 -83
- package/services/sub/clients/redis.ts +0 -74
- package/services/sub/index.ts +0 -25
- package/services/task/index.ts +0 -250
- package/services/telemetry/index.ts +0 -273
- package/services/worker/index.ts +0 -248
- package/types/ioredisclient.ts +0 -10
- package/types/redisclient.ts +0 -30
|
@@ -61,7 +61,6 @@ class WorkerService {
|
|
|
61
61
|
const baseTopic = `${config.taskQueue}-${workflowFunctionName}`;
|
|
62
62
|
const activityTopic = `${baseTopic}-activity`;
|
|
63
63
|
const workflowTopic = `${baseTopic}`;
|
|
64
|
-
//initialize supporting workflows
|
|
65
64
|
const worker = new WorkerService();
|
|
66
65
|
worker.activityRunner = await worker.initActivityWorker(config, activityTopic);
|
|
67
66
|
worker.workflowRunner = await worker.initWorkflowWorker(config, workflowTopic, workflowFunction);
|
|
@@ -104,11 +103,9 @@ class WorkerService {
|
|
|
104
103
|
WorkerService.instances.set(activityTopic, hotMeshWorker);
|
|
105
104
|
return hotMeshWorker;
|
|
106
105
|
}
|
|
107
|
-
//this is the linked worker function in the reentrant workflow test
|
|
108
106
|
wrapActivityFunctions() {
|
|
109
107
|
return async (data) => {
|
|
110
108
|
try {
|
|
111
|
-
//always run the activity function when instructed; return the response
|
|
112
109
|
const activityInput = data.data;
|
|
113
110
|
const activityName = activityInput.activityName;
|
|
114
111
|
const activityFunction = WorkerService.activityRegistry[activityName];
|
|
@@ -124,8 +121,6 @@ class WorkerService {
|
|
|
124
121
|
if (!(err instanceof errors_1.DurableTimeoutError) &&
|
|
125
122
|
!(err instanceof errors_1.DurableMaxedError) &&
|
|
126
123
|
!(err instanceof errors_1.DurableFatalError)) {
|
|
127
|
-
//use code 599 as a proxy for all retryable errors
|
|
128
|
-
// (basically anything not 596, 597, 598)
|
|
129
124
|
return {
|
|
130
125
|
status: stream_1.StreamStatus.SUCCESS,
|
|
131
126
|
code: enums_1.HMSH_CODE_DURABLE_RETRYABLE,
|
|
@@ -140,8 +135,6 @@ class WorkerService {
|
|
|
140
135
|
};
|
|
141
136
|
}
|
|
142
137
|
return {
|
|
143
|
-
//always returrn success (the Durable module is just fine);
|
|
144
|
-
// it's the user's function that has failed
|
|
145
138
|
status: stream_1.StreamStatus.SUCCESS,
|
|
146
139
|
code: err.code,
|
|
147
140
|
stack: err.stack,
|
|
@@ -181,7 +174,6 @@ class WorkerService {
|
|
|
181
174
|
const counter = { counter: 0 };
|
|
182
175
|
const interruptionRegistry = [];
|
|
183
176
|
try {
|
|
184
|
-
//incoming data payload has arguments and workflowId
|
|
185
177
|
const workflowInput = data.data;
|
|
186
178
|
const context = new Map();
|
|
187
179
|
context.set('canRetry', workflowInput.canRetry);
|
|
@@ -191,20 +183,14 @@ class WorkerService {
|
|
|
191
183
|
context.set('raw', data);
|
|
192
184
|
context.set('workflowId', workflowInput.workflowId);
|
|
193
185
|
if (workflowInput.originJobId) {
|
|
194
|
-
//if present there is an origin job to which this job is subordinated;
|
|
195
|
-
// garbage collect (expire) this job when originJobId is expired
|
|
196
186
|
context.set('originJobId', workflowInput.originJobId);
|
|
197
187
|
}
|
|
198
188
|
let replayQuery = '';
|
|
199
189
|
if (workflowInput.workflowDimension) {
|
|
200
|
-
//every hook function runs in an isolated dimension controlled
|
|
201
|
-
//by the index assigned when the signal was received; even if the
|
|
202
|
-
//hook function re-runs, its scope will always remain constant
|
|
203
190
|
context.set('workflowDimension', workflowInput.workflowDimension);
|
|
204
191
|
replayQuery = `-*${workflowInput.workflowDimension}-*`;
|
|
205
192
|
}
|
|
206
193
|
else {
|
|
207
|
-
//last letter of words like 'hook', 'sleep', 'wait', 'signal', 'search', 'start', 'proxy', 'child', 'collator'
|
|
208
194
|
replayQuery = '-*[ehklptydr]-*';
|
|
209
195
|
}
|
|
210
196
|
context.set('workflowTopic', workflowTopic);
|
|
@@ -214,7 +200,7 @@ class WorkerService {
|
|
|
214
200
|
const store = this.workflowRunner.engine.store;
|
|
215
201
|
const [cursor, replay] = await store.findJobFields(workflowInput.workflowId, replayQuery, 50000, 5000);
|
|
216
202
|
context.set('replay', replay);
|
|
217
|
-
context.set('cursor', cursor);
|
|
203
|
+
context.set('cursor', cursor);
|
|
218
204
|
const workflowResponse = await storage_1.asyncLocalStorage.run(context, async () => {
|
|
219
205
|
return await workflowFunction.apply(this, workflowInput.arguments);
|
|
220
206
|
});
|
|
@@ -227,7 +213,6 @@ class WorkerService {
|
|
|
227
213
|
}
|
|
228
214
|
catch (err) {
|
|
229
215
|
if (err instanceof errors_1.DurableWaitForError || interruptionRegistry.length > 1) {
|
|
230
|
-
//NOTE: this type is spawned when `Promise.all` is used OR if the interruption is a `waitFor`
|
|
231
216
|
const workflowInput = data.data;
|
|
232
217
|
const execIndex = counter.counter - interruptionRegistry.length + 1;
|
|
233
218
|
const { workflowId, workflowTopic, workflowDimension, originJobId } = workflowInput;
|
|
@@ -250,7 +235,6 @@ class WorkerService {
|
|
|
250
235
|
};
|
|
251
236
|
}
|
|
252
237
|
else if (err instanceof errors_1.DurableSleepError) {
|
|
253
|
-
//return the sleep interruption
|
|
254
238
|
return {
|
|
255
239
|
status: stream_1.StreamStatus.SUCCESS,
|
|
256
240
|
code: err.code,
|
|
@@ -265,7 +249,6 @@ class WorkerService {
|
|
|
265
249
|
};
|
|
266
250
|
}
|
|
267
251
|
else if (err instanceof errors_1.DurableProxyError) {
|
|
268
|
-
//return the proxyActivity interruption
|
|
269
252
|
return {
|
|
270
253
|
status: stream_1.StreamStatus.SUCCESS,
|
|
271
254
|
code: err.code,
|
|
@@ -288,7 +271,6 @@ class WorkerService {
|
|
|
288
271
|
};
|
|
289
272
|
}
|
|
290
273
|
else if (err instanceof errors_1.DurableChildError) {
|
|
291
|
-
//return the child interruption
|
|
292
274
|
const msg = {
|
|
293
275
|
message: err.message,
|
|
294
276
|
workflowId: err.workflowId,
|
|
@@ -315,8 +297,6 @@ class WorkerService {
|
|
|
315
297
|
}
|
|
316
298
|
};
|
|
317
299
|
}
|
|
318
|
-
// ALL other errors are actual fatal errors (598, 597, 596)
|
|
319
|
-
// OR will be retried (599)
|
|
320
300
|
return {
|
|
321
301
|
status: stream_1.StreamStatus.SUCCESS,
|
|
322
302
|
code: err.code || new errors_1.DurableRetryError(err.message).code,
|
|
@@ -341,7 +321,7 @@ class WorkerService {
|
|
|
341
321
|
}
|
|
342
322
|
}
|
|
343
323
|
_a = WorkerService;
|
|
344
|
-
WorkerService.activityRegistry = {};
|
|
324
|
+
WorkerService.activityRegistry = {};
|
|
345
325
|
WorkerService.instances = new Map();
|
|
346
326
|
WorkerService.getHotMesh = async (workflowTopic, config, options) => {
|
|
347
327
|
if (WorkerService.instances.has(workflowTopic)) {
|
|
@@ -4,136 +4,22 @@ import { ActivityConfig, HookOptions, ProxyType, WorkflowContext, WorkflowOption
|
|
|
4
4
|
import { JobInterruptOptions } from '../../types/job';
|
|
5
5
|
import { DurableChildErrorType, DurableProxyErrorType } from '../../types/error';
|
|
6
6
|
export declare class WorkflowService {
|
|
7
|
-
/**
|
|
8
|
-
* Returns the synchronous output from the activity (replay)
|
|
9
|
-
* if available locally, revealing whether or not the activity already
|
|
10
|
-
* ran during a prior execution cycle
|
|
11
|
-
* @param {string} prefix - one of: proxy, child, start, wait etc
|
|
12
|
-
* @returns
|
|
13
|
-
*/
|
|
14
7
|
static didRun(prefix: string): Promise<[boolean, number, any]>;
|
|
15
|
-
/**
|
|
16
|
-
* Those methods that may only be called once must be protected by flagging
|
|
17
|
-
* their execution with a unique key (the key is stored in the HASH alongside
|
|
18
|
-
* process state and job state)
|
|
19
|
-
* @private
|
|
20
|
-
*/
|
|
21
8
|
static isSideEffectAllowed(hotMeshClient: HotMesh, prefix: string): Promise<boolean>;
|
|
22
|
-
/**
|
|
23
|
-
* Returns the current workflow context restored
|
|
24
|
-
* from Redis
|
|
25
|
-
*/
|
|
26
9
|
static getContext(): WorkflowContext;
|
|
27
|
-
/**
|
|
28
|
-
* Return a handle to the hotmesh client hosting the workflow execution
|
|
29
|
-
* @returns {Promise<HotMesh>} - a hotmesh client
|
|
30
|
-
*/
|
|
31
10
|
static getHotMesh(): Promise<HotMesh>;
|
|
32
|
-
/**
|
|
33
|
-
* Spawns a child workflow and awaits the return.
|
|
34
|
-
* @template T - the result type
|
|
35
|
-
* @param {WorkflowOptions} options - the workflow options
|
|
36
|
-
* @returns {Promise<T>} - the result of the child workflow
|
|
37
|
-
* @example
|
|
38
|
-
* const result = await Durable.workflow.execChild<typeof resultType>({ ...options });
|
|
39
|
-
*/
|
|
40
11
|
static execChild<T>(options: WorkflowOptions): Promise<T>;
|
|
41
|
-
/**
|
|
42
|
-
* constructs the payload necessary to spawn a child job
|
|
43
|
-
* @private
|
|
44
|
-
*/
|
|
45
12
|
static getChildInterruptPayload(context: WorkflowContext, options: WorkflowOptions, execIndex: number): DurableChildErrorType;
|
|
46
|
-
/**
|
|
47
|
-
* Spawns a child workflow and returns the child Job ID.
|
|
48
|
-
* This method guarantees the spawned child has reserved the Job ID,
|
|
49
|
-
* returning a 'DuplicateJobError' error if not. Otherwise,
|
|
50
|
-
* this is a fire-and-forget method.
|
|
51
|
-
*
|
|
52
|
-
* @param {WorkflowOptions} options - the workflow options
|
|
53
|
-
* @returns {Promise<string>} - the childJobId
|
|
54
|
-
* @example
|
|
55
|
-
* const childJobId = await Durable.workflow.startChild({ ...options });
|
|
56
|
-
*/
|
|
57
13
|
static startChild(options: WorkflowOptions): Promise<string>;
|
|
58
|
-
/**
|
|
59
|
-
* Wraps activities in a proxy that durably runs/re-runs them to completion.
|
|
60
|
-
* TODO: verify that activities do not collide if named same on same server but bound to different workflows
|
|
61
|
-
*
|
|
62
|
-
* @param {ActivityConfig} options - the activity configuration
|
|
63
|
-
* that will be used to wrap the activities.
|
|
64
|
-
* @returns {ProxyType<ACT>} - a proxy object with the same keys as the
|
|
65
|
-
* activities object, but with the values replaced by a wrapped function
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* // import the activities
|
|
69
|
-
* import * as activities from './activities';
|
|
70
|
-
* const proxy = WorkflowService.proxyActivities<typeof activities>({ activities });
|
|
71
|
-
*
|
|
72
|
-
* //or destructure the proxy object, as the function names are the keys
|
|
73
|
-
* const { activity1, activity2 } = WorkflowService.proxyActivities<typeof activities>({ activities });
|
|
74
|
-
*/
|
|
75
14
|
static proxyActivities<ACT>(options?: ActivityConfig): ProxyType<ACT>;
|
|
76
15
|
static wrapActivity<T>(activityName: string, options?: ActivityConfig): T;
|
|
77
|
-
/**
|
|
78
|
-
* constructs the payload necessary to spawn a proxyActivity job
|
|
79
|
-
* @private
|
|
80
|
-
*/
|
|
81
16
|
static getProxyInterruptPayload(context: WorkflowContext, activityName: string, execIndex: number, args: any[], options?: ActivityConfig): DurableProxyErrorType;
|
|
82
|
-
/**
|
|
83
|
-
* Returns a search session for use when reading/writing to the workflow HASH.
|
|
84
|
-
* The search session provides access to methods like `get`, `mget`, `set`, `del`, and `incr`.
|
|
85
|
-
* @returns {Promise<Search>} - a search session
|
|
86
|
-
*/
|
|
87
17
|
static search(): Promise<Search>;
|
|
88
|
-
/**
|
|
89
|
-
* Returns a random number between 0 and 1. This number is deterministic
|
|
90
|
-
* and will never vary for a given seed. This is useful for randomizing
|
|
91
|
-
* pathways in a workflow that can be safely replayed.
|
|
92
|
-
* @returns {number} - a random number between 0 and 1
|
|
93
|
-
*/
|
|
94
18
|
static random(): number;
|
|
95
|
-
/**
|
|
96
|
-
* Sends signal data into any other paused thread (which is currently
|
|
97
|
-
* awaiting the signal)
|
|
98
|
-
* @param {string} signalId - the signal id
|
|
99
|
-
* @param {Record<any, any>} data - the signal data
|
|
100
|
-
* @returns {Promise<string>} - the stream id
|
|
101
|
-
*/
|
|
102
19
|
static signal(signalId: string, data: Record<any, any>): Promise<string>;
|
|
103
|
-
/**
|
|
104
|
-
* Spawns a hook from either the main thread or a hook thread with
|
|
105
|
-
* the provided options; worflowId/TaskQueue/Name are optional and will
|
|
106
|
-
* default to the current workflowId/WorkflowTopic if not provided
|
|
107
|
-
* @param {HookOptions} options - the hook options
|
|
108
|
-
*/
|
|
109
20
|
static hook(options: HookOptions): Promise<string>;
|
|
110
|
-
/**
|
|
111
|
-
* Executes a function once and caches the result. If the function is called
|
|
112
|
-
* again, the cached result is returned. This is useful for wrapping
|
|
113
|
-
* expensive activity calls that should only be run once, but which might
|
|
114
|
-
* not require the cost and safety provided by proxyActivities.
|
|
115
|
-
* @template T - the result type
|
|
116
|
-
*/
|
|
117
21
|
static once<T>(fn: (...args: any[]) => Promise<T>, ...args: any[]): Promise<T>;
|
|
118
|
-
/**
|
|
119
|
-
* Interrupts a running job
|
|
120
|
-
*/
|
|
121
22
|
static interrupt(jobId: string, options?: JobInterruptOptions): Promise<string | void>;
|
|
122
|
-
/**
|
|
123
|
-
* Sleeps the workflow for a duration. As the function is reentrant,
|
|
124
|
-
* upon reentry, the function will traverse prior execution paths up
|
|
125
|
-
* until the sleep command and then resume execution thereafter.
|
|
126
|
-
* @param {string} duration - See the `ms` package for syntax examples: '1 minute', '2 hours', '3 days'
|
|
127
|
-
* @returns {Promise<number>} - resolved duration in seconds
|
|
128
|
-
*/
|
|
129
23
|
static sleepFor(duration: string): Promise<number>;
|
|
130
|
-
/**
|
|
131
|
-
* Pauses the workflow until `signalId` is received.
|
|
132
|
-
* @template T - the result type
|
|
133
|
-
* @param {string} signalId - a unique, shareable guid (e.g, 'abc123')
|
|
134
|
-
* @returns {Promise<T>}
|
|
135
|
-
* @example
|
|
136
|
-
* const result = await Durable.workflow.waitFor<typeof resultType>('abc123');
|
|
137
|
-
*/
|
|
138
24
|
static waitFor<T>(signalId: string): Promise<T>;
|
|
139
25
|
}
|
|
@@ -15,13 +15,6 @@ const serializer_1 = require("../serializer");
|
|
|
15
15
|
const stream_1 = require("../../types/stream");
|
|
16
16
|
const enums_1 = require("../../modules/enums");
|
|
17
17
|
class WorkflowService {
|
|
18
|
-
/**
|
|
19
|
-
* Returns the synchronous output from the activity (replay)
|
|
20
|
-
* if available locally, revealing whether or not the activity already
|
|
21
|
-
* ran during a prior execution cycle
|
|
22
|
-
* @param {string} prefix - one of: proxy, child, start, wait etc
|
|
23
|
-
* @returns
|
|
24
|
-
*/
|
|
25
18
|
static async didRun(prefix) {
|
|
26
19
|
const { COUNTER, replay, workflowDimension, } = WorkflowService.getContext();
|
|
27
20
|
const execIndex = COUNTER.counter = COUNTER.counter + 1;
|
|
@@ -32,12 +25,6 @@ class WorkflowService {
|
|
|
32
25
|
}
|
|
33
26
|
return [false, execIndex, null];
|
|
34
27
|
}
|
|
35
|
-
/**
|
|
36
|
-
* Those methods that may only be called once must be protected by flagging
|
|
37
|
-
* their execution with a unique key (the key is stored in the HASH alongside
|
|
38
|
-
* process state and job state)
|
|
39
|
-
* @private
|
|
40
|
-
*/
|
|
41
28
|
static async isSideEffectAllowed(hotMeshClient, prefix) {
|
|
42
29
|
const store = storage_1.asyncLocalStorage.getStore();
|
|
43
30
|
const workflowId = store.get('workflowId');
|
|
@@ -57,10 +44,6 @@ class WorkflowService {
|
|
|
57
44
|
const guidValue = Number(await hotMeshClient.engine.store.exec('HINCRBYFLOAT', workflowGuid, sessionId, '1'));
|
|
58
45
|
return guidValue === 1;
|
|
59
46
|
}
|
|
60
|
-
/**
|
|
61
|
-
* Returns the current workflow context restored
|
|
62
|
-
* from Redis
|
|
63
|
-
*/
|
|
64
47
|
static getContext() {
|
|
65
48
|
const store = storage_1.asyncLocalStorage.getStore();
|
|
66
49
|
const workflowId = store.get('workflowId');
|
|
@@ -93,36 +76,21 @@ class WorkflowService {
|
|
|
93
76
|
workflowSpan,
|
|
94
77
|
};
|
|
95
78
|
}
|
|
96
|
-
/**
|
|
97
|
-
* Return a handle to the hotmesh client hosting the workflow execution
|
|
98
|
-
* @returns {Promise<HotMesh>} - a hotmesh client
|
|
99
|
-
*/
|
|
100
79
|
static async getHotMesh() {
|
|
101
80
|
const store = storage_1.asyncLocalStorage.getStore();
|
|
102
81
|
const workflowTopic = store.get('workflowTopic');
|
|
103
82
|
const namespace = store.get('namespace');
|
|
104
83
|
return await worker_1.WorkerService.getHotMesh(workflowTopic, { namespace });
|
|
105
84
|
}
|
|
106
|
-
/**
|
|
107
|
-
* Spawns a child workflow and awaits the return.
|
|
108
|
-
* @template T - the result type
|
|
109
|
-
* @param {WorkflowOptions} options - the workflow options
|
|
110
|
-
* @returns {Promise<T>} - the result of the child workflow
|
|
111
|
-
* @example
|
|
112
|
-
* const result = await Durable.workflow.execChild<typeof resultType>({ ...options });
|
|
113
|
-
*/
|
|
114
85
|
static async execChild(options) {
|
|
115
|
-
//SYNC
|
|
116
|
-
//check if the activity already ran (check $error/done)
|
|
117
86
|
const isStartChild = options.await === false;
|
|
118
87
|
const prefix = isStartChild ? 'start' : 'child';
|
|
119
88
|
const [didRun, execIndex, result] = await WorkflowService.didRun(prefix);
|
|
120
|
-
const context =
|
|
89
|
+
const context = WorkflowService.getContext();
|
|
121
90
|
const { canRetry, interruptionRegistry } = context;
|
|
122
91
|
if (didRun) {
|
|
123
92
|
if (result?.$error && (!result.$error.is_stream_error || (result.$error.is_stream_error && !canRetry))) {
|
|
124
93
|
if (options?.config?.throwOnError !== false) {
|
|
125
|
-
//rethrow remote execution error (simulates local failure)
|
|
126
94
|
const code = result.$error.code;
|
|
127
95
|
const message = result.$error.message;
|
|
128
96
|
const stack = result.$error.stack;
|
|
@@ -145,21 +113,14 @@ class WorkflowService {
|
|
|
145
113
|
return result.data;
|
|
146
114
|
}
|
|
147
115
|
}
|
|
148
|
-
const interruptionMessage =
|
|
149
|
-
//push the packaged inputs to the registry
|
|
116
|
+
const interruptionMessage = WorkflowService.getChildInterruptPayload(context, options, execIndex);
|
|
150
117
|
interruptionRegistry.push({
|
|
151
118
|
code: enums_1.HMSH_CODE_DURABLE_CHILD,
|
|
152
119
|
...interruptionMessage,
|
|
153
120
|
});
|
|
154
|
-
//ASYNC
|
|
155
|
-
//sleep (allow others to be packaged / registered) and throw the error
|
|
156
121
|
await (0, utils_1.sleepFor)(0);
|
|
157
122
|
throw new errors_1.DurableChildError(interruptionMessage);
|
|
158
123
|
}
|
|
159
|
-
/**
|
|
160
|
-
* constructs the payload necessary to spawn a child job
|
|
161
|
-
* @private
|
|
162
|
-
*/
|
|
163
124
|
static getChildInterruptPayload(context, options, execIndex) {
|
|
164
125
|
const { workflowId, originJobId, workflowDimension } = context;
|
|
165
126
|
let childJobId;
|
|
@@ -190,37 +151,9 @@ class WorkflowService {
|
|
|
190
151
|
workflowTopic,
|
|
191
152
|
};
|
|
192
153
|
}
|
|
193
|
-
/**
|
|
194
|
-
* Spawns a child workflow and returns the child Job ID.
|
|
195
|
-
* This method guarantees the spawned child has reserved the Job ID,
|
|
196
|
-
* returning a 'DuplicateJobError' error if not. Otherwise,
|
|
197
|
-
* this is a fire-and-forget method.
|
|
198
|
-
*
|
|
199
|
-
* @param {WorkflowOptions} options - the workflow options
|
|
200
|
-
* @returns {Promise<string>} - the childJobId
|
|
201
|
-
* @example
|
|
202
|
-
* const childJobId = await Durable.workflow.startChild({ ...options });
|
|
203
|
-
*/
|
|
204
154
|
static async startChild(options) {
|
|
205
|
-
return
|
|
155
|
+
return WorkflowService.execChild({ ...options, await: false });
|
|
206
156
|
}
|
|
207
|
-
/**
|
|
208
|
-
* Wraps activities in a proxy that durably runs/re-runs them to completion.
|
|
209
|
-
* TODO: verify that activities do not collide if named same on same server but bound to different workflows
|
|
210
|
-
*
|
|
211
|
-
* @param {ActivityConfig} options - the activity configuration
|
|
212
|
-
* that will be used to wrap the activities.
|
|
213
|
-
* @returns {ProxyType<ACT>} - a proxy object with the same keys as the
|
|
214
|
-
* activities object, but with the values replaced by a wrapped function
|
|
215
|
-
*
|
|
216
|
-
* @example
|
|
217
|
-
* // import the activities
|
|
218
|
-
* import * as activities from './activities';
|
|
219
|
-
* const proxy = WorkflowService.proxyActivities<typeof activities>({ activities });
|
|
220
|
-
*
|
|
221
|
-
* //or destructure the proxy object, as the function names are the keys
|
|
222
|
-
* const { activity1, activity2 } = WorkflowService.proxyActivities<typeof activities>({ activities });
|
|
223
|
-
*/
|
|
224
157
|
static proxyActivities(options) {
|
|
225
158
|
if (options.activities) {
|
|
226
159
|
worker_1.WorkerService.registerActivities(options.activities);
|
|
@@ -237,13 +170,10 @@ class WorkflowService {
|
|
|
237
170
|
}
|
|
238
171
|
static wrapActivity(activityName, options) {
|
|
239
172
|
return async function () {
|
|
240
|
-
//SYNC
|
|
241
|
-
//check if the activity already ran
|
|
242
173
|
const [didRun, execIndex, result] = await WorkflowService.didRun('proxy');
|
|
243
174
|
if (didRun) {
|
|
244
175
|
if (result?.$error) {
|
|
245
176
|
if (options?.retryPolicy?.throwOnError !== false) {
|
|
246
|
-
//rethrow remote execution error (simulates throw)
|
|
247
177
|
const code = result.$error.code;
|
|
248
178
|
const message = result.$error.message;
|
|
249
179
|
const stack = result.$error.stack;
|
|
@@ -261,25 +191,17 @@ class WorkflowService {
|
|
|
261
191
|
}
|
|
262
192
|
return result.data;
|
|
263
193
|
}
|
|
264
|
-
//package the interruption inputs
|
|
265
194
|
const context = WorkflowService.getContext();
|
|
266
195
|
const { interruptionRegistry } = context;
|
|
267
196
|
const interruptionMessage = WorkflowService.getProxyInterruptPayload(context, activityName, execIndex, Array.from(arguments), options);
|
|
268
|
-
//push the packaged inputs to the registry
|
|
269
197
|
interruptionRegistry.push({
|
|
270
198
|
code: enums_1.HMSH_CODE_DURABLE_PROXY,
|
|
271
199
|
...interruptionMessage,
|
|
272
200
|
});
|
|
273
|
-
//ASYNC
|
|
274
|
-
//sleep (allow others to be packaged / registered) and throw the error
|
|
275
201
|
await (0, utils_1.sleepFor)(0);
|
|
276
202
|
throw new errors_1.DurableProxyError(interruptionMessage);
|
|
277
203
|
};
|
|
278
204
|
}
|
|
279
|
-
/**
|
|
280
|
-
* constructs the payload necessary to spawn a proxyActivity job
|
|
281
|
-
* @private
|
|
282
|
-
*/
|
|
283
205
|
static getProxyInterruptPayload(context, activityName, execIndex, args, options) {
|
|
284
206
|
const { workflowDimension, workflowId, originJobId, workflowTopic } = context;
|
|
285
207
|
const activityTopic = `${workflowTopic}-activity`;
|
|
@@ -302,11 +224,6 @@ class WorkflowService {
|
|
|
302
224
|
maximumInterval: maximumInterval ?? undefined,
|
|
303
225
|
};
|
|
304
226
|
}
|
|
305
|
-
/**
|
|
306
|
-
* Returns a search session for use when reading/writing to the workflow HASH.
|
|
307
|
-
* The search session provides access to methods like `get`, `mget`, `set`, `del`, and `incr`.
|
|
308
|
-
* @returns {Promise<Search>} - a search session
|
|
309
|
-
*/
|
|
310
227
|
static async search() {
|
|
311
228
|
const store = storage_1.asyncLocalStorage.getStore();
|
|
312
229
|
const workflowId = store.get('workflowId');
|
|
@@ -316,29 +233,15 @@ class WorkflowService {
|
|
|
316
233
|
const COUNTER = store.get('counter');
|
|
317
234
|
const execIndex = COUNTER.counter = COUNTER.counter + 1;
|
|
318
235
|
const hotMeshClient = await worker_1.WorkerService.getHotMesh(workflowTopic, { namespace });
|
|
319
|
-
//this ID is used as a item key with a hash (dash prefix ensures no collision)
|
|
320
236
|
const searchSessionId = `-search${workflowDimension}-${execIndex}`;
|
|
321
237
|
return new search_1.Search(workflowId, hotMeshClient, searchSessionId);
|
|
322
238
|
}
|
|
323
|
-
/**
|
|
324
|
-
* Returns a random number between 0 and 1. This number is deterministic
|
|
325
|
-
* and will never vary for a given seed. This is useful for randomizing
|
|
326
|
-
* pathways in a workflow that can be safely replayed.
|
|
327
|
-
* @returns {number} - a random number between 0 and 1
|
|
328
|
-
*/
|
|
329
239
|
static random() {
|
|
330
240
|
const store = storage_1.asyncLocalStorage.getStore();
|
|
331
241
|
const COUNTER = store.get('counter');
|
|
332
242
|
const seed = COUNTER.counter = COUNTER.counter + 1;
|
|
333
243
|
return (0, utils_1.deterministicRandom)(seed);
|
|
334
244
|
}
|
|
335
|
-
/**
|
|
336
|
-
* Sends signal data into any other paused thread (which is currently
|
|
337
|
-
* awaiting the signal)
|
|
338
|
-
* @param {string} signalId - the signal id
|
|
339
|
-
* @param {Record<any, any>} data - the signal data
|
|
340
|
-
* @returns {Promise<string>} - the stream id
|
|
341
|
-
*/
|
|
342
245
|
static async signal(signalId, data) {
|
|
343
246
|
const store = storage_1.asyncLocalStorage.getStore();
|
|
344
247
|
const workflowTopic = store.get('workflowTopic');
|
|
@@ -348,12 +251,6 @@ class WorkflowService {
|
|
|
348
251
|
return await hotMeshClient.hook(`${namespace}.wfs.signal`, { id: signalId, data });
|
|
349
252
|
}
|
|
350
253
|
}
|
|
351
|
-
/**
|
|
352
|
-
* Spawns a hook from either the main thread or a hook thread with
|
|
353
|
-
* the provided options; worflowId/TaskQueue/Name are optional and will
|
|
354
|
-
* default to the current workflowId/WorkflowTopic if not provided
|
|
355
|
-
* @param {HookOptions} options - the hook options
|
|
356
|
-
*/
|
|
357
254
|
static async hook(options) {
|
|
358
255
|
const { workflowId, namespace, workflowTopic, } = WorkflowService.getContext();
|
|
359
256
|
const hotMeshClient = await worker_1.WorkerService.getHotMesh(workflowTopic, { namespace });
|
|
@@ -375,13 +272,6 @@ class WorkflowService {
|
|
|
375
272
|
return await hotMeshClient.hook(`${namespace}.flow.signal`, payload, stream_1.StreamStatus.PENDING, 202);
|
|
376
273
|
}
|
|
377
274
|
}
|
|
378
|
-
/**
|
|
379
|
-
* Executes a function once and caches the result. If the function is called
|
|
380
|
-
* again, the cached result is returned. This is useful for wrapping
|
|
381
|
-
* expensive activity calls that should only be run once, but which might
|
|
382
|
-
* not require the cost and safety provided by proxyActivities.
|
|
383
|
-
* @template T - the result type
|
|
384
|
-
*/
|
|
385
275
|
static async once(fn, ...args) {
|
|
386
276
|
const { COUNTER, namespace, workflowId, workflowTopic, workflowDimension, replay, } = WorkflowService.getContext();
|
|
387
277
|
const execIndex = COUNTER.counter = COUNTER.counter + 1;
|
|
@@ -406,9 +296,6 @@ class WorkflowService {
|
|
|
406
296
|
await hotMeshClient.engine.store.exec('HSET', workflowGuid, sessionId, serializer_1.SerializerService.toString(payload));
|
|
407
297
|
return response;
|
|
408
298
|
}
|
|
409
|
-
/**
|
|
410
|
-
* Interrupts a running job
|
|
411
|
-
*/
|
|
412
299
|
static async interrupt(jobId, options = {}) {
|
|
413
300
|
const { workflowTopic, namespace } = WorkflowService.getContext();
|
|
414
301
|
const hotMeshClient = await worker_1.WorkerService.getHotMesh(workflowTopic, { namespace });
|
|
@@ -416,21 +303,11 @@ class WorkflowService {
|
|
|
416
303
|
return await hotMeshClient.interrupt(`${hotMeshClient.appId}.execute`, jobId, options);
|
|
417
304
|
}
|
|
418
305
|
}
|
|
419
|
-
/**
|
|
420
|
-
* Sleeps the workflow for a duration. As the function is reentrant,
|
|
421
|
-
* upon reentry, the function will traverse prior execution paths up
|
|
422
|
-
* until the sleep command and then resume execution thereafter.
|
|
423
|
-
* @param {string} duration - See the `ms` package for syntax examples: '1 minute', '2 hours', '3 days'
|
|
424
|
-
* @returns {Promise<number>} - resolved duration in seconds
|
|
425
|
-
*/
|
|
426
306
|
static async sleepFor(duration) {
|
|
427
|
-
//SYNC
|
|
428
|
-
//return early if this sleep command has already run
|
|
429
307
|
const [didRun, execIndex, result] = await WorkflowService.didRun('sleep');
|
|
430
308
|
if (didRun) {
|
|
431
|
-
return result.duration;
|
|
309
|
+
return result.duration;
|
|
432
310
|
}
|
|
433
|
-
//package the interruption inputs
|
|
434
311
|
const store = storage_1.asyncLocalStorage.getStore();
|
|
435
312
|
const interruptionRegistry = store.get('interruptionRegistry');
|
|
436
313
|
const workflowId = store.get('workflowId');
|
|
@@ -445,28 +322,14 @@ class WorkflowService {
|
|
|
445
322
|
code: enums_1.HMSH_CODE_DURABLE_SLEEP,
|
|
446
323
|
...interruptionMessage,
|
|
447
324
|
});
|
|
448
|
-
//ASYNC
|
|
449
|
-
//sleep to allow other interruptions to be packaged and registered
|
|
450
325
|
await (0, utils_1.sleepFor)(0);
|
|
451
|
-
// NOTE: If you are reading this in the stack trace, await `sleepFor`
|
|
452
326
|
throw new errors_1.DurableSleepError(interruptionMessage);
|
|
453
327
|
}
|
|
454
|
-
/**
|
|
455
|
-
* Pauses the workflow until `signalId` is received.
|
|
456
|
-
* @template T - the result type
|
|
457
|
-
* @param {string} signalId - a unique, shareable guid (e.g, 'abc123')
|
|
458
|
-
* @returns {Promise<T>}
|
|
459
|
-
* @example
|
|
460
|
-
* const result = await Durable.workflow.waitFor<typeof resultType>('abc123');
|
|
461
|
-
*/
|
|
462
328
|
static async waitFor(signalId) {
|
|
463
|
-
//SYNC
|
|
464
|
-
//return early if this waitFor command has already run
|
|
465
329
|
const [didRun, execIndex, result] = await WorkflowService.didRun('wait');
|
|
466
330
|
if (didRun) {
|
|
467
331
|
return result.data.data;
|
|
468
332
|
}
|
|
469
|
-
//package the interruption inputs
|
|
470
333
|
const store = storage_1.asyncLocalStorage.getStore();
|
|
471
334
|
const interruptionRegistry = store.get('interruptionRegistry');
|
|
472
335
|
const workflowId = store.get('workflowId');
|
|
@@ -481,10 +344,7 @@ class WorkflowService {
|
|
|
481
344
|
code: enums_1.HMSH_CODE_DURABLE_WAIT,
|
|
482
345
|
...interruptionMessage,
|
|
483
346
|
});
|
|
484
|
-
//ASYNC
|
|
485
|
-
//sleep to allow other interruptions to be packaged and registered
|
|
486
347
|
await (0, utils_1.sleepFor)(0);
|
|
487
|
-
// NOTE: If you are reading this in the stack trace, await `waitFor`
|
|
488
348
|
throw new errors_1.DurableWaitForError(interruptionMessage);
|
|
489
349
|
}
|
|
490
350
|
}
|
|
@@ -15,6 +15,7 @@ import { TaskService } from '../task';
|
|
|
15
15
|
import { AppVID } from '../../types/app';
|
|
16
16
|
import { ActivityType } from '../../types/activity';
|
|
17
17
|
import { CacheMode } from '../../types/cache';
|
|
18
|
+
import { JobExport } from '../../types/exporter';
|
|
18
19
|
import { JobState, JobData, JobMetadata, JobOutput, JobStatus, JobInterruptOptions, JobCompletionOptions, ExtensionType } from '../../types/job';
|
|
19
20
|
import { HotMeshApps, HotMeshConfig, HotMeshManifest, HotMeshSettings } from '../../types/hotmesh';
|
|
20
21
|
import { JobMessageCallback } from '../../types/quorum';
|
|
@@ -23,7 +24,6 @@ import { StringAnyType, StringStringType } from '../../types/serializer';
|
|
|
23
24
|
import { GetStatsOptions, IdsResponse, JobStatsInput, StatsResponse } from '../../types/stats';
|
|
24
25
|
import { StreamCode, StreamData, StreamDataResponse, StreamError, StreamStatus } from '../../types/stream';
|
|
25
26
|
import { WorkListTaskType } from '../../types/task';
|
|
26
|
-
import { JobExport } from '../../types/exporter';
|
|
27
27
|
declare class EngineService {
|
|
28
28
|
namespace: string;
|
|
29
29
|
apps: HotMeshApps | null;
|
|
@@ -86,11 +86,6 @@ declare class EngineService {
|
|
|
86
86
|
delistJobCallback(jobId: string): void;
|
|
87
87
|
hasOneTimeSubscription(context: JobState): boolean;
|
|
88
88
|
runJobCompletionTasks(context: JobState, options?: JobCompletionOptions): Promise<string | void>;
|
|
89
|
-
/**
|
|
90
|
-
* Job hash expiration is typically reliant on the metadata field
|
|
91
|
-
* if the activity concludes normally. However, if the job is `interrupted`,
|
|
92
|
-
* it will be expired immediately.
|
|
93
|
-
*/
|
|
94
89
|
resolveExpires(context: JobState, options: JobCompletionOptions): number;
|
|
95
90
|
export(jobId: string): Promise<JobExport>;
|
|
96
91
|
getRaw(jobId: string): Promise<StringStringType>;
|