@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
|
@@ -1,4 +1,21 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* 1) `maxSystemRetries` | can be 0 to 3 and represents milliseconds;
|
|
3
|
+
* if there is an error, the workflow will retry up to `maxSystemRetries` times
|
|
4
|
+
* delaying by 10, 100, and 1000ms; this is a system level retry
|
|
5
|
+
* and is not configurable. It exists to handle intermittent network
|
|
6
|
+
* errors. (NOTE: each retry spawns a new transition stream)
|
|
7
|
+
*
|
|
8
|
+
* 2) `backoffExponent` | can be any number and represents `seconds` when applied;
|
|
9
|
+
* retries will happen indefinitely and adhere to the
|
|
10
|
+
* exponential backoff algorithm by multiplying by `backoffExponent`.
|
|
11
|
+
* For example, if `backoffExponent` is 10, the workflow will retry
|
|
12
|
+
* in 10s, 100s, 1000s, 10000s, etc.
|
|
13
|
+
*
|
|
14
|
+
* EXAMPLE | Using `maxSystemRetries = 3` and `backoffExponent = 10`, errant
|
|
15
|
+
* workflows will be retried on the following schedule (8 times in 27 hours):
|
|
16
|
+
* => 10ms, 100ms, 1000ms, 10s, 100s, 1_000s, 10_000s, 100_000s
|
|
17
|
+
*/
|
|
18
|
+
const getWorkflowYAML = (topic: string, version = '1', maxSystemRetries = 2, backoffExponent = 10) => {
|
|
2
19
|
return `app:
|
|
3
20
|
id: ${topic}
|
|
4
21
|
version: '${version}'
|
|
@@ -30,10 +47,20 @@ const getWorkflowYAML = (topic: string, version = '1') => {
|
|
|
30
47
|
a1:
|
|
31
48
|
type: activity
|
|
32
49
|
cycle: true
|
|
33
|
-
|
|
50
|
+
output:
|
|
51
|
+
schema:
|
|
52
|
+
type: object
|
|
53
|
+
properties:
|
|
54
|
+
duration:
|
|
55
|
+
type: number
|
|
56
|
+
maps:
|
|
57
|
+
duration: ${backoffExponent}
|
|
58
|
+
|
|
34
59
|
w1:
|
|
35
60
|
type: worker
|
|
36
61
|
topic: ${topic}
|
|
62
|
+
retry:
|
|
63
|
+
'599': [${maxSystemRetries}]
|
|
37
64
|
input:
|
|
38
65
|
schema:
|
|
39
66
|
type: object
|
|
@@ -55,18 +82,33 @@ const getWorkflowYAML = (topic: string, version = '1') => {
|
|
|
55
82
|
maps:
|
|
56
83
|
response: '{$self.output.data.response}'
|
|
57
84
|
|
|
85
|
+
a599:
|
|
86
|
+
title: Sleep before trying again
|
|
87
|
+
type: activity
|
|
88
|
+
sleep: "{a1.output.data.duration}"
|
|
89
|
+
|
|
58
90
|
c1:
|
|
91
|
+
title: Goto Activity a1
|
|
59
92
|
type: cycle
|
|
60
93
|
ancestor: a1
|
|
94
|
+
input:
|
|
95
|
+
maps:
|
|
96
|
+
duration:
|
|
97
|
+
'@pipe':
|
|
98
|
+
- ['{a1.output.data.duration}', ${backoffExponent}]
|
|
99
|
+
- ['{@math.multiply}']
|
|
100
|
+
|
|
61
101
|
transitions:
|
|
62
102
|
t1:
|
|
63
103
|
- to: a1
|
|
64
104
|
a1:
|
|
65
105
|
- to: w1
|
|
66
106
|
w1:
|
|
67
|
-
- to:
|
|
107
|
+
- to: a599
|
|
68
108
|
conditions:
|
|
69
|
-
code:
|
|
109
|
+
code: 599
|
|
110
|
+
a599:
|
|
111
|
+
- to: c1
|
|
70
112
|
`;
|
|
71
113
|
}
|
|
72
114
|
|
|
@@ -16,22 +16,37 @@ export class WorkflowHandleService {
|
|
|
16
16
|
let status = await this.hotMesh.getStatus(this.workflowId);
|
|
17
17
|
const topic = `${this.workflowTopic}.${this.workflowId}`;
|
|
18
18
|
|
|
19
|
-
if (status == 0) {
|
|
20
|
-
return (await this.hotMesh.getState(this.workflowTopic, this.workflowId)).data?.response;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
19
|
return new Promise((resolve, reject) => {
|
|
24
20
|
let isResolved = false;
|
|
25
21
|
//common fulfill/unsubscribe
|
|
26
|
-
const complete = async (response?: any) => {
|
|
22
|
+
const complete = async (response?: any, err?: string) => {
|
|
27
23
|
if (isResolved) return;
|
|
28
24
|
isResolved = true;
|
|
29
25
|
this.hotMesh.unsub(topic);
|
|
30
|
-
|
|
26
|
+
if (err) {
|
|
27
|
+
return reject(JSON.parse(err));
|
|
28
|
+
} else if (!response) {
|
|
29
|
+
const state = await this.hotMesh.getState(this.workflowTopic, this.workflowId);
|
|
30
|
+
if (!state.data && state.metadata.err) {
|
|
31
|
+
return reject(JSON.parse(state.metadata.err));
|
|
32
|
+
}
|
|
33
|
+
response = state.data?.response;
|
|
34
|
+
}
|
|
35
|
+
resolve(response);
|
|
31
36
|
};
|
|
32
|
-
|
|
33
|
-
|
|
37
|
+
//check for done
|
|
38
|
+
if (status == 0) {
|
|
39
|
+
return complete();
|
|
40
|
+
}
|
|
41
|
+
//subscribe to topic
|
|
42
|
+
this.hotMesh.sub(topic, async (topic: string, state: JobOutput) => {
|
|
43
|
+
if (!state.data && state.metadata.err) {
|
|
44
|
+
await complete(null, state.metadata.err);
|
|
45
|
+
} else {
|
|
46
|
+
await complete(state.data?.response);
|
|
47
|
+
}
|
|
34
48
|
});
|
|
49
|
+
//resolve for race condition
|
|
35
50
|
setTimeout(async () => {
|
|
36
51
|
status = await this.hotMesh.getStatus(this.workflowId);
|
|
37
52
|
if (status == 0) {
|
|
@@ -2,8 +2,18 @@ import { asyncLocalStorage } from './asyncLocalStorage';
|
|
|
2
2
|
import { HotMeshService as HotMesh } from '../hotmesh';
|
|
3
3
|
import { RedisClass, RedisOptions } from '../../types/redis';
|
|
4
4
|
import { StreamData, StreamDataResponse, StreamStatus } from '../../types/stream';
|
|
5
|
-
import {
|
|
5
|
+
import { ActivityWorkflowDataType,
|
|
6
|
+
Connection,
|
|
7
|
+
Registry,
|
|
8
|
+
WorkerConfig,
|
|
9
|
+
WorkerOptions,
|
|
10
|
+
WorkflowDataType } from "../../types/durable";
|
|
6
11
|
import { getWorkflowYAML, getActivityYAML } from './factory';
|
|
12
|
+
import {
|
|
13
|
+
DurableFatalError,
|
|
14
|
+
DurableMaxedError,
|
|
15
|
+
DurableRetryError,
|
|
16
|
+
DurableTimeoutError } from '../../modules/errors';
|
|
7
17
|
|
|
8
18
|
/*
|
|
9
19
|
Here is an example of how the methods in this file are used:
|
|
@@ -25,7 +35,6 @@ async function run() {
|
|
|
25
35
|
});
|
|
26
36
|
const worker = await Worker.create({
|
|
27
37
|
connection,
|
|
28
|
-
namespace: 'default',
|
|
29
38
|
taskQueue: 'hello-world',
|
|
30
39
|
workflow: workflows.example,
|
|
31
40
|
activities,
|
|
@@ -46,7 +55,7 @@ export class WorkerService {
|
|
|
46
55
|
workflowRunner: HotMesh;
|
|
47
56
|
activityRunner: HotMesh;
|
|
48
57
|
|
|
49
|
-
static getHotMesh = async (worflowTopic: string) => {
|
|
58
|
+
static getHotMesh = async (worflowTopic: string, options?: WorkerOptions) => {
|
|
50
59
|
if (WorkerService.instances.has(worflowTopic)) {
|
|
51
60
|
return await WorkerService.instances.get(worflowTopic);
|
|
52
61
|
}
|
|
@@ -55,17 +64,17 @@ export class WorkerService {
|
|
|
55
64
|
engine: { redis: { ...WorkerService.connection } }
|
|
56
65
|
});
|
|
57
66
|
WorkerService.instances.set(worflowTopic, hotMesh);
|
|
58
|
-
await WorkerService.activateWorkflow(await hotMesh, worflowTopic, getWorkflowYAML);
|
|
67
|
+
await WorkerService.activateWorkflow(await hotMesh, worflowTopic, getWorkflowYAML, options);
|
|
59
68
|
return hotMesh;
|
|
60
69
|
}
|
|
61
70
|
|
|
62
|
-
static async activateWorkflow(hotMesh: HotMesh, topic: string,
|
|
71
|
+
static async activateWorkflow(hotMesh: HotMesh, topic: string, dagFactory: Function, options: WorkerOptions = {}) {
|
|
63
72
|
const version = '1';
|
|
64
73
|
const app = await hotMesh.engine.store.getApp(topic);
|
|
65
74
|
const appVersion = app?.version;
|
|
66
75
|
if(!appVersion) {
|
|
67
76
|
try {
|
|
68
|
-
await hotMesh.deploy(
|
|
77
|
+
await hotMesh.deploy(dagFactory(topic, version, options.maxSystemRetries, options.backoffExponent));
|
|
69
78
|
await hotMesh.activate(version);
|
|
70
79
|
} catch (err) {
|
|
71
80
|
hotMesh.engine.logger.error('durable-worker-deploy-activate-err', err);
|
|
@@ -118,7 +127,7 @@ export class WorkerService {
|
|
|
118
127
|
worker.activityRunner = await worker.initActivityWorkflow(config, activityTopic);
|
|
119
128
|
await WorkerService.activateWorkflow(worker.activityRunner, activityTopic, getActivityYAML);
|
|
120
129
|
worker.workflowRunner = await worker.initWorkerWorkflow(config, workflowTopic, workflowFunction);
|
|
121
|
-
await WorkerService.activateWorkflow(worker.workflowRunner, workflowTopic, getWorkflowYAML);
|
|
130
|
+
await WorkerService.activateWorkflow(worker.workflowRunner, workflowTopic, getWorkflowYAML, config.options);
|
|
122
131
|
return worker;
|
|
123
132
|
}
|
|
124
133
|
|
|
@@ -161,7 +170,7 @@ export class WorkerService {
|
|
|
161
170
|
return async (data: StreamData): Promise<StreamDataResponse> => {
|
|
162
171
|
try {
|
|
163
172
|
//always run the activity function when instructed; return the response
|
|
164
|
-
const activityInput = data.data as unknown as
|
|
173
|
+
const activityInput = data.data as unknown as ActivityWorkflowDataType;
|
|
165
174
|
const activityName = activityInput.activityName;
|
|
166
175
|
const activityFunction = WorkerService.activityRegistry[activityName];
|
|
167
176
|
const pojoResponse = await activityFunction.apply(this, activityInput.arguments);
|
|
@@ -173,12 +182,16 @@ export class WorkerService {
|
|
|
173
182
|
};
|
|
174
183
|
} catch (err) {
|
|
175
184
|
this.activityRunner.engine.logger.error('durable-worker-activity-err', err);
|
|
185
|
+
if (!(err instanceof DurableTimeoutError) &&
|
|
186
|
+
!(err instanceof DurableMaxedError) &&
|
|
187
|
+
!(err instanceof DurableFatalError)) {
|
|
188
|
+
err = new DurableRetryError(err.message);
|
|
189
|
+
}
|
|
176
190
|
return {
|
|
177
191
|
status: StreamStatus.ERROR,
|
|
178
|
-
code:
|
|
179
|
-
message: err.message,
|
|
192
|
+
code: err.code,
|
|
180
193
|
metadata: { ...data.metadata },
|
|
181
|
-
data: {
|
|
194
|
+
data: { message: err.message }
|
|
182
195
|
} as StreamDataResponse;
|
|
183
196
|
}
|
|
184
197
|
}
|
|
@@ -258,11 +271,12 @@ export class WorkerService {
|
|
|
258
271
|
data: { response: workflowResponse }
|
|
259
272
|
};
|
|
260
273
|
} catch (err) {
|
|
274
|
+
// 59* - Durable*Error
|
|
261
275
|
return {
|
|
262
276
|
status: StreamStatus.ERROR,
|
|
263
|
-
code:
|
|
277
|
+
code: err.code || new DurableRetryError(err.message).code,
|
|
264
278
|
metadata: { ...data.metadata },
|
|
265
|
-
data: {
|
|
279
|
+
data: { message: err.message, type: err.name }
|
|
266
280
|
} as StreamDataResponse;
|
|
267
281
|
}
|
|
268
282
|
}
|
|
@@ -62,7 +62,7 @@ export class WorkflowService {
|
|
|
62
62
|
const client = new Client({
|
|
63
63
|
connection: await Connection.connect(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}`, //concat
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
-
import {
|
|
2
|
+
import { HMNS } from '../../modules/key';
|
|
3
3
|
import { EngineService } from '../engine';
|
|
4
4
|
import { LoggerService, ILogger } from '../logger';
|
|
5
5
|
import { StreamSignaler } from '../signaler/stream';
|
|
@@ -33,7 +33,7 @@ class HotMeshService {
|
|
|
33
33
|
|
|
34
34
|
verifyAndSetNamespace(namespace?: string) {
|
|
35
35
|
if (!namespace) {
|
|
36
|
-
this.namespace =
|
|
36
|
+
this.namespace = HMNS;
|
|
37
37
|
} else if (!namespace.match(/^[A-Za-z0-9-]+$/)) {
|
|
38
38
|
throw new Error(`config.namespace [${namespace}] is invalid`);
|
|
39
39
|
} else {
|
|
@@ -1,4 +1,78 @@
|
|
|
1
1
|
class MathHandler {
|
|
2
|
+
add(...operands: (number | number[])[]): number {
|
|
3
|
+
// @ts-ignore
|
|
4
|
+
return operands.reduce((a: number, b: number | number[]) => {
|
|
5
|
+
if (Array.isArray(b)) {
|
|
6
|
+
return a + this.add(...b);
|
|
7
|
+
} else {
|
|
8
|
+
return a + b;
|
|
9
|
+
}
|
|
10
|
+
}, 0);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
subtract(...operands: (number | number[])[]): number {
|
|
14
|
+
if (operands.length === 0) {
|
|
15
|
+
throw new Error('At least one operand is required.');
|
|
16
|
+
}
|
|
17
|
+
let flatOperands: number[] = [];
|
|
18
|
+
operands.forEach((op: number | number[]) => {
|
|
19
|
+
if (Array.isArray(op)) {
|
|
20
|
+
flatOperands = [...flatOperands, ...op];
|
|
21
|
+
} else {
|
|
22
|
+
flatOperands.push(op);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
if (flatOperands.length === 0) {
|
|
26
|
+
throw new Error('At least one operand is required after flattening.');
|
|
27
|
+
}
|
|
28
|
+
const result = flatOperands.reduce((a: number, b: number, i: number) => {
|
|
29
|
+
return i === 0 ? a : a - b;
|
|
30
|
+
});
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
multiply(...operands: (number | number[])[]): number {
|
|
35
|
+
if (operands.length === 0) {
|
|
36
|
+
throw new Error('At least one operand is required.');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// @ts-ignore
|
|
40
|
+
return operands.reduce((a: number, b: number | number[]) => {
|
|
41
|
+
if (Array.isArray(b)) {
|
|
42
|
+
return a * this.multiply(...b);
|
|
43
|
+
} else {
|
|
44
|
+
return a * b;
|
|
45
|
+
}
|
|
46
|
+
}, 1);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
divide(...operands: (number | number[])[]): number {
|
|
50
|
+
if (operands.length === 0) {
|
|
51
|
+
throw new Error('At least one operand is required.');
|
|
52
|
+
}
|
|
53
|
+
let flatOperands: number[] = [];
|
|
54
|
+
operands.forEach((op: number | number[]) => {
|
|
55
|
+
if (Array.isArray(op)) {
|
|
56
|
+
flatOperands = [...flatOperands, ...op];
|
|
57
|
+
} else {
|
|
58
|
+
flatOperands.push(op);
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
if (flatOperands.length === 0) {
|
|
62
|
+
throw new Error('At least one operand is required after flattening.');
|
|
63
|
+
}
|
|
64
|
+
const result = flatOperands.reduce((a: number, b: number, i: number) => {
|
|
65
|
+
if (b === 0) {
|
|
66
|
+
return NaN;
|
|
67
|
+
}
|
|
68
|
+
return i === 0 ? a : a / b;
|
|
69
|
+
});
|
|
70
|
+
if (isNaN(result)) {
|
|
71
|
+
return NaN;
|
|
72
|
+
}
|
|
73
|
+
return result;
|
|
74
|
+
}
|
|
75
|
+
|
|
2
76
|
abs(x: number): number {
|
|
3
77
|
return Math.abs(x);
|
|
4
78
|
}
|
|
@@ -70,81 +70,6 @@ class NumberHandler {
|
|
|
70
70
|
round(input: number): number {
|
|
71
71
|
return Math.round(input);
|
|
72
72
|
}
|
|
73
|
-
|
|
74
|
-
add(...operands: (number | number[])[]): number {
|
|
75
|
-
// @ts-ignore
|
|
76
|
-
return operands.reduce((a: number, b: number | number[]) => {
|
|
77
|
-
if (Array.isArray(b)) {
|
|
78
|
-
return a + this.add(...b);
|
|
79
|
-
} else {
|
|
80
|
-
return a + b;
|
|
81
|
-
}
|
|
82
|
-
}, 0);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
subtract(...operands: (number | number[])[]): number {
|
|
86
|
-
if (operands.length === 0) {
|
|
87
|
-
throw new Error('At least one operand is required.');
|
|
88
|
-
}
|
|
89
|
-
let flatOperands: number[] = [];
|
|
90
|
-
operands.forEach((op: number | number[]) => {
|
|
91
|
-
if (Array.isArray(op)) {
|
|
92
|
-
flatOperands = [...flatOperands, ...op];
|
|
93
|
-
} else {
|
|
94
|
-
flatOperands.push(op);
|
|
95
|
-
}
|
|
96
|
-
});
|
|
97
|
-
if (flatOperands.length === 0) {
|
|
98
|
-
throw new Error('At least one operand is required after flattening.');
|
|
99
|
-
}
|
|
100
|
-
const result = flatOperands.reduce((a: number, b: number, i: number) => {
|
|
101
|
-
return i === 0 ? a : a - b;
|
|
102
|
-
});
|
|
103
|
-
return result;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
multiply(...operands: (number | number[])[]): number {
|
|
108
|
-
if (operands.length === 0) {
|
|
109
|
-
throw new Error('At least one operand is required.');
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
// @ts-ignore
|
|
113
|
-
return operands.reduce((a: number, b: number | number[]) => {
|
|
114
|
-
if (Array.isArray(b)) {
|
|
115
|
-
return a * this.multiply(...b);
|
|
116
|
-
} else {
|
|
117
|
-
return a * b;
|
|
118
|
-
}
|
|
119
|
-
}, 1);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
divide(...operands: (number | number[])[]): number {
|
|
123
|
-
if (operands.length === 0) {
|
|
124
|
-
throw new Error('At least one operand is required.');
|
|
125
|
-
}
|
|
126
|
-
let flatOperands: number[] = [];
|
|
127
|
-
operands.forEach((op: number | number[]) => {
|
|
128
|
-
if (Array.isArray(op)) {
|
|
129
|
-
flatOperands = [...flatOperands, ...op];
|
|
130
|
-
} else {
|
|
131
|
-
flatOperands.push(op);
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
if (flatOperands.length === 0) {
|
|
135
|
-
throw new Error('At least one operand is required after flattening.');
|
|
136
|
-
}
|
|
137
|
-
const result = flatOperands.reduce((a: number, b: number, i: number) => {
|
|
138
|
-
if (b === 0) {
|
|
139
|
-
return NaN;
|
|
140
|
-
}
|
|
141
|
-
return i === 0 ? a : a / b;
|
|
142
|
-
});
|
|
143
|
-
if (isNaN(result)) {
|
|
144
|
-
return NaN;
|
|
145
|
-
}
|
|
146
|
-
return result;
|
|
147
|
-
}
|
|
148
73
|
}
|
|
149
74
|
|
|
150
75
|
export { NumberHandler };
|
|
@@ -16,7 +16,7 @@ import {
|
|
|
16
16
|
StreamStatus
|
|
17
17
|
} from '../../types/stream';
|
|
18
18
|
|
|
19
|
-
const MAX_RETRIES =
|
|
19
|
+
const MAX_RETRIES = 3; //local retry; 10, 100, 1000ms
|
|
20
20
|
const MAX_TIMEOUT_MS = 60000;
|
|
21
21
|
const GRADUATED_INTERVAL_MS = 5000;
|
|
22
22
|
const BLOCK_DURATION = 15000; //Set to `15` so SIGINT/SIGTERM can interrupt; set to `0` to BLOCK indefinitely
|
|
@@ -189,8 +189,11 @@ class StreamSignaler {
|
|
|
189
189
|
const policy = policies?.[errorCode];
|
|
190
190
|
const maxRetries = policy?.[0];
|
|
191
191
|
const tryCount = Math.min(input.metadata.try || 0, MAX_RETRIES);
|
|
192
|
-
|
|
193
|
-
|
|
192
|
+
//only possible values for maxRetries are 1, 2, 3
|
|
193
|
+
//only possible values for tryCount are 0, 1, 2
|
|
194
|
+
if (maxRetries > tryCount) {
|
|
195
|
+
// 10ms, 100ms, or 1000ms delays between system retries
|
|
196
|
+
return[true, Math.pow(10, tryCount + 1)];
|
|
194
197
|
}
|
|
195
198
|
return [false, 0];
|
|
196
199
|
}
|
package/services/store/index.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
KeyService,
|
|
3
3
|
KeyStoreParams,
|
|
4
4
|
KeyType,
|
|
5
|
-
|
|
5
|
+
HMNS} from '../../modules/key';
|
|
6
6
|
import { ILogger } from '../logger';
|
|
7
7
|
import { MDATA_SYMBOLS, SerializerService as Serializer } from '../serializer';
|
|
8
8
|
import { Cache } from './cache';
|
|
@@ -119,7 +119,7 @@ abstract class StoreService<T, U extends AbstractRedisClient> {
|
|
|
119
119
|
this.redisClient = redisClient;
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
async init(namespace =
|
|
122
|
+
async init(namespace = HMNS, appId: string, logger: ILogger): Promise<HotMeshApps> {
|
|
123
123
|
this.namespace = namespace;
|
|
124
124
|
this.appId = appId;
|
|
125
125
|
this.logger = logger;
|
|
@@ -178,7 +178,7 @@ abstract class StoreService<T, U extends AbstractRedisClient> {
|
|
|
178
178
|
if (bCreate) {
|
|
179
179
|
const packageJson = await import('../../package.json');
|
|
180
180
|
const version: string = packageJson['version'] || '0.0.0';
|
|
181
|
-
settings = { namespace:
|
|
181
|
+
settings = { namespace: HMNS, version } as HotMeshSettings;
|
|
182
182
|
await this.setSettings(settings);
|
|
183
183
|
return settings;
|
|
184
184
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KeyService, KeyStoreParams, KeyType,
|
|
1
|
+
import { KeyService, KeyStoreParams, KeyType, HMNS } from '../../../modules/key';
|
|
2
2
|
import { ILogger } from '../../logger';
|
|
3
3
|
import { StreamService } from '../index';
|
|
4
4
|
import { RedisClientType, RedisMultiType } from '../../../types/ioredisclient';
|
|
@@ -14,7 +14,7 @@ class IORedisStreamService extends StreamService<RedisClientType, RedisMultiType
|
|
|
14
14
|
super(redisClient);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
async init(namespace =
|
|
17
|
+
async init(namespace = HMNS, appId: string, logger: ILogger): Promise<void> {
|
|
18
18
|
this.namespace = namespace;
|
|
19
19
|
this.logger = logger;
|
|
20
20
|
this.appId = appId;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KeyService, KeyStoreParams, KeyType,
|
|
1
|
+
import { KeyService, KeyStoreParams, KeyType, HMNS } from '../../../modules/key';
|
|
2
2
|
import { ILogger } from '../../logger';
|
|
3
3
|
import { StreamService } from '../index';
|
|
4
4
|
import { RedisClientType, RedisMultiType } from '../../../types/redisclient';
|
|
@@ -14,7 +14,7 @@ class RedisStreamService extends StreamService<RedisClientType, RedisMultiType>
|
|
|
14
14
|
super(redisClient);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
async init(namespace =
|
|
17
|
+
async init(namespace = HMNS, appId: string, logger: ILogger): Promise<void> {
|
|
18
18
|
this.namespace = namespace;
|
|
19
19
|
this.logger = logger;
|
|
20
20
|
this.appId = appId;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KeyService, KeyStoreParams, KeyType,
|
|
1
|
+
import { KeyService, KeyStoreParams, KeyType, HMNS } from '../../../modules/key';
|
|
2
2
|
import { ILogger } from '../../logger';
|
|
3
3
|
import { SubService } from '../index';
|
|
4
4
|
import { RedisClientType, RedisMultiType } from '../../../types/ioredisclient';
|
|
@@ -14,7 +14,7 @@ class IORedisSubService extends SubService<RedisClientType, RedisMultiType> {
|
|
|
14
14
|
super(redisClient);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
async init(namespace =
|
|
17
|
+
async init(namespace = HMNS, appId: string, engineId: string, logger: ILogger): Promise<void> {
|
|
18
18
|
this.namespace = namespace;
|
|
19
19
|
this.logger = logger;
|
|
20
20
|
this.appId = appId;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { KeyService, KeyStoreParams, KeyType,
|
|
1
|
+
import { KeyService, KeyStoreParams, KeyType, HMNS } from '../../../modules/key';
|
|
2
2
|
import { ILogger } from '../../logger';
|
|
3
3
|
import { SubService } from '../index';
|
|
4
4
|
import { RedisClientType, RedisMultiType } from '../../../types/redisclient';
|
|
@@ -14,7 +14,7 @@ class RedisSubService extends SubService<RedisClientType, RedisMultiType> {
|
|
|
14
14
|
super(redisClient);
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
async init(namespace =
|
|
17
|
+
async init(namespace = HMNS, appId: string, engineId: string, logger: ILogger): Promise<void> {
|
|
18
18
|
this.namespace = namespace;
|
|
19
19
|
this.logger = logger;
|
|
20
20
|
this.appId = appId;
|
package/types/durable.ts
CHANGED
|
@@ -9,7 +9,7 @@ type WorkflowOptions = {
|
|
|
9
9
|
workflowSpan?: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
type
|
|
12
|
+
type ActivityWorkflowDataType = {
|
|
13
13
|
activityName: string;
|
|
14
14
|
arguments: any[];
|
|
15
15
|
workflowId: string;
|
|
@@ -39,9 +39,15 @@ type Registry = {
|
|
|
39
39
|
|
|
40
40
|
type WorkerConfig = {
|
|
41
41
|
connection: Connection;
|
|
42
|
-
namespace
|
|
42
|
+
namespace?: string; //`appid` in the YAML (e.g, 'default')
|
|
43
43
|
taskQueue: string; //`subscribes` in the YAML (e.g, 'hello-world')
|
|
44
|
-
workflow: Function //target function to run
|
|
44
|
+
workflow: Function; //target function to run
|
|
45
|
+
options?: WorkerOptions;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
type WorkerOptions = {
|
|
49
|
+
maxSystemRetries?: number; //1-3 (10ms, 100ms, 1_000ms)
|
|
50
|
+
backoffExponent?: number; //2-10ish
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
type ContextType = {
|
|
@@ -67,7 +73,7 @@ type ActivityConfig = {
|
|
|
67
73
|
|
|
68
74
|
export {
|
|
69
75
|
ActivityConfig,
|
|
70
|
-
|
|
76
|
+
ActivityWorkflowDataType,
|
|
71
77
|
ClientConfig,
|
|
72
78
|
ContextType,
|
|
73
79
|
ConnectionConfig,
|
|
@@ -76,6 +82,7 @@ export {
|
|
|
76
82
|
ProxyType,
|
|
77
83
|
Registry,
|
|
78
84
|
WorkerConfig,
|
|
85
|
+
WorkerOptions,
|
|
79
86
|
WorkflowDataType,
|
|
80
87
|
WorkflowOptions,
|
|
81
|
-
};
|
|
88
|
+
};
|
package/types/index.ts
CHANGED
|
@@ -26,6 +26,21 @@ export { CacheMode } from './cache';
|
|
|
26
26
|
export {
|
|
27
27
|
CollationFaultType,
|
|
28
28
|
CollationStage } from './collator';
|
|
29
|
+
export {
|
|
30
|
+
ActivityConfig,
|
|
31
|
+
ActivityWorkflowDataType,
|
|
32
|
+
ClientConfig,
|
|
33
|
+
ContextType,
|
|
34
|
+
ConnectionConfig,
|
|
35
|
+
Connection,
|
|
36
|
+
NativeConnection,
|
|
37
|
+
ProxyType,
|
|
38
|
+
Registry,
|
|
39
|
+
WorkerConfig,
|
|
40
|
+
WorkerOptions,
|
|
41
|
+
WorkflowDataType,
|
|
42
|
+
WorkflowOptions,
|
|
43
|
+
}from './durable'
|
|
29
44
|
export {
|
|
30
45
|
HookCondition,
|
|
31
46
|
HookConditions,
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { HotMeshGraph } from '../../types/hotmesh';
|
|
2
|
-
declare class DimensionService {
|
|
3
|
-
static targetLength: number;
|
|
4
|
-
/**
|
|
5
|
-
* entry point for compiler-type activities. This is called by the compiler
|
|
6
|
-
* to bind the sorted activity IDs to the trigger activity. These are then used
|
|
7
|
-
* at runtime by the activities to track job/activity status.
|
|
8
|
-
* @param graphs
|
|
9
|
-
*/
|
|
10
|
-
static compile(graphs: HotMeshGraph[]): void;
|
|
11
|
-
/**
|
|
12
|
-
* All activities exist on a dimensional plane. Zero
|
|
13
|
-
* is the default and is implied if no dimension is
|
|
14
|
-
* present in the hash item key. EVERY value in the
|
|
15
|
-
* job ledger is dimensionalized even if the dimension
|
|
16
|
-
* is not present. The key, `AaA`, might not contain
|
|
17
|
-
* a dimensional index, but it is still implicitly
|
|
18
|
-
* dimensionalized as `AaA,0` (assuming a trigger).
|
|
19
|
-
* A value of `AxY,0,0,0,0,1,0,0` would reflect that
|
|
20
|
-
* an ancestor activity was dimensionalized beyond
|
|
21
|
-
* the default. The dimensional string must
|
|
22
|
-
* be included if not zero. There is likely a preceding
|
|
23
|
-
* sibling dimension, so it would not need to include
|
|
24
|
-
* the suffix, so these addresses are equivalent:
|
|
25
|
-
* `AxY,0,0,0,0,0,0,0` == `AxY` for said sibling.
|
|
26
|
-
*/
|
|
27
|
-
static getSeed(index?: number): string;
|
|
28
|
-
}
|
|
29
|
-
export { DimensionService };
|