@hotmeshio/hotmesh 0.0.9 → 0.0.11
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 +84 -77
- package/build/modules/errors.d.ts +17 -1
- package/build/modules/errors.js +29 -1
- package/build/modules/key.d.ts +2 -2
- package/build/modules/key.js +3 -3
- package/build/package.json +2 -1
- package/build/services/activities/activity.d.ts +1 -0
- package/build/services/activities/activity.js +13 -2
- package/build/services/activities/cycle.js +6 -1
- package/build/services/activities/trigger.js +2 -3
- package/build/services/collator/index.d.ts +8 -0
- package/build/services/collator/index.js +11 -1
- package/build/services/durable/factory.d.ts +18 -1
- package/build/services/durable/factory.js +46 -4
- package/build/services/durable/handle.js +25 -7
- package/build/services/durable/native.js +0 -1
- package/build/services/durable/worker.d.ts +3 -3
- package/build/services/durable/worker.js +16 -11
- package/build/services/durable/workflow.js +1 -1
- package/build/services/hotmesh/index.js +1 -1
- package/build/services/pipe/functions/math.d.ts +4 -0
- package/build/services/pipe/functions/math.js +73 -0
- package/build/services/pipe/functions/number.d.ts +0 -4
- package/build/services/pipe/functions/number.js +0 -73
- package/build/services/signaler/stream.js +6 -3
- package/build/services/store/index.js +2 -2
- package/build/services/stream/clients/ioredis.js +1 -1
- package/build/services/stream/clients/redis.js +1 -1
- package/build/services/sub/clients/ioredis.js +1 -1
- package/build/services/sub/clients/redis.js +1 -1
- package/build/types/durable.d.ts +8 -3
- package/build/types/index.d.ts +1 -0
- package/modules/errors.ts +42 -1
- package/modules/key.ts +2 -2
- package/package.json +2 -1
- package/services/activities/activity.ts +14 -2
- package/services/activities/cycle.ts +6 -1
- package/services/activities/trigger.ts +2 -3
- package/services/collator/index.ts +12 -1
- package/services/durable/factory.ts +46 -4
- package/services/durable/handle.ts +23 -8
- package/services/durable/native.ts +0 -1
- package/services/durable/worker.ts +27 -13
- package/services/durable/workflow.ts +1 -1
- package/services/hotmesh/index.ts +2 -2
- package/services/pipe/functions/math.ts +74 -0
- package/services/pipe/functions/number.ts +0 -75
- package/services/signaler/stream.ts +6 -3
- package/services/store/index.ts +3 -3
- package/services/stream/clients/ioredis.ts +2 -2
- package/services/stream/clients/redis.ts +2 -2
- package/services/sub/clients/ioredis.ts +2 -2
- package/services/sub/clients/redis.ts +2 -2
- package/types/durable.ts +12 -5
- package/types/index.ts +15 -0
- package/build/services/dimension/index.d.ts +0 -29
- package/build/services/dimension/index.js +0 -35
- package/services/dimension/README.md +0 -73
- package/services/dimension/index.ts +0 -39
|
@@ -6,6 +6,7 @@ const asyncLocalStorage_1 = require("./asyncLocalStorage");
|
|
|
6
6
|
const hotmesh_1 = require("../hotmesh");
|
|
7
7
|
const stream_1 = require("../../types/stream");
|
|
8
8
|
const factory_1 = require("./factory");
|
|
9
|
+
const errors_1 = require("../../modules/errors");
|
|
9
10
|
/*
|
|
10
11
|
Here is an example of how the methods in this file are used:
|
|
11
12
|
|
|
@@ -26,7 +27,6 @@ async function run() {
|
|
|
26
27
|
});
|
|
27
28
|
const worker = await Worker.create({
|
|
28
29
|
connection,
|
|
29
|
-
namespace: 'default',
|
|
30
30
|
taskQueue: 'hello-world',
|
|
31
31
|
workflow: workflows.example,
|
|
32
32
|
activities,
|
|
@@ -40,13 +40,13 @@ run().catch((err) => {
|
|
|
40
40
|
});
|
|
41
41
|
*/
|
|
42
42
|
class WorkerService {
|
|
43
|
-
static async activateWorkflow(hotMesh, topic,
|
|
43
|
+
static async activateWorkflow(hotMesh, topic, dagFactory, options = {}) {
|
|
44
44
|
const version = '1';
|
|
45
45
|
const app = await hotMesh.engine.store.getApp(topic);
|
|
46
46
|
const appVersion = app?.version;
|
|
47
47
|
if (!appVersion) {
|
|
48
48
|
try {
|
|
49
|
-
await hotMesh.deploy(
|
|
49
|
+
await hotMesh.deploy(dagFactory(topic, version, options.maxSystemRetries, options.backoffExponent));
|
|
50
50
|
await hotMesh.activate(version);
|
|
51
51
|
}
|
|
52
52
|
catch (err) {
|
|
@@ -100,7 +100,7 @@ class WorkerService {
|
|
|
100
100
|
worker.activityRunner = await worker.initActivityWorkflow(config, activityTopic);
|
|
101
101
|
await WorkerService.activateWorkflow(worker.activityRunner, activityTopic, factory_1.getActivityYAML);
|
|
102
102
|
worker.workflowRunner = await worker.initWorkerWorkflow(config, workflowTopic, workflowFunction);
|
|
103
|
-
await WorkerService.activateWorkflow(worker.workflowRunner, workflowTopic, factory_1.getWorkflowYAML);
|
|
103
|
+
await WorkerService.activateWorkflow(worker.workflowRunner, workflowTopic, factory_1.getWorkflowYAML, config.options);
|
|
104
104
|
return worker;
|
|
105
105
|
}
|
|
106
106
|
static resolveWorkflowTarget(workflow) {
|
|
@@ -152,12 +152,16 @@ class WorkerService {
|
|
|
152
152
|
}
|
|
153
153
|
catch (err) {
|
|
154
154
|
this.activityRunner.engine.logger.error('durable-worker-activity-err', err);
|
|
155
|
+
if (!(err instanceof errors_1.DurableTimeoutError) &&
|
|
156
|
+
!(err instanceof errors_1.DurableMaxedError) &&
|
|
157
|
+
!(err instanceof errors_1.DurableFatalError)) {
|
|
158
|
+
err = new errors_1.DurableRetryError(err.message);
|
|
159
|
+
}
|
|
155
160
|
return {
|
|
156
161
|
status: stream_1.StreamStatus.ERROR,
|
|
157
|
-
code:
|
|
158
|
-
message: err.message,
|
|
162
|
+
code: err.code,
|
|
159
163
|
metadata: { ...data.metadata },
|
|
160
|
-
data: {
|
|
164
|
+
data: { message: err.message }
|
|
161
165
|
};
|
|
162
166
|
}
|
|
163
167
|
};
|
|
@@ -228,11 +232,12 @@ class WorkerService {
|
|
|
228
232
|
};
|
|
229
233
|
}
|
|
230
234
|
catch (err) {
|
|
235
|
+
// 59* - Durable*Error
|
|
231
236
|
return {
|
|
232
237
|
status: stream_1.StreamStatus.ERROR,
|
|
233
|
-
code:
|
|
238
|
+
code: err.code || new errors_1.DurableRetryError(err.message).code,
|
|
234
239
|
metadata: { ...data.metadata },
|
|
235
|
-
data: {
|
|
240
|
+
data: { message: err.message, type: err.name }
|
|
236
241
|
};
|
|
237
242
|
}
|
|
238
243
|
};
|
|
@@ -247,7 +252,7 @@ class WorkerService {
|
|
|
247
252
|
_a = WorkerService;
|
|
248
253
|
WorkerService.activityRegistry = {}; //user's activities
|
|
249
254
|
WorkerService.instances = new Map();
|
|
250
|
-
WorkerService.getHotMesh = async (worflowTopic) => {
|
|
255
|
+
WorkerService.getHotMesh = async (worflowTopic, options) => {
|
|
251
256
|
if (WorkerService.instances.has(worflowTopic)) {
|
|
252
257
|
return await WorkerService.instances.get(worflowTopic);
|
|
253
258
|
}
|
|
@@ -256,7 +261,7 @@ WorkerService.getHotMesh = async (worflowTopic) => {
|
|
|
256
261
|
engine: { redis: { ...WorkerService.connection } }
|
|
257
262
|
});
|
|
258
263
|
WorkerService.instances.set(worflowTopic, hotMesh);
|
|
259
|
-
await WorkerService.activateWorkflow(await hotMesh, worflowTopic, factory_1.getWorkflowYAML);
|
|
264
|
+
await WorkerService.activateWorkflow(await hotMesh, worflowTopic, factory_1.getWorkflowYAML, options);
|
|
260
265
|
return hotMesh;
|
|
261
266
|
};
|
|
262
267
|
WorkerService.Context = {
|
|
@@ -62,7 +62,7 @@ class WorkflowService {
|
|
|
62
62
|
const client = new client_1.ClientService({
|
|
63
63
|
connection: await connection_1.ConnectionService.connect(worker_1.WorkerService.connection),
|
|
64
64
|
});
|
|
65
|
-
//todo:
|
|
65
|
+
//todo: allow cross/app callback (pj:'@DURABLE@hello-world@<pjid>'/pa: <paid>/pd: <pdad>)
|
|
66
66
|
const handle = await client.workflow.start({
|
|
67
67
|
...options,
|
|
68
68
|
workflowId: `${workflowId}${options.workflowId}`,
|
|
@@ -17,7 +17,7 @@ class HotMeshService {
|
|
|
17
17
|
}
|
|
18
18
|
verifyAndSetNamespace(namespace) {
|
|
19
19
|
if (!namespace) {
|
|
20
|
-
this.namespace = key_1.
|
|
20
|
+
this.namespace = key_1.HMNS;
|
|
21
21
|
}
|
|
22
22
|
else if (!namespace.match(/^[A-Za-z0-9-]+$/)) {
|
|
23
23
|
throw new Error(`config.namespace [${namespace}] is invalid`);
|
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
declare class MathHandler {
|
|
2
|
+
add(...operands: (number | number[])[]): number;
|
|
3
|
+
subtract(...operands: (number | number[])[]): number;
|
|
4
|
+
multiply(...operands: (number | number[])[]): number;
|
|
5
|
+
divide(...operands: (number | number[])[]): number;
|
|
2
6
|
abs(x: number): number;
|
|
3
7
|
acos(x: number): number;
|
|
4
8
|
acosh(x: number): number;
|
|
@@ -2,6 +2,79 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MathHandler = void 0;
|
|
4
4
|
class MathHandler {
|
|
5
|
+
add(...operands) {
|
|
6
|
+
// @ts-ignore
|
|
7
|
+
return operands.reduce((a, b) => {
|
|
8
|
+
if (Array.isArray(b)) {
|
|
9
|
+
return a + this.add(...b);
|
|
10
|
+
}
|
|
11
|
+
else {
|
|
12
|
+
return a + b;
|
|
13
|
+
}
|
|
14
|
+
}, 0);
|
|
15
|
+
}
|
|
16
|
+
subtract(...operands) {
|
|
17
|
+
if (operands.length === 0) {
|
|
18
|
+
throw new Error('At least one operand is required.');
|
|
19
|
+
}
|
|
20
|
+
let flatOperands = [];
|
|
21
|
+
operands.forEach((op) => {
|
|
22
|
+
if (Array.isArray(op)) {
|
|
23
|
+
flatOperands = [...flatOperands, ...op];
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
flatOperands.push(op);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
if (flatOperands.length === 0) {
|
|
30
|
+
throw new Error('At least one operand is required after flattening.');
|
|
31
|
+
}
|
|
32
|
+
const result = flatOperands.reduce((a, b, i) => {
|
|
33
|
+
return i === 0 ? a : a - b;
|
|
34
|
+
});
|
|
35
|
+
return result;
|
|
36
|
+
}
|
|
37
|
+
multiply(...operands) {
|
|
38
|
+
if (operands.length === 0) {
|
|
39
|
+
throw new Error('At least one operand is required.');
|
|
40
|
+
}
|
|
41
|
+
// @ts-ignore
|
|
42
|
+
return operands.reduce((a, b) => {
|
|
43
|
+
if (Array.isArray(b)) {
|
|
44
|
+
return a * this.multiply(...b);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
return a * b;
|
|
48
|
+
}
|
|
49
|
+
}, 1);
|
|
50
|
+
}
|
|
51
|
+
divide(...operands) {
|
|
52
|
+
if (operands.length === 0) {
|
|
53
|
+
throw new Error('At least one operand is required.');
|
|
54
|
+
}
|
|
55
|
+
let flatOperands = [];
|
|
56
|
+
operands.forEach((op) => {
|
|
57
|
+
if (Array.isArray(op)) {
|
|
58
|
+
flatOperands = [...flatOperands, ...op];
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
flatOperands.push(op);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
if (flatOperands.length === 0) {
|
|
65
|
+
throw new Error('At least one operand is required after flattening.');
|
|
66
|
+
}
|
|
67
|
+
const result = flatOperands.reduce((a, b, i) => {
|
|
68
|
+
if (b === 0) {
|
|
69
|
+
return NaN;
|
|
70
|
+
}
|
|
71
|
+
return i === 0 ? a : a / b;
|
|
72
|
+
});
|
|
73
|
+
if (isNaN(result)) {
|
|
74
|
+
return NaN;
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
5
78
|
abs(x) {
|
|
6
79
|
return Math.abs(x);
|
|
7
80
|
}
|
|
@@ -17,9 +17,5 @@ declare class NumberHandler {
|
|
|
17
17
|
min(...values: number[]): number;
|
|
18
18
|
pow(base: number, exponent: number): number;
|
|
19
19
|
round(input: number): number;
|
|
20
|
-
add(...operands: (number | number[])[]): number;
|
|
21
|
-
subtract(...operands: (number | number[])[]): number;
|
|
22
|
-
multiply(...operands: (number | number[])[]): number;
|
|
23
|
-
divide(...operands: (number | number[])[]): number;
|
|
24
20
|
}
|
|
25
21
|
export { NumberHandler };
|
|
@@ -56,78 +56,5 @@ class NumberHandler {
|
|
|
56
56
|
round(input) {
|
|
57
57
|
return Math.round(input);
|
|
58
58
|
}
|
|
59
|
-
add(...operands) {
|
|
60
|
-
// @ts-ignore
|
|
61
|
-
return operands.reduce((a, b) => {
|
|
62
|
-
if (Array.isArray(b)) {
|
|
63
|
-
return a + this.add(...b);
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
return a + b;
|
|
67
|
-
}
|
|
68
|
-
}, 0);
|
|
69
|
-
}
|
|
70
|
-
subtract(...operands) {
|
|
71
|
-
if (operands.length === 0) {
|
|
72
|
-
throw new Error('At least one operand is required.');
|
|
73
|
-
}
|
|
74
|
-
let flatOperands = [];
|
|
75
|
-
operands.forEach((op) => {
|
|
76
|
-
if (Array.isArray(op)) {
|
|
77
|
-
flatOperands = [...flatOperands, ...op];
|
|
78
|
-
}
|
|
79
|
-
else {
|
|
80
|
-
flatOperands.push(op);
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
if (flatOperands.length === 0) {
|
|
84
|
-
throw new Error('At least one operand is required after flattening.');
|
|
85
|
-
}
|
|
86
|
-
const result = flatOperands.reduce((a, b, i) => {
|
|
87
|
-
return i === 0 ? a : a - b;
|
|
88
|
-
});
|
|
89
|
-
return result;
|
|
90
|
-
}
|
|
91
|
-
multiply(...operands) {
|
|
92
|
-
if (operands.length === 0) {
|
|
93
|
-
throw new Error('At least one operand is required.');
|
|
94
|
-
}
|
|
95
|
-
// @ts-ignore
|
|
96
|
-
return operands.reduce((a, b) => {
|
|
97
|
-
if (Array.isArray(b)) {
|
|
98
|
-
return a * this.multiply(...b);
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
return a * b;
|
|
102
|
-
}
|
|
103
|
-
}, 1);
|
|
104
|
-
}
|
|
105
|
-
divide(...operands) {
|
|
106
|
-
if (operands.length === 0) {
|
|
107
|
-
throw new Error('At least one operand is required.');
|
|
108
|
-
}
|
|
109
|
-
let flatOperands = [];
|
|
110
|
-
operands.forEach((op) => {
|
|
111
|
-
if (Array.isArray(op)) {
|
|
112
|
-
flatOperands = [...flatOperands, ...op];
|
|
113
|
-
}
|
|
114
|
-
else {
|
|
115
|
-
flatOperands.push(op);
|
|
116
|
-
}
|
|
117
|
-
});
|
|
118
|
-
if (flatOperands.length === 0) {
|
|
119
|
-
throw new Error('At least one operand is required after flattening.');
|
|
120
|
-
}
|
|
121
|
-
const result = flatOperands.reduce((a, b, i) => {
|
|
122
|
-
if (b === 0) {
|
|
123
|
-
return NaN;
|
|
124
|
-
}
|
|
125
|
-
return i === 0 ? a : a / b;
|
|
126
|
-
});
|
|
127
|
-
if (isNaN(result)) {
|
|
128
|
-
return NaN;
|
|
129
|
-
}
|
|
130
|
-
return result;
|
|
131
|
-
}
|
|
132
59
|
}
|
|
133
60
|
exports.NumberHandler = NumberHandler;
|
|
@@ -5,7 +5,7 @@ const key_1 = require("../../modules/key");
|
|
|
5
5
|
const utils_1 = require("../../modules/utils");
|
|
6
6
|
const telemetry_1 = require("../telemetry");
|
|
7
7
|
const stream_1 = require("../../types/stream");
|
|
8
|
-
const MAX_RETRIES =
|
|
8
|
+
const MAX_RETRIES = 3; //local retry; 10, 100, 1000ms
|
|
9
9
|
const MAX_TIMEOUT_MS = 60000;
|
|
10
10
|
const GRADUATED_INTERVAL_MS = 5000;
|
|
11
11
|
const BLOCK_DURATION = 15000; //Set to `15` so SIGINT/SIGTERM can interrupt; set to `0` to BLOCK indefinitely
|
|
@@ -158,8 +158,11 @@ class StreamSignaler {
|
|
|
158
158
|
const policy = policies?.[errorCode];
|
|
159
159
|
const maxRetries = policy?.[0];
|
|
160
160
|
const tryCount = Math.min(input.metadata.try || 0, MAX_RETRIES);
|
|
161
|
-
|
|
162
|
-
|
|
161
|
+
//only possible values for maxRetries are 1, 2, 3
|
|
162
|
+
//only possible values for tryCount are 0, 1, 2
|
|
163
|
+
if (maxRetries > tryCount) {
|
|
164
|
+
// 10ms, 100ms, or 1000ms delays between system retries
|
|
165
|
+
return [true, Math.pow(10, tryCount + 1)];
|
|
163
166
|
}
|
|
164
167
|
return [false, 0];
|
|
165
168
|
}
|
|
@@ -58,7 +58,7 @@ class StoreService {
|
|
|
58
58
|
};
|
|
59
59
|
this.redisClient = redisClient;
|
|
60
60
|
}
|
|
61
|
-
async init(namespace = key_1.
|
|
61
|
+
async init(namespace = key_1.HMNS, appId, logger) {
|
|
62
62
|
this.namespace = namespace;
|
|
63
63
|
this.appId = appId;
|
|
64
64
|
this.logger = logger;
|
|
@@ -111,7 +111,7 @@ class StoreService {
|
|
|
111
111
|
if (bCreate) {
|
|
112
112
|
const packageJson = await Promise.resolve().then(() => __importStar(require('../../package.json')));
|
|
113
113
|
const version = packageJson['version'] || '0.0.0';
|
|
114
|
-
settings = { namespace: key_1.
|
|
114
|
+
settings = { namespace: key_1.HMNS, version };
|
|
115
115
|
await this.setSettings(settings);
|
|
116
116
|
return settings;
|
|
117
117
|
}
|
|
@@ -7,7 +7,7 @@ class IORedisStreamService extends index_1.StreamService {
|
|
|
7
7
|
constructor(redisClient) {
|
|
8
8
|
super(redisClient);
|
|
9
9
|
}
|
|
10
|
-
async init(namespace = key_1.
|
|
10
|
+
async init(namespace = key_1.HMNS, appId, logger) {
|
|
11
11
|
this.namespace = namespace;
|
|
12
12
|
this.logger = logger;
|
|
13
13
|
this.appId = appId;
|
|
@@ -7,7 +7,7 @@ class RedisStreamService extends index_1.StreamService {
|
|
|
7
7
|
constructor(redisClient) {
|
|
8
8
|
super(redisClient);
|
|
9
9
|
}
|
|
10
|
-
async init(namespace = key_1.
|
|
10
|
+
async init(namespace = key_1.HMNS, appId, logger) {
|
|
11
11
|
this.namespace = namespace;
|
|
12
12
|
this.logger = logger;
|
|
13
13
|
this.appId = appId;
|
|
@@ -7,7 +7,7 @@ class IORedisSubService extends index_1.SubService {
|
|
|
7
7
|
constructor(redisClient) {
|
|
8
8
|
super(redisClient);
|
|
9
9
|
}
|
|
10
|
-
async init(namespace = key_1.
|
|
10
|
+
async init(namespace = key_1.HMNS, appId, engineId, logger) {
|
|
11
11
|
this.namespace = namespace;
|
|
12
12
|
this.logger = logger;
|
|
13
13
|
this.appId = appId;
|
|
@@ -7,7 +7,7 @@ class RedisSubService extends index_1.SubService {
|
|
|
7
7
|
constructor(redisClient) {
|
|
8
8
|
super(redisClient);
|
|
9
9
|
}
|
|
10
|
-
async init(namespace = key_1.
|
|
10
|
+
async init(namespace = key_1.HMNS, appId, engineId, logger) {
|
|
11
11
|
this.namespace = namespace;
|
|
12
12
|
this.logger = logger;
|
|
13
13
|
this.appId = appId;
|
package/build/types/durable.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ type WorkflowOptions = {
|
|
|
7
7
|
workflowTrace?: string;
|
|
8
8
|
workflowSpan?: string;
|
|
9
9
|
};
|
|
10
|
-
type
|
|
10
|
+
type ActivityWorkflowDataType = {
|
|
11
11
|
activityName: string;
|
|
12
12
|
arguments: any[];
|
|
13
13
|
workflowId: string;
|
|
@@ -32,9 +32,14 @@ type Registry = {
|
|
|
32
32
|
};
|
|
33
33
|
type WorkerConfig = {
|
|
34
34
|
connection: Connection;
|
|
35
|
-
namespace
|
|
35
|
+
namespace?: string;
|
|
36
36
|
taskQueue: string;
|
|
37
37
|
workflow: Function;
|
|
38
|
+
options?: WorkerOptions;
|
|
39
|
+
};
|
|
40
|
+
type WorkerOptions = {
|
|
41
|
+
maxSystemRetries?: number;
|
|
42
|
+
backoffExponent?: number;
|
|
38
43
|
};
|
|
39
44
|
type ContextType = {
|
|
40
45
|
workflowId: string;
|
|
@@ -54,4 +59,4 @@ type ActivityConfig = {
|
|
|
54
59
|
maximumInterval: string;
|
|
55
60
|
};
|
|
56
61
|
};
|
|
57
|
-
export { ActivityConfig,
|
|
62
|
+
export { ActivityConfig, ActivityWorkflowDataType, ClientConfig, ContextType, ConnectionConfig, Connection, NativeConnection, ProxyType, Registry, WorkerConfig, WorkerOptions, WorkflowDataType, WorkflowOptions, };
|
package/build/types/index.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export { App, AppVID, AppTransitions, AppSubscriptions } from './app';
|
|
|
3
3
|
export { AsyncSignal } from './async';
|
|
4
4
|
export { CacheMode } from './cache';
|
|
5
5
|
export { CollationFaultType, CollationStage } from './collator';
|
|
6
|
+
export { ActivityConfig, ActivityWorkflowDataType, ClientConfig, ContextType, ConnectionConfig, Connection, NativeConnection, ProxyType, Registry, WorkerConfig, WorkerOptions, WorkflowDataType, WorkflowOptions, } from './durable';
|
|
6
7
|
export { HookCondition, HookConditions, HookGate, HookInterface, HookRule, HookRules, HookSignal } from './hook';
|
|
7
8
|
export { RedisClientType as IORedisClientType, RedisMultiType as IORedisMultiType } from './ioredisclient';
|
|
8
9
|
export { ILogger } from './logger';
|
package/modules/errors.ts
CHANGED
|
@@ -12,6 +12,35 @@ class SetStateError extends Error {
|
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
class DurableTimeoutError extends Error {
|
|
16
|
+
code: number;
|
|
17
|
+
constructor(message: string) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.code = 596;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
class DurableMaxedError extends Error {
|
|
23
|
+
code: number;
|
|
24
|
+
constructor(message: string) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.code = 597;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
class DurableFatalError extends Error {
|
|
30
|
+
code: number;
|
|
31
|
+
constructor(message: string) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.code = 598;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
class DurableRetryError extends Error {
|
|
37
|
+
code: number;
|
|
38
|
+
constructor(message: string) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.code = 599;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
15
44
|
class MapDataError extends Error {
|
|
16
45
|
constructor() {
|
|
17
46
|
super("Error occurred while mapping data");
|
|
@@ -52,4 +81,16 @@ class CollationError extends Error {
|
|
|
52
81
|
}
|
|
53
82
|
}
|
|
54
83
|
|
|
55
|
-
export {
|
|
84
|
+
export {
|
|
85
|
+
CollationError,
|
|
86
|
+
DurableTimeoutError,
|
|
87
|
+
DurableMaxedError,
|
|
88
|
+
DurableFatalError,
|
|
89
|
+
DurableRetryError,
|
|
90
|
+
DuplicateJobError,
|
|
91
|
+
GetStateError,
|
|
92
|
+
SetStateError,
|
|
93
|
+
MapDataError,
|
|
94
|
+
RegisterTimeoutError,
|
|
95
|
+
ExecActivityError
|
|
96
|
+
};
|
package/modules/key.ts
CHANGED
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
*/
|
|
28
28
|
|
|
29
29
|
//default namespace for hotmesh
|
|
30
|
-
const
|
|
30
|
+
const HMNS = "hmsh";
|
|
31
31
|
|
|
32
32
|
//these are the entity types that are stored in the key/value store
|
|
33
33
|
enum KeyType {
|
|
@@ -126,4 +126,4 @@ class KeyService {
|
|
|
126
126
|
}
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
export { KeyService, KeyType, KeyStoreParams,
|
|
129
|
+
export { KeyService, KeyType, KeyStoreParams, HMNS };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotmeshio/hotmesh",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.11",
|
|
4
4
|
"description": "Durable Workflows",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -43,6 +43,7 @@
|
|
|
43
43
|
"test:durable:hello": "NODE_ENV=test jest ./tests/durable/helloworld/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
44
44
|
"test:durable:goodbye": "NODE_ENV=test jest ./tests/durable/goodbye/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
45
45
|
"test:durable:retry": "NODE_ENV=test jest ./tests/durable/retry/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
46
|
+
"test:durable:fatal": "NODE_ENV=test jest ./tests/durable/fatal/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
46
47
|
"test:durable:loopactivity": "NODE_ENV=test jest ./tests/durable/loopactivity/index.test.ts --detectOpenHandles --forceExit --verbose",
|
|
47
48
|
"test:durable:nested": "NODE_ENV=test jest ./tests/durable/nested/index.test.ts --detectOpenHandles --forceExit --verbose"
|
|
48
49
|
},
|
|
@@ -4,7 +4,6 @@ import {
|
|
|
4
4
|
getValueByPath,
|
|
5
5
|
restoreHierarchy } from '../../modules/utils';
|
|
6
6
|
import { CollatorService } from '../collator';
|
|
7
|
-
import { DimensionService } from '../dimension';
|
|
8
7
|
import { EngineService } from '../engine';
|
|
9
8
|
import { ILogger } from '../logger';
|
|
10
9
|
import { MapperService } from '../mapper';
|
|
@@ -84,6 +83,7 @@ class Activity {
|
|
|
84
83
|
if (this.doesHook()) {
|
|
85
84
|
//sleep and wait to awaken upon a signal
|
|
86
85
|
await this.registerHook(multi);
|
|
86
|
+
this.mapOutputData();
|
|
87
87
|
this.mapJobData();
|
|
88
88
|
await this.setState(multi);
|
|
89
89
|
await CollatorService.authorizeReentry(this, multi);
|
|
@@ -94,6 +94,7 @@ class Activity {
|
|
|
94
94
|
} else {
|
|
95
95
|
//end the activity and transition to its children
|
|
96
96
|
this.adjacencyList = await this.filterAdjacent();
|
|
97
|
+
this.mapOutputData();
|
|
97
98
|
this.mapJobData();
|
|
98
99
|
await this.setState(multi);
|
|
99
100
|
await CollatorService.notarizeEarlyCompletion(this, multi);
|
|
@@ -322,6 +323,17 @@ class Activity {
|
|
|
322
323
|
}
|
|
323
324
|
}
|
|
324
325
|
|
|
326
|
+
mapOutputData(): void {
|
|
327
|
+
//activity YAML may include output map data that produces/extends activity output data.
|
|
328
|
+
if(this.config.output?.maps) {
|
|
329
|
+
const mapper = new MapperService(this.config.output.maps, this.context);
|
|
330
|
+
const actOutData = mapper.mapRules();
|
|
331
|
+
const activityId = this.metadata.aid;
|
|
332
|
+
const data = { ...this.context[activityId].output, ...actOutData };
|
|
333
|
+
this.context[activityId].output.data = data;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
325
337
|
async registerTimeout(): Promise<void> {
|
|
326
338
|
//set timeout in support of hook and/or duplex
|
|
327
339
|
}
|
|
@@ -526,7 +538,7 @@ class Activity {
|
|
|
526
538
|
|
|
527
539
|
resolveAdjacentDad(): string {
|
|
528
540
|
//concat self and child dimension (all children (leg 1) begin life at 0)
|
|
529
|
-
return `${this.resolveDad()}${
|
|
541
|
+
return `${this.resolveDad()}${CollatorService.getDimensionalSeed(0)}`;
|
|
530
542
|
};
|
|
531
543
|
|
|
532
544
|
async filterAdjacent(): Promise<StreamData[]> {
|
|
@@ -81,13 +81,18 @@ class Cycle extends Activity {
|
|
|
81
81
|
* pattern allows for retries without violating the DAG.
|
|
82
82
|
*/
|
|
83
83
|
async cycleAncestorActivity(multi: RedisMulti): Promise<string> {
|
|
84
|
+
//Cycle activity L1 is a standin for the target ancestor L1.
|
|
85
|
+
//Input data mapping (mapInputData) allows for the
|
|
86
|
+
//next dimensonal thread to execute with different
|
|
87
|
+
//input data than the current dimensional thread
|
|
88
|
+
this.mapInputData();
|
|
84
89
|
const streamData: StreamData = {
|
|
85
90
|
metadata: {
|
|
86
91
|
dad: CollatorService.resolveReentryDimension(this),
|
|
87
92
|
jid: this.context.metadata.jid,
|
|
88
93
|
aid: this.config.ancestor,
|
|
89
94
|
},
|
|
90
|
-
data:
|
|
95
|
+
data: this.context.data
|
|
91
96
|
};
|
|
92
97
|
return (await this.engine.streamSignaler?.publishMessage(null, streamData, multi)) as string;
|
|
93
98
|
}
|
|
@@ -3,7 +3,6 @@ import { DuplicateJobError } from '../../modules/errors';
|
|
|
3
3
|
import { formatISODate, getTimeSeries } from '../../modules/utils';
|
|
4
4
|
import { Activity } from './activity';
|
|
5
5
|
import { CollatorService } from '../collator';
|
|
6
|
-
import { DimensionService } from '../dimension';
|
|
7
6
|
import { EngineService } from '../engine';
|
|
8
7
|
import { Pipe } from '../pipe';
|
|
9
8
|
import { ReporterService } from '../reporter';
|
|
@@ -99,7 +98,7 @@ class Trigger extends Activity {
|
|
|
99
98
|
|
|
100
99
|
const utc = formatISODate(new Date());
|
|
101
100
|
const { id, version } = await this.engine.getVID();
|
|
102
|
-
this.initDimensionalAddress(
|
|
101
|
+
this.initDimensionalAddress(CollatorService.getDimensionalSeed());
|
|
103
102
|
const activityMetadata = {
|
|
104
103
|
...this.metadata,
|
|
105
104
|
jid: jobId,
|
|
@@ -119,7 +118,7 @@ class Trigger extends Activity {
|
|
|
119
118
|
trc: this.context.metadata.trc,
|
|
120
119
|
spn: this.context.metadata.spn,
|
|
121
120
|
jid: jobId,
|
|
122
|
-
dad:
|
|
121
|
+
dad: CollatorService.getDimensionalSeed(), //top-level job implicitly uses `,0`
|
|
123
122
|
key: jobKey,
|
|
124
123
|
jc: utc,
|
|
125
124
|
ju: utc,
|
|
@@ -85,7 +85,7 @@ class CollatorService {
|
|
|
85
85
|
static async notarizeCompletion(activity: Activity, multi?: RedisMulti): Promise<number> {
|
|
86
86
|
//1) ALWAYS actualize leg2 dimension (+1)
|
|
87
87
|
//2) IF the activity is used in a cycle, don't close leg 2!
|
|
88
|
-
const decrement = activity.config.cycle ? 0 :
|
|
88
|
+
const decrement = activity.config.cycle ? 0 : 1_000_000_000_000;
|
|
89
89
|
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1 - decrement, this.getDimensionalAddress(activity), multi);
|
|
90
90
|
};
|
|
91
91
|
|
|
@@ -234,6 +234,17 @@ class CollatorService {
|
|
|
234
234
|
static isActivityComplete(status: number): boolean {
|
|
235
235
|
return (status - 0) <= 0;
|
|
236
236
|
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* All activities exist on a dimensional plane. Zero
|
|
240
|
+
* is the default. A value of
|
|
241
|
+
* `AxY,0,0,0,0,1,0,0` would reflect that
|
|
242
|
+
* an ancestor activity was dimensionalized beyond
|
|
243
|
+
* the default.
|
|
244
|
+
*/
|
|
245
|
+
static getDimensionalSeed(index = 0): string {
|
|
246
|
+
return `,${index}`;
|
|
247
|
+
}
|
|
237
248
|
}
|
|
238
249
|
|
|
239
250
|
export { CollatorService };
|