@hotmeshio/hotmesh 0.0.53 → 0.0.55
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/errors.d.ts +6 -48
- package/build/modules/errors.js +5 -5
- package/build/package.json +1 -1
- package/build/services/activities/hook.js +5 -1
- package/build/services/activities/trigger.d.ts +5 -2
- package/build/services/activities/trigger.js +22 -1
- package/build/services/durable/client.js +1 -8
- package/build/services/durable/exporter.d.ts +24 -13
- package/build/services/durable/exporter.js +145 -127
- package/build/services/durable/handle.d.ts +2 -2
- package/build/services/durable/handle.js +2 -2
- package/build/services/durable/worker.js +1 -1
- package/build/services/durable/workflow.d.ts +29 -17
- package/build/services/durable/workflow.js +117 -97
- package/build/services/engine/index.d.ts +2 -2
- package/build/services/engine/index.js +2 -2
- package/build/services/hotmesh/index.d.ts +2 -2
- package/build/services/hotmesh/index.js +2 -2
- package/build/types/durable.d.ts +15 -3
- package/build/types/error.d.ts +48 -0
- package/build/types/error.js +2 -0
- package/build/types/exporter.d.ts +26 -20
- package/build/types/index.d.ts +2 -1
- package/build/types/job.d.ts +24 -1
- package/modules/errors.ts +18 -55
- package/package.json +1 -1
- package/services/activities/hook.ts +8 -1
- package/services/activities/trigger.ts +27 -2
- package/services/durable/client.ts +2 -8
- package/services/durable/exporter.ts +149 -128
- package/services/durable/handle.ts +3 -3
- package/services/durable/worker.ts +1 -1
- package/services/durable/workflow.ts +137 -104
- package/services/engine/index.ts +4 -3
- package/services/hotmesh/index.ts +4 -3
- package/types/durable.ts +18 -3
- package/types/error.ts +52 -0
- package/types/exporter.ts +31 -23
- package/types/index.ts +8 -1
- package/types/job.ts +27 -0
|
@@ -14,10 +14,12 @@ import { asyncLocalStorage } from '../../modules/storage';
|
|
|
14
14
|
import {
|
|
15
15
|
deterministicRandom,
|
|
16
16
|
formatISODate,
|
|
17
|
+
guid,
|
|
17
18
|
sleepFor } from '../../modules/utils';
|
|
18
19
|
import { Search } from './search';
|
|
19
20
|
import { WorkerService } from './worker';
|
|
20
21
|
import { HotMeshService as HotMesh } from '../hotmesh';
|
|
22
|
+
import { SerializerService } from '../serializer';
|
|
21
23
|
import {
|
|
22
24
|
ActivityConfig,
|
|
23
25
|
ChildResponseType,
|
|
@@ -28,8 +30,8 @@ import {
|
|
|
28
30
|
WorkflowOptions
|
|
29
31
|
} from "../../types/durable";
|
|
30
32
|
import { JobInterruptOptions } from '../../types/job';
|
|
31
|
-
import { StreamCode,
|
|
32
|
-
import { StringStringType } from '../../types';
|
|
33
|
+
import { StreamCode, StreamStatus } from '../../types/stream';
|
|
34
|
+
import { StringStringType } from '../../types/serializer';
|
|
33
35
|
import {
|
|
34
36
|
HMSH_CODE_DURABLE_CHILD,
|
|
35
37
|
HMSH_CODE_DURABLE_FATAL,
|
|
@@ -41,19 +43,62 @@ import {
|
|
|
41
43
|
HMSH_DURABLE_EXP_BACKOFF,
|
|
42
44
|
HMSH_DURABLE_MAX_ATTEMPTS,
|
|
43
45
|
HMSH_DURABLE_MAX_INTERVAL} from '../../modules/enums';
|
|
44
|
-
import {
|
|
46
|
+
import { DurableChildErrorType, DurableProxyErrorType } from '../../types/error';
|
|
45
47
|
|
|
46
48
|
export class WorkflowService {
|
|
47
49
|
|
|
48
50
|
/**
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
+
* Returns the synchronous output from the activity (replay)
|
|
52
|
+
* if available locally, revealing whether or not the activity already
|
|
53
|
+
* ran during a prior execution cycle
|
|
54
|
+
* @param {string} prefix - one of: proxy, child, start, wait etc
|
|
55
|
+
* @returns
|
|
51
56
|
*/
|
|
52
|
-
static async
|
|
57
|
+
static async didRun(prefix: string): Promise<[boolean, number, any]> {
|
|
58
|
+
const {
|
|
59
|
+
COUNTER,
|
|
60
|
+
replay,
|
|
61
|
+
workflowDimension,
|
|
62
|
+
} = WorkflowService.getContext();
|
|
63
|
+
const execIndex = COUNTER.counter = COUNTER.counter + 1;
|
|
64
|
+
const sessionId = `-${prefix}${workflowDimension}-${execIndex}-`;
|
|
65
|
+
if (sessionId in replay) {
|
|
66
|
+
const restored = SerializerService.fromString(replay[sessionId]);
|
|
67
|
+
return [true, execIndex, restored];
|
|
68
|
+
}
|
|
69
|
+
return [false, execIndex, null];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Those methods that may only be called once must be protected by flagging
|
|
74
|
+
* their execution with a unique key (the key is stored in the HASH alongside
|
|
75
|
+
* process state and job state)
|
|
76
|
+
* @private
|
|
77
|
+
*/
|
|
78
|
+
static async isSideEffectAllowed(hotMeshClient: HotMesh, prefix: string): Promise<boolean> {
|
|
53
79
|
const store = asyncLocalStorage.getStore();
|
|
54
|
-
const
|
|
55
|
-
const
|
|
56
|
-
|
|
80
|
+
const workflowId = store.get('workflowId');
|
|
81
|
+
const workflowDimension = store.get('workflowDimension') ?? '';
|
|
82
|
+
const COUNTER = store.get('counter');
|
|
83
|
+
const execIndex = COUNTER.counter = COUNTER.counter + 1;
|
|
84
|
+
const sessionId = `-${prefix}${workflowDimension}-${execIndex}-`;
|
|
85
|
+
const replay = store.get('replay') as StringStringType;
|
|
86
|
+
if (sessionId in replay) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
const keyParams = {
|
|
90
|
+
appId: hotMeshClient.appId,
|
|
91
|
+
jobId: workflowId
|
|
92
|
+
}
|
|
93
|
+
const workflowGuid = KeyService.mintKey(hotMeshClient.namespace, KeyType.JOB_STATE, keyParams);
|
|
94
|
+
const guidValue = Number(
|
|
95
|
+
await hotMeshClient.engine.store.exec(
|
|
96
|
+
'HINCRBYFLOAT',
|
|
97
|
+
workflowGuid,
|
|
98
|
+
sessionId,
|
|
99
|
+
'1') as string
|
|
100
|
+
);
|
|
101
|
+
return guidValue === 1;
|
|
57
102
|
}
|
|
58
103
|
|
|
59
104
|
/**
|
|
@@ -65,9 +110,11 @@ export class WorkflowService {
|
|
|
65
110
|
const workflowId = store.get('workflowId');
|
|
66
111
|
const replay = store.get('replay');
|
|
67
112
|
const cursor = store.get('cursor');
|
|
113
|
+
const interruptionRegistry = store.get('interruptionRegistry');
|
|
68
114
|
const workflowDimension = store.get('workflowDimension') ?? '';
|
|
69
115
|
const workflowTopic = store.get('workflowTopic');
|
|
70
116
|
const namespace = store.get('namespace');
|
|
117
|
+
const originJobId = store.get('originJobId');
|
|
71
118
|
const workflowTrace = store.get('workflowTrace');
|
|
72
119
|
const canRetry = store.get('canRetry');
|
|
73
120
|
const workflowSpan = store.get('workflowSpan');
|
|
@@ -78,7 +125,9 @@ export class WorkflowService {
|
|
|
78
125
|
COUNTER,
|
|
79
126
|
counter: COUNTER.counter,
|
|
80
127
|
cursor,
|
|
128
|
+
interruptionRegistry,
|
|
81
129
|
namespace,
|
|
130
|
+
originJobId,
|
|
82
131
|
raw,
|
|
83
132
|
replay,
|
|
84
133
|
workflowId,
|
|
@@ -89,6 +138,17 @@ export class WorkflowService {
|
|
|
89
138
|
};
|
|
90
139
|
}
|
|
91
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Return a handle to the hotmesh client hosting the workflow execution
|
|
143
|
+
* @returns {Promise<HotMesh>} - a hotmesh client
|
|
144
|
+
*/
|
|
145
|
+
static async getHotMesh(): Promise<HotMesh> {
|
|
146
|
+
const store = asyncLocalStorage.getStore();
|
|
147
|
+
const workflowTopic = store.get('workflowTopic');
|
|
148
|
+
const namespace = store.get('namespace');
|
|
149
|
+
return await WorkerService.getHotMesh(workflowTopic, { namespace });
|
|
150
|
+
}
|
|
151
|
+
|
|
92
152
|
/**
|
|
93
153
|
* Spawns a child workflow and awaits the return.
|
|
94
154
|
* @template T - the result type
|
|
@@ -101,14 +161,15 @@ export class WorkflowService {
|
|
|
101
161
|
//SYNC
|
|
102
162
|
//check if the activity already ran (check $error/done)
|
|
103
163
|
const isStartChild = options.await === false;
|
|
104
|
-
const
|
|
105
|
-
const
|
|
106
|
-
const
|
|
164
|
+
const prefix = isStartChild ? 'start' : 'child';
|
|
165
|
+
const [didRun, execIndex, result]: [boolean, number, ChildResponseType<T>] = await WorkflowService.didRun(prefix);
|
|
166
|
+
const context = WorkflowService.getContext();
|
|
167
|
+
const { canRetry, interruptionRegistry } = context;
|
|
107
168
|
|
|
108
169
|
if (didRun) {
|
|
109
170
|
if (result?.$error && (!result.$error.is_stream_error || (result.$error.is_stream_error && !canRetry))) {
|
|
110
171
|
if (options?.config?.throwOnError !== false) {
|
|
111
|
-
//rethrow remote execution error (simulates
|
|
172
|
+
//rethrow remote execution error (simulates local failure)
|
|
112
173
|
const code: StreamCode = result.$error.code;
|
|
113
174
|
const message = result.$error.message;
|
|
114
175
|
const stack = result.$error.stack;
|
|
@@ -127,18 +188,36 @@ export class WorkflowService {
|
|
|
127
188
|
return result.data as T;
|
|
128
189
|
}
|
|
129
190
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
191
|
+
const interruptionMessage = WorkflowService.getChildInterruptPayload(context, options, execIndex);
|
|
192
|
+
//push the packaged inputs to the registry
|
|
193
|
+
interruptionRegistry.push({
|
|
194
|
+
code: HMSH_CODE_DURABLE_CHILD,
|
|
195
|
+
...interruptionMessage,
|
|
196
|
+
});
|
|
197
|
+
//ASYNC
|
|
198
|
+
//sleep (allow others to be packaged / registered) and throw the error
|
|
199
|
+
await sleepFor(0);
|
|
200
|
+
throw new DurableChildError(interruptionMessage );
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* constructs the payload necessary to spawn a child job
|
|
205
|
+
* @private
|
|
206
|
+
*/
|
|
207
|
+
static getChildInterruptPayload(context: WorkflowContext, options: WorkflowOptions, execIndex: number): DurableChildErrorType {
|
|
208
|
+
const { workflowId, originJobId, workflowDimension } = context; let childJobId: string;
|
|
209
|
+
if (options.workflowId) {
|
|
210
|
+
childJobId = options.workflowId;
|
|
211
|
+
} else if (options.entity) {
|
|
212
|
+
childJobId = `${options.entity}-${workflowId.substring(0, 7)}-${guid()}-${workflowDimension}-${execIndex}`;
|
|
213
|
+
} else {
|
|
214
|
+
childJobId = `-${options.workflowName}-${guid()}-${workflowDimension}-${execIndex}`;
|
|
215
|
+
}
|
|
137
216
|
const parentWorkflowId = workflowId;
|
|
138
217
|
const taskQueueName = options.entity ?? options.taskQueue;
|
|
139
218
|
const workflowName = options.entity ?? options.workflowName;
|
|
140
219
|
const workflowTopic = `${taskQueueName}-${workflowName}`;
|
|
141
|
-
|
|
220
|
+
return {
|
|
142
221
|
arguments: [...(options.args || [])],
|
|
143
222
|
await: options?.await ?? true,
|
|
144
223
|
backoffCoefficient: options?.config?.backoffCoefficient ?? HMSH_DURABLE_EXP_BACKOFF,
|
|
@@ -151,15 +230,6 @@ export class WorkflowService {
|
|
|
151
230
|
workflowId: childJobId,
|
|
152
231
|
workflowTopic,
|
|
153
232
|
};
|
|
154
|
-
//push the packaged inputs to the registry
|
|
155
|
-
interruptionRegistry.push({
|
|
156
|
-
code: HMSH_CODE_DURABLE_CHILD,
|
|
157
|
-
...interruptionMessage,
|
|
158
|
-
});
|
|
159
|
-
//ASYNC
|
|
160
|
-
//sleep (allow others to be packaged / registered) and throw the error
|
|
161
|
-
await sleepFor(0);
|
|
162
|
-
throw new DurableChildError(interruptionMessage);
|
|
163
233
|
}
|
|
164
234
|
|
|
165
235
|
/**
|
|
@@ -174,7 +244,7 @@ export class WorkflowService {
|
|
|
174
244
|
* const childJobId = await Durable.workflow.startChild({ ...options });
|
|
175
245
|
*/
|
|
176
246
|
static async startChild(options: WorkflowOptions): Promise<string> {
|
|
177
|
-
return
|
|
247
|
+
return WorkflowService.execChild({ ...options, await: false });
|
|
178
248
|
}
|
|
179
249
|
|
|
180
250
|
/**
|
|
@@ -234,31 +304,15 @@ export class WorkflowService {
|
|
|
234
304
|
return result.data as T;
|
|
235
305
|
}
|
|
236
306
|
//package the interruption inputs
|
|
237
|
-
const
|
|
238
|
-
const interruptionRegistry =
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
const originJobId = store.get('originJobId');
|
|
242
|
-
const workflowTopic = store.get('workflowTopic');
|
|
243
|
-
const activityTopic = `${workflowTopic}-activity`;
|
|
244
|
-
const activityJobId = `-${workflowId}-$${activityName}${workflowDimension}-${execIndex}`;
|
|
245
|
-
let maximumInterval: number;
|
|
246
|
-
if (options.retryPolicy?.maximumInterval) {
|
|
247
|
-
maximumInterval = ms(options.retryPolicy.maximumInterval) / 1000;
|
|
248
|
-
}
|
|
249
|
-
const interruptionMessage = {
|
|
250
|
-
arguments: Array.from(arguments),
|
|
251
|
-
workflowDimension: workflowDimension,
|
|
252
|
-
index: execIndex,
|
|
253
|
-
originJobId: originJobId || workflowId,
|
|
254
|
-
parentWorkflowId: workflowId,
|
|
255
|
-
workflowId: activityJobId,
|
|
256
|
-
workflowTopic: activityTopic,
|
|
307
|
+
const context = WorkflowService.getContext();
|
|
308
|
+
const { interruptionRegistry } = context;
|
|
309
|
+
const interruptionMessage = WorkflowService.getProxyInterruptPayload(
|
|
310
|
+
context,
|
|
257
311
|
activityName,
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
312
|
+
execIndex,
|
|
313
|
+
Array.from(arguments),
|
|
314
|
+
options
|
|
315
|
+
);
|
|
262
316
|
//push the packaged inputs to the registry
|
|
263
317
|
interruptionRegistry.push({
|
|
264
318
|
code: HMSH_CODE_DURABLE_PROXY,
|
|
@@ -271,6 +325,33 @@ export class WorkflowService {
|
|
|
271
325
|
} as T;
|
|
272
326
|
}
|
|
273
327
|
|
|
328
|
+
/**
|
|
329
|
+
* constructs the payload necessary to spawn a proxyActivity job
|
|
330
|
+
* @private
|
|
331
|
+
*/
|
|
332
|
+
static getProxyInterruptPayload(context: WorkflowContext, activityName: string, execIndex: number, args: any[], options?: ActivityConfig): DurableProxyErrorType {
|
|
333
|
+
const { workflowDimension, workflowId, originJobId, workflowTopic } = context;
|
|
334
|
+
const activityTopic = `${workflowTopic}-activity`;
|
|
335
|
+
const activityJobId = `-${workflowId}-$${activityName}${workflowDimension}-${execIndex}`;
|
|
336
|
+
let maximumInterval: number;
|
|
337
|
+
if (options.retryPolicy?.maximumInterval) {
|
|
338
|
+
maximumInterval = ms(options.retryPolicy.maximumInterval) / 1000;
|
|
339
|
+
}
|
|
340
|
+
return {
|
|
341
|
+
arguments: args,
|
|
342
|
+
workflowDimension: workflowDimension,
|
|
343
|
+
index: execIndex,
|
|
344
|
+
originJobId: originJobId || workflowId,
|
|
345
|
+
parentWorkflowId: workflowId,
|
|
346
|
+
workflowId: activityJobId,
|
|
347
|
+
workflowTopic: activityTopic,
|
|
348
|
+
activityName,
|
|
349
|
+
backoffCoefficient: options?.retryPolicy?.backoffCoefficient ?? undefined,
|
|
350
|
+
maximumAttempts: options?.retryPolicy?.maximumAttempts ?? undefined,
|
|
351
|
+
maximumInterval: maximumInterval ?? undefined,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
|
|
274
355
|
/**
|
|
275
356
|
* Returns a search session for use when reading/writing to the workflow HASH.
|
|
276
357
|
* The search session provides access to methods like `get`, `mget`, `set`, `del`, and `incr`.
|
|
@@ -290,52 +371,6 @@ export class WorkflowService {
|
|
|
290
371
|
return new Search(workflowId, hotMeshClient, searchSessionId);
|
|
291
372
|
}
|
|
292
373
|
|
|
293
|
-
/**
|
|
294
|
-
* Returns the synchronous output from the activity (replay)
|
|
295
|
-
* if available locally
|
|
296
|
-
* @param {string} prefix - one of: proxy, child, start, wait etc
|
|
297
|
-
* @returns
|
|
298
|
-
*/
|
|
299
|
-
static async didRun(prefix: string): Promise<[boolean, number, any]> {
|
|
300
|
-
const {
|
|
301
|
-
COUNTER,
|
|
302
|
-
replay,
|
|
303
|
-
workflowDimension,
|
|
304
|
-
} = WorkflowService.getContext();
|
|
305
|
-
const execIndex = COUNTER.counter = COUNTER.counter + 1;
|
|
306
|
-
const sessionId = `-${prefix}${workflowDimension}-${execIndex}-`;
|
|
307
|
-
if (sessionId in replay) {
|
|
308
|
-
return [true, execIndex, SerializerService.fromString(replay[sessionId])];
|
|
309
|
-
}
|
|
310
|
-
return [false, execIndex, null];
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
/**
|
|
314
|
-
* Those methods that may only be called once must be protected by flagging
|
|
315
|
-
* their execution with a unique key (the key is stored in the HASH alongside
|
|
316
|
-
* process state and job state)
|
|
317
|
-
* @private
|
|
318
|
-
*/
|
|
319
|
-
static async isSideEffectAllowed(hotMeshClient: HotMesh, prefix: string): Promise<boolean> {
|
|
320
|
-
const store = asyncLocalStorage.getStore();
|
|
321
|
-
const workflowId = store.get('workflowId');
|
|
322
|
-
const workflowDimension = store.get('workflowDimension') ?? '';
|
|
323
|
-
const COUNTER = store.get('counter');
|
|
324
|
-
const execIndex = COUNTER.counter = COUNTER.counter + 1;
|
|
325
|
-
const sessionId = `-${prefix}${workflowDimension}-${execIndex}-`;
|
|
326
|
-
const replay = store.get('replay') as StringStringType;
|
|
327
|
-
if (sessionId in replay) {
|
|
328
|
-
return false;
|
|
329
|
-
}
|
|
330
|
-
const keyParams = {
|
|
331
|
-
appId: hotMeshClient.appId,
|
|
332
|
-
jobId: workflowId
|
|
333
|
-
}
|
|
334
|
-
const workflowGuid = KeyService.mintKey(hotMeshClient.namespace, KeyType.JOB_STATE, keyParams);
|
|
335
|
-
const guidValue = Number(await hotMeshClient.engine.store.exec('HINCRBYFLOAT', workflowGuid, sessionId, '1') as string);
|
|
336
|
-
return guidValue === 1;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
374
|
/**
|
|
340
375
|
* Returns a random number between 0 and 1. This number is deterministic
|
|
341
376
|
* and will never vary for a given seed. This is useful for randomizing
|
|
@@ -440,9 +475,7 @@ export class WorkflowService {
|
|
|
440
475
|
* Interrupts a running job
|
|
441
476
|
*/
|
|
442
477
|
static async interrupt(jobId: string, options: JobInterruptOptions = {}): Promise<string | void> {
|
|
443
|
-
const
|
|
444
|
-
const workflowTopic = store.get('workflowTopic');
|
|
445
|
-
const namespace = store.get('namespace');
|
|
478
|
+
const { workflowTopic, namespace } = WorkflowService.getContext();
|
|
446
479
|
const hotMeshClient = await WorkerService.getHotMesh(workflowTopic, { namespace });
|
|
447
480
|
if (await WorkflowService.isSideEffectAllowed(hotMeshClient, 'interrupt')) {
|
|
448
481
|
return await hotMeshClient.interrupt(`${hotMeshClient.appId}.execute`, jobId, options);
|
package/services/engine/index.ts
CHANGED
|
@@ -51,7 +51,8 @@ import {
|
|
|
51
51
|
PartialJobState,
|
|
52
52
|
JobStatus,
|
|
53
53
|
JobInterruptOptions,
|
|
54
|
-
JobCompletionOptions
|
|
54
|
+
JobCompletionOptions,
|
|
55
|
+
ExtensionType} from '../../types/job';
|
|
55
56
|
import {
|
|
56
57
|
HotMeshApps,
|
|
57
58
|
HotMeshConfig,
|
|
@@ -566,10 +567,10 @@ class EngineService {
|
|
|
566
567
|
|
|
567
568
|
// ********************** PUB/SUB ENTRY POINT **********************
|
|
568
569
|
//publish (returns just the job id)
|
|
569
|
-
async pub(topic: string, data: JobData, context?: JobState): Promise<string> {
|
|
570
|
+
async pub(topic: string, data: JobData, context?: JobState, extended?: ExtensionType): Promise<string> {
|
|
570
571
|
const activityHandler = await this.initActivity(topic, data, context);
|
|
571
572
|
if (activityHandler) {
|
|
572
|
-
return await activityHandler.process();
|
|
573
|
+
return await activityHandler.process(extended);
|
|
573
574
|
} else {
|
|
574
575
|
throw new Error(`unable to process activity for topic ${topic}`);
|
|
575
576
|
}
|
|
@@ -12,7 +12,8 @@ import {
|
|
|
12
12
|
JobData,
|
|
13
13
|
JobOutput,
|
|
14
14
|
JobStatus,
|
|
15
|
-
JobInterruptOptions
|
|
15
|
+
JobInterruptOptions,
|
|
16
|
+
ExtensionType} from '../../types/job';
|
|
16
17
|
import {
|
|
17
18
|
HotMeshConfig,
|
|
18
19
|
HotMeshManifest } from '../../types/hotmesh';
|
|
@@ -115,8 +116,8 @@ class HotMeshService {
|
|
|
115
116
|
}
|
|
116
117
|
|
|
117
118
|
// ************* PUB/SUB METHODS *************
|
|
118
|
-
async pub(topic: string, data: JobData = {}, context?: JobState): Promise<string> {
|
|
119
|
-
return await this.engine?.pub(topic, data, context);
|
|
119
|
+
async pub(topic: string, data: JobData = {}, context?: JobState, extended?: ExtensionType): Promise<string> {
|
|
120
|
+
return await this.engine?.pub(topic, data, context, extended);
|
|
120
121
|
}
|
|
121
122
|
async sub(topic: string, callback: JobMessageCallback): Promise<void> {
|
|
122
123
|
return await this.engine?.sub(topic, callback);
|
package/types/durable.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { LogLevel } from './logger';
|
|
2
2
|
import { RedisClass, RedisOptions } from './redis';
|
|
3
|
-
import { StringStringType } from './serializer';
|
|
3
|
+
import { StringAnyType, StringStringType } from './serializer';
|
|
4
4
|
import { StreamData, StreamError } from './stream';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -68,6 +68,16 @@ type WorkflowContext = {
|
|
|
68
68
|
*/
|
|
69
69
|
namespace: string;
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* holds list of interruption payloads; if list is longer than 1 when the error is thrown, a `collator` subflow will be used
|
|
73
|
+
*/
|
|
74
|
+
interruptionRegistry: any[];
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* entry point ancestor flow; might be the parent; will never be self
|
|
78
|
+
*/
|
|
79
|
+
originJobId: string;
|
|
80
|
+
|
|
71
81
|
/**
|
|
72
82
|
* the workflow/job ID
|
|
73
83
|
*/
|
|
@@ -112,7 +122,7 @@ type WorkflowSearchOptions = {
|
|
|
112
122
|
schema?: Record<string, { type: 'TEXT' | 'NUMERIC' | 'TAG', sortable?: boolean }>;
|
|
113
123
|
|
|
114
124
|
/** Additional data as a key-value record */
|
|
115
|
-
data?:
|
|
125
|
+
data?: StringStringType;
|
|
116
126
|
}
|
|
117
127
|
|
|
118
128
|
|
|
@@ -173,6 +183,11 @@ type WorkflowOptions = {
|
|
|
173
183
|
*/
|
|
174
184
|
search?: WorkflowSearchOptions
|
|
175
185
|
|
|
186
|
+
/**
|
|
187
|
+
* marker data (begins with a -)
|
|
188
|
+
*/
|
|
189
|
+
marker?: StringStringType
|
|
190
|
+
|
|
176
191
|
/**
|
|
177
192
|
* the workflow configuration object
|
|
178
193
|
*/
|
|
@@ -235,7 +250,7 @@ type SignalOptions = {
|
|
|
235
250
|
/**
|
|
236
251
|
* Input data for the signal (any serializable object)
|
|
237
252
|
*/
|
|
238
|
-
data:
|
|
253
|
+
data: StringAnyType;
|
|
239
254
|
|
|
240
255
|
/**
|
|
241
256
|
* Execution ID, also known as the job ID
|
package/types/error.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
export type DurableChildErrorType = {
|
|
2
|
+
arguments: string[],
|
|
3
|
+
await?: boolean,
|
|
4
|
+
backoffCoefficient?: number,
|
|
5
|
+
index: number,
|
|
6
|
+
maximumAttempts?: number,
|
|
7
|
+
maximumInterval?: number,
|
|
8
|
+
originJobId: string | null,
|
|
9
|
+
parentWorkflowId: string,
|
|
10
|
+
workflowDimension: string,
|
|
11
|
+
workflowId: string,
|
|
12
|
+
workflowTopic: string,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type DurableWaitForAllErrorType = {
|
|
16
|
+
items: string[],
|
|
17
|
+
workflowId: string,
|
|
18
|
+
workflowTopic: string,
|
|
19
|
+
parentWorkflowId: string,
|
|
20
|
+
originJobId: string | null,
|
|
21
|
+
size: number,
|
|
22
|
+
index: number,
|
|
23
|
+
workflowDimension: string
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export type DurableProxyErrorType = {
|
|
27
|
+
arguments: string[],
|
|
28
|
+
activityName: string,
|
|
29
|
+
backoffCoefficient?: number,
|
|
30
|
+
index: number,
|
|
31
|
+
maximumAttempts?: number,
|
|
32
|
+
maximumInterval?: number,
|
|
33
|
+
originJobId: string | null,
|
|
34
|
+
parentWorkflowId: string,
|
|
35
|
+
workflowDimension: string,
|
|
36
|
+
workflowId: string,
|
|
37
|
+
workflowTopic: string,
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export type DurableWaitForErrorType = {
|
|
41
|
+
signalId: string,
|
|
42
|
+
index: number,
|
|
43
|
+
workflowDimension: string
|
|
44
|
+
workflowId: string;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type DurableSleepErrorType = {
|
|
48
|
+
duration: number,
|
|
49
|
+
index: number,
|
|
50
|
+
workflowDimension: string,
|
|
51
|
+
workflowId: string,
|
|
52
|
+
};
|
package/types/exporter.ts
CHANGED
|
@@ -2,7 +2,28 @@ import { StringAnyType } from "./serializer";
|
|
|
2
2
|
|
|
3
3
|
export type ExportItem = [(string | null), string, any];
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* job export data can be large, particularly transitions the timeline
|
|
7
|
+
*/
|
|
8
|
+
export type ExportFields = 'data' | 'state' | 'status' | 'timeline' | 'transitions';
|
|
9
|
+
|
|
10
|
+
export interface ExportOptions {
|
|
11
|
+
/**
|
|
12
|
+
* limit the export byte size by specifying an allowlist
|
|
13
|
+
*/
|
|
14
|
+
allow?: Array<ExportFields>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* limit the export byte size by specifying a block list
|
|
18
|
+
*/
|
|
19
|
+
block?: Array<ExportFields>;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* If false, do not return timeline values (like child job response, proxy activity response, etc)
|
|
23
|
+
* @default true
|
|
24
|
+
*/
|
|
25
|
+
values?: boolean;
|
|
26
|
+
};
|
|
6
27
|
|
|
7
28
|
export type JobAction = {
|
|
8
29
|
cursor: number;
|
|
@@ -46,26 +67,15 @@ export interface ExportCycles {
|
|
|
46
67
|
[key: string]: string[];
|
|
47
68
|
};
|
|
48
69
|
|
|
49
|
-
export type
|
|
70
|
+
export type TimelineType = {
|
|
71
|
+
key: string;
|
|
72
|
+
value: Record<string, any> | string | number | null;
|
|
50
73
|
index: number;
|
|
51
74
|
secondary?: number;
|
|
52
75
|
dimension?: string;
|
|
53
76
|
};
|
|
54
77
|
|
|
55
|
-
export
|
|
56
|
-
key: string;
|
|
57
|
-
value: string;
|
|
58
|
-
parts: IdemParts;
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export interface TimelineEntry {
|
|
62
|
-
activity: string;
|
|
63
|
-
dimensions: string;
|
|
64
|
-
created: string;
|
|
65
|
-
updated: string;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface TimestampParts {
|
|
78
|
+
export interface TransitionType {
|
|
69
79
|
activity: string;
|
|
70
80
|
dimensions: string;
|
|
71
81
|
created: string;
|
|
@@ -73,13 +83,11 @@ export interface TimestampParts {
|
|
|
73
83
|
}
|
|
74
84
|
|
|
75
85
|
export interface DurableJobExport {
|
|
76
|
-
data
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
idempotents: IdemType[];
|
|
82
|
-
replay: TimestampParts[];
|
|
86
|
+
data?: StringAnyType;
|
|
87
|
+
state?: StringAnyType;
|
|
88
|
+
status?: number;
|
|
89
|
+
timeline?: TimelineType[];
|
|
90
|
+
transitions?: TransitionType[];
|
|
83
91
|
};
|
|
84
92
|
|
|
85
93
|
export interface JobExport {
|
package/types/index.ts
CHANGED
|
@@ -51,6 +51,12 @@ export {
|
|
|
51
51
|
WorkflowDataType,
|
|
52
52
|
WorkflowOptions,
|
|
53
53
|
} from './durable';
|
|
54
|
+
export {
|
|
55
|
+
DurableChildErrorType,
|
|
56
|
+
DurableProxyErrorType,
|
|
57
|
+
DurableSleepErrorType,
|
|
58
|
+
DurableWaitForAllErrorType,
|
|
59
|
+
DurableWaitForErrorType } from "./error";
|
|
54
60
|
export {
|
|
55
61
|
ActivityAction,
|
|
56
62
|
DependencyExport,
|
|
@@ -83,7 +89,8 @@ export {
|
|
|
83
89
|
JobOutput,
|
|
84
90
|
JobState,
|
|
85
91
|
JobStatus,
|
|
86
|
-
PartialJobState
|
|
92
|
+
PartialJobState,
|
|
93
|
+
ExtensionType } from './job';
|
|
87
94
|
export { MappingStatements } from './map';
|
|
88
95
|
export {
|
|
89
96
|
Pipe,
|
package/types/job.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { StringStringType } from "./serializer";
|
|
2
|
+
|
|
1
3
|
type JobData = Record<string, unknown|Record<string, unknown>>;
|
|
2
4
|
type JobsData = Record<string, unknown>;
|
|
3
5
|
|
|
@@ -82,6 +84,30 @@ type JobMetadata = {
|
|
|
82
84
|
expire?: number;
|
|
83
85
|
};
|
|
84
86
|
|
|
87
|
+
/**
|
|
88
|
+
* User-defined (extended) types for job data. Users may interleave
|
|
89
|
+
* data into the job hash safely by using the `ExtensionType` interface.
|
|
90
|
+
* The data will be prefixed as necessary using an underscore or
|
|
91
|
+
* dash to ensure it is not confused with system process data.
|
|
92
|
+
*/
|
|
93
|
+
type ExtensionType = {
|
|
94
|
+
/**
|
|
95
|
+
* Custom search data field (name/value pairs) to seed the Hash.
|
|
96
|
+
* Every field will be prefixed with an underscore before being
|
|
97
|
+
* stored with the initial Hash data set along side system
|
|
98
|
+
* process data.
|
|
99
|
+
*/
|
|
100
|
+
search?: StringStringType;
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Custom marker data field used for adding a searchable marker to the job.
|
|
104
|
+
* markers always begin with a dash (-). Any field that does not
|
|
105
|
+
* begin with a dash will be removed and will not be inserted with
|
|
106
|
+
* the initial data set.
|
|
107
|
+
*/
|
|
108
|
+
marker?: StringStringType;
|
|
109
|
+
}
|
|
110
|
+
|
|
85
111
|
/**
|
|
86
112
|
* job_status semaphore
|
|
87
113
|
*/
|
|
@@ -183,4 +209,5 @@ export {
|
|
|
183
209
|
JobState,
|
|
184
210
|
JobStatus,
|
|
185
211
|
PartialJobState,
|
|
212
|
+
ExtensionType,
|
|
186
213
|
};
|