@hotmeshio/hotmesh 0.0.41 → 0.0.43
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/build/modules/enums.d.ts +2 -0
- package/build/modules/enums.js +4 -1
- package/build/modules/errors.d.ts +1 -8
- package/build/modules/errors.js +1 -12
- package/build/modules/utils.js +1 -1
- package/build/package.json +1 -1
- package/build/services/activities/activity.d.ts +8 -1
- package/build/services/activities/activity.js +17 -12
- package/build/services/collator/index.d.ts +20 -2
- package/build/services/collator/index.js +41 -7
- package/build/services/durable/client.d.ts +2 -1
- package/build/services/durable/client.js +17 -3
- package/build/services/durable/factory.d.ts +0 -1
- package/build/services/durable/factory.js +0 -138
- package/build/services/durable/meshos.js +3 -0
- package/build/services/durable/worker.js +0 -15
- package/build/services/durable/workflow.d.ts +0 -9
- package/build/services/durable/workflow.js +0 -29
- package/build/services/engine/index.d.ts +1 -1
- package/build/services/engine/index.js +5 -8
- package/build/services/quorum/index.d.ts +5 -2
- package/build/services/quorum/index.js +32 -15
- package/build/services/store/clients/redis.js +1 -0
- package/build/services/store/index.d.ts +13 -1
- package/build/services/store/index.js +22 -6
- package/build/types/hotmesh.d.ts +1 -1
- package/build/types/job.d.ts +1 -0
- package/modules/enums.ts +4 -0
- package/modules/errors.ts +0 -15
- package/modules/utils.ts +1 -1
- package/package.json +1 -1
- package/services/activities/activity.ts +30 -15
- package/services/collator/index.ts +41 -8
- package/services/durable/client.ts +19 -4
- package/services/durable/factory.ts +0 -138
- package/services/durable/meshos.ts +3 -0
- package/services/durable/worker.ts +0 -16
- package/services/durable/workflow.ts +0 -32
- package/services/engine/index.ts +5 -6
- package/services/quorum/index.ts +35 -12
- package/services/store/clients/redis.ts +1 -0
- package/services/store/index.ts +25 -7
- package/types/hotmesh.ts +1 -1
- package/types/job.ts +1 -0
package/build/modules/enums.d.ts
CHANGED
|
@@ -15,6 +15,8 @@ export declare const HMSH_CODE_DURABLE_MAXED = 597;
|
|
|
15
15
|
export declare const HMSH_CODE_DURABLE_FATAL = 598;
|
|
16
16
|
export declare const HMSH_CODE_DURABLE_RETRYABLE = 599;
|
|
17
17
|
export declare const HMSH_STATUS_UNKNOWN = "unknown";
|
|
18
|
+
export declare const HMSH_QUORUM_DELAY_MS = 250;
|
|
19
|
+
export declare const HMSH_ACTIVATION_MAX_RETRY = 3;
|
|
18
20
|
export declare const HMSH_OTT_WAIT_TIME: number;
|
|
19
21
|
export declare const HMSH_EXPIRE_JOB_SECONDS: number;
|
|
20
22
|
export declare const HMSH_MAX_RETRIES: number;
|
package/build/modules/enums.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.HMSH_SCOUT_INTERVAL_SECONDS = exports.HMSH_FIDELITY_SECONDS = exports.HMSH_EXPIRE_DURATION = exports.HMSH_XPENDING_COUNT = exports.HMSH_XCLAIM_COUNT = exports.HMSH_XCLAIM_DELAY_MS = exports.HMSH_BLOCK_TIME_MS = exports.HMSH_GRADUATED_INTERVAL_MS = exports.HMSH_MAX_TIMEOUT_MS = exports.HMSH_MAX_RETRIES = exports.HMSH_EXPIRE_JOB_SECONDS = exports.HMSH_OTT_WAIT_TIME = exports.HMSH_STATUS_UNKNOWN = exports.HMSH_CODE_DURABLE_RETRYABLE = exports.HMSH_CODE_DURABLE_FATAL = exports.HMSH_CODE_DURABLE_MAXED = exports.HMSH_CODE_DURABLE_TIMEOUT = exports.HMSH_CODE_DURABLE_WAITFOR = exports.HMSH_CODE_DURABLE_INCOMPLETE = exports.HMSH_CODE_DURABLE_SLEEPFOR = exports.HMSH_CODE_UNACKED = exports.HMSH_CODE_TIMEOUT = exports.HMSH_CODE_UNKNOWN = exports.HMSH_CODE_INTERRUPT = exports.HMSH_CODE_NOTFOUND = exports.HMSH_CODE_PENDING = exports.HMSH_CODE_SUCCESS = exports.HMSH_LOGLEVEL = void 0;
|
|
3
|
+
exports.HMSH_SCOUT_INTERVAL_SECONDS = exports.HMSH_FIDELITY_SECONDS = exports.HMSH_EXPIRE_DURATION = exports.HMSH_XPENDING_COUNT = exports.HMSH_XCLAIM_COUNT = exports.HMSH_XCLAIM_DELAY_MS = exports.HMSH_BLOCK_TIME_MS = exports.HMSH_GRADUATED_INTERVAL_MS = exports.HMSH_MAX_TIMEOUT_MS = exports.HMSH_MAX_RETRIES = exports.HMSH_EXPIRE_JOB_SECONDS = exports.HMSH_OTT_WAIT_TIME = exports.HMSH_ACTIVATION_MAX_RETRY = exports.HMSH_QUORUM_DELAY_MS = exports.HMSH_STATUS_UNKNOWN = exports.HMSH_CODE_DURABLE_RETRYABLE = exports.HMSH_CODE_DURABLE_FATAL = exports.HMSH_CODE_DURABLE_MAXED = exports.HMSH_CODE_DURABLE_TIMEOUT = exports.HMSH_CODE_DURABLE_WAITFOR = exports.HMSH_CODE_DURABLE_INCOMPLETE = exports.HMSH_CODE_DURABLE_SLEEPFOR = exports.HMSH_CODE_UNACKED = exports.HMSH_CODE_TIMEOUT = exports.HMSH_CODE_UNKNOWN = exports.HMSH_CODE_INTERRUPT = exports.HMSH_CODE_NOTFOUND = exports.HMSH_CODE_PENDING = exports.HMSH_CODE_SUCCESS = exports.HMSH_LOGLEVEL = void 0;
|
|
4
4
|
// HOTMESH SYSTEM
|
|
5
5
|
exports.HMSH_LOGLEVEL = process.env.HMSH_LOGLEVEL || 'info';
|
|
6
6
|
// STATUS CODES AND MESSAGES
|
|
@@ -19,6 +19,9 @@ exports.HMSH_CODE_DURABLE_MAXED = 597;
|
|
|
19
19
|
exports.HMSH_CODE_DURABLE_FATAL = 598;
|
|
20
20
|
exports.HMSH_CODE_DURABLE_RETRYABLE = 599;
|
|
21
21
|
exports.HMSH_STATUS_UNKNOWN = 'unknown';
|
|
22
|
+
// QUORUM
|
|
23
|
+
exports.HMSH_QUORUM_DELAY_MS = 250;
|
|
24
|
+
exports.HMSH_ACTIVATION_MAX_RETRY = 3;
|
|
22
25
|
// ENGINE
|
|
23
26
|
exports.HMSH_OTT_WAIT_TIME = parseInt(process.env.HMSH_OTT_WAIT_TIME, 10) || 1000;
|
|
24
27
|
exports.HMSH_EXPIRE_JOB_SECONDS = parseInt(process.env.HMSH_EXPIRE_JOB_SECONDS, 10) || 1;
|
|
@@ -23,13 +23,6 @@ declare class DurableWaitForSignalError extends Error {
|
|
|
23
23
|
index: number;
|
|
24
24
|
}[]);
|
|
25
25
|
}
|
|
26
|
-
declare class DurableSleepError extends Error {
|
|
27
|
-
code: number;
|
|
28
|
-
duration: number;
|
|
29
|
-
index: number;
|
|
30
|
-
dimension: string;
|
|
31
|
-
constructor(message: string, duration: number, index: number, dimension: string);
|
|
32
|
-
}
|
|
33
26
|
declare class DurableSleepForError extends Error {
|
|
34
27
|
code: number;
|
|
35
28
|
duration: number;
|
|
@@ -86,4 +79,4 @@ declare class CollationError extends Error {
|
|
|
86
79
|
fault: CollationFaultType;
|
|
87
80
|
constructor(status: number, leg: ActivityDuplex, stage: CollationStage, fault?: CollationFaultType);
|
|
88
81
|
}
|
|
89
|
-
export { CollationError, DurableFatalError, DurableIncompleteSignalError, DurableMaxedError, DurableRetryError,
|
|
82
|
+
export { CollationError, DurableFatalError, DurableIncompleteSignalError, DurableMaxedError, DurableRetryError, DurableSleepForError, DurableTimeoutError, DurableWaitForSignalError, DuplicateJobError, ExecActivityError, GenerationalError, GetStateError, InactiveJobError, MapDataError, RegisterTimeoutError, SetStateError, };
|
package/build/modules/errors.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SetStateError = exports.RegisterTimeoutError = exports.MapDataError = exports.InactiveJobError = exports.GetStateError = exports.GenerationalError = exports.ExecActivityError = exports.DuplicateJobError = exports.DurableWaitForSignalError = exports.DurableTimeoutError = exports.DurableSleepForError = exports.
|
|
3
|
+
exports.SetStateError = exports.RegisterTimeoutError = exports.MapDataError = exports.InactiveJobError = exports.GetStateError = exports.GenerationalError = exports.ExecActivityError = exports.DuplicateJobError = exports.DurableWaitForSignalError = exports.DurableTimeoutError = exports.DurableSleepForError = exports.DurableRetryError = exports.DurableMaxedError = exports.DurableIncompleteSignalError = exports.DurableFatalError = exports.CollationError = void 0;
|
|
4
4
|
const enums_1 = require("./enums");
|
|
5
5
|
class GetStateError extends Error {
|
|
6
6
|
constructor(jobId) {
|
|
@@ -34,17 +34,6 @@ class DurableWaitForSignalError extends Error {
|
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
36
|
exports.DurableWaitForSignalError = DurableWaitForSignalError;
|
|
37
|
-
/* @deprecated */
|
|
38
|
-
class DurableSleepError extends Error {
|
|
39
|
-
constructor(message, duration, index, dimension) {
|
|
40
|
-
super(message);
|
|
41
|
-
this.duration = duration;
|
|
42
|
-
this.index = index;
|
|
43
|
-
this.dimension = dimension;
|
|
44
|
-
this.code = 595;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
exports.DurableSleepError = DurableSleepError;
|
|
48
37
|
class DurableSleepForError extends Error {
|
|
49
38
|
constructor(message, duration, index, dimension) {
|
|
50
39
|
super(message);
|
package/build/modules/utils.js
CHANGED
package/build/package.json
CHANGED
|
@@ -31,6 +31,11 @@ declare class Activity {
|
|
|
31
31
|
* all aspects of the entry including job and activty state
|
|
32
32
|
*/
|
|
33
33
|
verifyEntry(): Promise<void>;
|
|
34
|
+
/**
|
|
35
|
+
* Upon entering leg 2 of a duplexed activty, verify
|
|
36
|
+
* all aspects of the re-entry including job and activty state
|
|
37
|
+
*/
|
|
38
|
+
verifyReentry(): Promise<number>;
|
|
34
39
|
processEvent(status?: StreamStatus, code?: StreamCode, type?: 'hook' | 'output'): Promise<void>;
|
|
35
40
|
processPending(telemetry: TelemetryService, type: 'hook' | 'output'): Promise<MultiResponseFlags>;
|
|
36
41
|
processSuccess(telemetry: TelemetryService, type: 'hook' | 'output'): Promise<MultiResponseFlags>;
|
|
@@ -57,7 +62,9 @@ declare class Activity {
|
|
|
57
62
|
getState(): Promise<void>;
|
|
58
63
|
/**
|
|
59
64
|
* if the job is created/deleted/created with the same key,
|
|
60
|
-
* the 'gid' ensures no stale messages
|
|
65
|
+
* the 'gid' ensures no stale messages (such as sleep delays)
|
|
66
|
+
* enter the workstream. Any message with a mismatched gid
|
|
67
|
+
* belongs to a prior job and can safely be ignored/dropped.
|
|
61
68
|
*/
|
|
62
69
|
assertGenerationalId(jobGID: string, msgGID?: string): void;
|
|
63
70
|
initDimensionalAddress(dad: string): void;
|
|
@@ -40,6 +40,17 @@ class Activity {
|
|
|
40
40
|
collator_1.CollatorService.assertJobActive(this.context.metadata.js, this.context.metadata.jid, this.metadata.aid);
|
|
41
41
|
await collator_1.CollatorService.notarizeEntry(this);
|
|
42
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Upon entering leg 2 of a duplexed activty, verify
|
|
45
|
+
* all aspects of the re-entry including job and activty state
|
|
46
|
+
*/
|
|
47
|
+
async verifyReentry() {
|
|
48
|
+
const guid = this.context.metadata.guid;
|
|
49
|
+
this.setLeg(2);
|
|
50
|
+
await this.getState();
|
|
51
|
+
collator_1.CollatorService.assertJobActive(this.context.metadata.js, this.context.metadata.jid, this.metadata.aid);
|
|
52
|
+
return await collator_1.CollatorService.notarizeReentry(this, guid);
|
|
53
|
+
}
|
|
43
54
|
//******** DUPLEX RE-ENTRY POINT ********//
|
|
44
55
|
async processEvent(status = stream_1.StreamStatus.SUCCESS, code = 200, type = 'output') {
|
|
45
56
|
this.setLeg(2);
|
|
@@ -54,17 +65,9 @@ class Activity {
|
|
|
54
65
|
this.logger.debug('activity-process-event', { topic: this.config.subtype, jid, aid, status, code });
|
|
55
66
|
let telemetry;
|
|
56
67
|
try {
|
|
57
|
-
await this.
|
|
58
|
-
|
|
59
|
-
const aState = await collator_1.CollatorService.notarizeReentry(this);
|
|
60
|
-
this.adjacentIndex = collator_1.CollatorService.getDimensionalIndex(aState);
|
|
68
|
+
const collationKey = await this.verifyReentry();
|
|
69
|
+
this.adjacentIndex = collator_1.CollatorService.getDimensionalIndex(collationKey);
|
|
61
70
|
telemetry = new telemetry_1.TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
|
|
62
|
-
let isComplete = collator_1.CollatorService.isActivityComplete(this.context.metadata.js);
|
|
63
|
-
if (isComplete) {
|
|
64
|
-
this.logger.warn('activity-process-event-duplicate', { jid, aid });
|
|
65
|
-
this.logger.debug('activity-process-event-duplicate-resolution', { resolution: 'Increase HotMesh config `reclaimDelay` timeout.' });
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
71
|
telemetry.startActivitySpan(this.leg);
|
|
69
72
|
let multiResponse;
|
|
70
73
|
if (status === stream_1.StreamStatus.PENDING) {
|
|
@@ -306,7 +309,7 @@ class Activity {
|
|
|
306
309
|
telemetry_1.TelemetryService.addTargetTelemetryPaths(consumes, this.config, this.metadata, this.leg);
|
|
307
310
|
let { dad, jid } = this.context.metadata;
|
|
308
311
|
const dIds = collator_1.CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], dad || '');
|
|
309
|
-
//`state` is a
|
|
312
|
+
//`state` is a unidimensional hash; context is a tree
|
|
310
313
|
const [state, status] = await this.store.getState(jid, consumes, dIds);
|
|
311
314
|
this.context = (0, utils_1.restoreHierarchy)(state);
|
|
312
315
|
this.assertGenerationalId(this.context.metadata.gid, gid);
|
|
@@ -316,7 +319,9 @@ class Activity {
|
|
|
316
319
|
}
|
|
317
320
|
/**
|
|
318
321
|
* if the job is created/deleted/created with the same key,
|
|
319
|
-
* the 'gid' ensures no stale messages
|
|
322
|
+
* the 'gid' ensures no stale messages (such as sleep delays)
|
|
323
|
+
* enter the workstream. Any message with a mismatched gid
|
|
324
|
+
* belongs to a prior job and can safely be ignored/dropped.
|
|
320
325
|
*/
|
|
321
326
|
assertGenerationalId(jobGID, msgGID) {
|
|
322
327
|
if (msgGID !== jobGID) {
|
|
@@ -30,7 +30,17 @@ declare class CollatorService {
|
|
|
30
30
|
static authorizeReentry(activity: Activity, multi?: RedisMulti): Promise<number>;
|
|
31
31
|
static notarizeEarlyExit(activity: Activity, multi?: RedisMulti): Promise<number>;
|
|
32
32
|
static notarizeEarlyCompletion(activity: Activity, multi?: RedisMulti): Promise<number>;
|
|
33
|
-
|
|
33
|
+
/**
|
|
34
|
+
* verifies both the concrete and synthetic keys for the activity; concrete keys
|
|
35
|
+
* exist in the original model and are effectively the 'real' keys. In reality,
|
|
36
|
+
* hook activities are atomized during compilation to create a synthetic DAG that
|
|
37
|
+
* is used to track the status of the graph in a distributed environment. The
|
|
38
|
+
* synthetic key represents different dimensional realities and is used to
|
|
39
|
+
* track re-entry overages (it distinguishes between the original and re-entry).
|
|
40
|
+
* The essential challenge is: is this a re-entry that is purposeful in
|
|
41
|
+
* order to induce cycles, or is the re-entry due to a failure in the system?
|
|
42
|
+
*/
|
|
43
|
+
static notarizeReentry(activity: Activity, guid: string, multi?: RedisMulti): Promise<number>;
|
|
34
44
|
static notarizeContinuation(activity: Activity, multi?: RedisMulti): Promise<number>;
|
|
35
45
|
static notarizeCompletion(activity: Activity, multi?: RedisMulti): Promise<number>;
|
|
36
46
|
static getDigitAtIndex(num: number, targetDigitIndex: number): number | null;
|
|
@@ -38,6 +48,15 @@ declare class CollatorService {
|
|
|
38
48
|
static isDuplicate(num: number, targetDigitIndex: number): boolean;
|
|
39
49
|
static isInactive(num: number): boolean;
|
|
40
50
|
static isPrimed(amount: number, leg: ActivityDuplex): boolean;
|
|
51
|
+
/**
|
|
52
|
+
* During compilation, the graphs are compiled into structures necessary
|
|
53
|
+
* for distributed processing; these are referred to as 'synthetic DAGs',
|
|
54
|
+
* because they are not part of the original graph, but are used to track
|
|
55
|
+
* the status of the graph in a distributed environment. This check ensures
|
|
56
|
+
* that the 'synthetic key' is not a duplicate. (which is different than
|
|
57
|
+
* saying the 'key' is not a duplicate)
|
|
58
|
+
*/
|
|
59
|
+
static verifySyntheticInteger(amount: number): void;
|
|
41
60
|
static verifyInteger(amount: number, leg: ActivityDuplex, stage: CollationStage): void;
|
|
42
61
|
static getDimensionsById(ancestors: string[], dad: string): Record<string, string>;
|
|
43
62
|
/**
|
|
@@ -72,7 +91,6 @@ declare class CollatorService {
|
|
|
72
91
|
*
|
|
73
92
|
*/
|
|
74
93
|
static bindAncestorArray(graphs: HotMeshGraph[]): void;
|
|
75
|
-
static isActivityComplete(status: number): boolean;
|
|
76
94
|
/**
|
|
77
95
|
* All activities exist on a dimensional plane. Zero
|
|
78
96
|
* is the default. A value of
|
|
@@ -67,11 +67,28 @@ class CollatorService {
|
|
|
67
67
|
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1000001 - decrement, this.getDimensionalAddress(activity), multi);
|
|
68
68
|
}
|
|
69
69
|
;
|
|
70
|
-
|
|
70
|
+
/**
|
|
71
|
+
* verifies both the concrete and synthetic keys for the activity; concrete keys
|
|
72
|
+
* exist in the original model and are effectively the 'real' keys. In reality,
|
|
73
|
+
* hook activities are atomized during compilation to create a synthetic DAG that
|
|
74
|
+
* is used to track the status of the graph in a distributed environment. The
|
|
75
|
+
* synthetic key represents different dimensional realities and is used to
|
|
76
|
+
* track re-entry overages (it distinguishes between the original and re-entry).
|
|
77
|
+
* The essential challenge is: is this a re-entry that is purposeful in
|
|
78
|
+
* order to induce cycles, or is the re-entry due to a failure in the system?
|
|
79
|
+
*/
|
|
80
|
+
static async notarizeReentry(activity, guid, multi) {
|
|
81
|
+
const jid = activity.context.metadata.jid;
|
|
82
|
+
const localMulti = multi || activity.store.getMulti();
|
|
71
83
|
//increment by 1_000_000 (indicates re-entry and is used to drive the 'dimensional address' for adjacent activities (minus 1))
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
84
|
+
await activity.store.collate(jid, activity.metadata.aid, 1000000, this.getDimensionalAddress(activity, true), localMulti);
|
|
85
|
+
await activity.store.collateSynthetic(jid, guid, 1000000, localMulti);
|
|
86
|
+
const [_amountConcrete, _amountSynthetic] = await localMulti.exec();
|
|
87
|
+
const amountConcrete = Array.isArray(_amountConcrete) ? _amountConcrete[1] : _amountConcrete;
|
|
88
|
+
const amountSynthetic = Array.isArray(_amountSynthetic) ? _amountSynthetic[1] : _amountSynthetic;
|
|
89
|
+
this.verifyInteger(amountConcrete, 2, 'enter');
|
|
90
|
+
this.verifySyntheticInteger(amountSynthetic);
|
|
91
|
+
return amountConcrete;
|
|
75
92
|
}
|
|
76
93
|
;
|
|
77
94
|
static async notarizeContinuation(activity, multi) {
|
|
@@ -119,6 +136,26 @@ class CollatorService {
|
|
|
119
136
|
this.getDigitAtIndex(amount, 1) < 9;
|
|
120
137
|
}
|
|
121
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* During compilation, the graphs are compiled into structures necessary
|
|
141
|
+
* for distributed processing; these are referred to as 'synthetic DAGs',
|
|
142
|
+
* because they are not part of the original graph, but are used to track
|
|
143
|
+
* the status of the graph in a distributed environment. This check ensures
|
|
144
|
+
* that the 'synthetic key' is not a duplicate. (which is different than
|
|
145
|
+
* saying the 'key' is not a duplicate)
|
|
146
|
+
*/
|
|
147
|
+
static verifySyntheticInteger(amount) {
|
|
148
|
+
const samount = amount.toString();
|
|
149
|
+
const isCompletedValue = parseInt(samount[samount.length - 1], 10);
|
|
150
|
+
if (isCompletedValue > 0) {
|
|
151
|
+
//already done error (ack/delete clearly failed; this is a duplicate)
|
|
152
|
+
throw new errors_1.CollationError(amount, 2, 'enter', collator_1.CollationFaultType.INACTIVE);
|
|
153
|
+
}
|
|
154
|
+
else if (amount >= 2000000) {
|
|
155
|
+
//duplicate synthetic key (todo: need to resolve/fix this!!)
|
|
156
|
+
throw new errors_1.CollationError(amount, 2, 'enter', collator_1.CollationFaultType.DUPLICATE);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
122
159
|
static verifyInteger(amount, leg, stage) {
|
|
123
160
|
let faultType;
|
|
124
161
|
if (leg === 1 && stage === 'enter') {
|
|
@@ -222,9 +259,6 @@ class CollatorService {
|
|
|
222
259
|
dfs(startingNode, []);
|
|
223
260
|
});
|
|
224
261
|
}
|
|
225
|
-
static isActivityComplete(status) {
|
|
226
|
-
return (status - 0) <= 0;
|
|
227
|
-
}
|
|
228
262
|
/**
|
|
229
263
|
* All activities exist on a dimensional plane. Zero
|
|
230
264
|
* is the default. A value of
|
|
@@ -3,8 +3,8 @@ import { HotMeshService as HotMesh } from '../hotmesh';
|
|
|
3
3
|
import { ClientConfig, Connection, HookOptions, WorkflowOptions, WorkflowSearchOptions } from '../../types/durable';
|
|
4
4
|
export declare class ClientService {
|
|
5
5
|
connection: Connection;
|
|
6
|
-
topics: string[];
|
|
7
6
|
options: WorkflowOptions;
|
|
7
|
+
static topics: string[];
|
|
8
8
|
static instances: Map<string, HotMesh | Promise<HotMesh>>;
|
|
9
9
|
constructor(config: ClientConfig);
|
|
10
10
|
getHotMeshClient: (workflowTopic: string, namespace?: string) => Promise<HotMesh>;
|
|
@@ -37,6 +37,7 @@ export declare class ClientService {
|
|
|
37
37
|
getHandle: (taskQueue: string, workflowName: string, workflowId: string, namespace?: string) => Promise<WorkflowHandleService>;
|
|
38
38
|
search: (taskQueue: string, workflowName: string, namespace: null | string, index: string, ...query: string[]) => Promise<string[]>;
|
|
39
39
|
};
|
|
40
|
+
verifyWorkflowActive(hotMesh: HotMesh, appId?: string, count?: number): Promise<boolean>;
|
|
40
41
|
activateWorkflow(hotMesh: HotMesh, appId?: string, version?: string): Promise<void>;
|
|
41
42
|
static shutdown(): Promise<void>;
|
|
42
43
|
}
|
|
@@ -8,16 +8,17 @@ const key_1 = require("../../modules/key");
|
|
|
8
8
|
const search_1 = require("./search");
|
|
9
9
|
const types_1 = require("../../types");
|
|
10
10
|
const enums_1 = require("../../modules/enums");
|
|
11
|
+
const utils_1 = require("../../modules/utils");
|
|
11
12
|
class ClientService {
|
|
12
13
|
constructor(config) {
|
|
13
|
-
this.topics = [];
|
|
14
14
|
this.getHotMeshClient = async (workflowTopic, namespace) => {
|
|
15
15
|
//use the cached instance
|
|
16
16
|
const instanceId = 'SINGLETON';
|
|
17
17
|
if (ClientService.instances.has(instanceId)) {
|
|
18
18
|
const hotMeshClient = await ClientService.instances.get(instanceId);
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
await this.verifyWorkflowActive(hotMeshClient, namespace ?? factory_1.APP_ID);
|
|
20
|
+
if (!ClientService.topics.includes(workflowTopic)) {
|
|
21
|
+
ClientService.topics.push(workflowTopic);
|
|
21
22
|
await this.createStream(hotMeshClient, workflowTopic, namespace);
|
|
22
23
|
}
|
|
23
24
|
return hotMeshClient;
|
|
@@ -174,6 +175,18 @@ class ClientService {
|
|
|
174
175
|
};
|
|
175
176
|
this.connection = config.connection;
|
|
176
177
|
}
|
|
178
|
+
async verifyWorkflowActive(hotMesh, appId = factory_1.APP_ID, count = 0) {
|
|
179
|
+
const app = await hotMesh.engine.store.getApp(appId);
|
|
180
|
+
const appVersion = app?.version;
|
|
181
|
+
if (isNaN(appVersion)) {
|
|
182
|
+
if (count > 10) {
|
|
183
|
+
throw new Error('Workflow failed to activate');
|
|
184
|
+
}
|
|
185
|
+
await (0, utils_1.sleepFor)(enums_1.HMSH_QUORUM_DELAY_MS * 2);
|
|
186
|
+
return await this.verifyWorkflowActive(hotMesh, appId, count + 1);
|
|
187
|
+
}
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
177
190
|
async activateWorkflow(hotMesh, appId = factory_1.APP_ID, version = factory_1.APP_VERSION) {
|
|
178
191
|
const app = await hotMesh.engine.store.getApp(appId);
|
|
179
192
|
const appVersion = app?.version;
|
|
@@ -203,5 +216,6 @@ class ClientService {
|
|
|
203
216
|
}
|
|
204
217
|
}
|
|
205
218
|
}
|
|
219
|
+
ClientService.topics = [];
|
|
206
220
|
ClientService.instances = new Map();
|
|
207
221
|
exports.ClientService = ClientService;
|
|
@@ -10,7 +10,6 @@ exports.DEFAULT_COEFFICIENT = exports.APP_ID = exports.APP_VERSION = exports.get
|
|
|
10
10
|
* ERROR CODES:
|
|
11
11
|
* 594: waitforsignal
|
|
12
12
|
* 592: sleepFor
|
|
13
|
-
* 595: sleep (deprecated)
|
|
14
13
|
* 596, 597, 598: fatal
|
|
15
14
|
* 599: retry
|
|
16
15
|
*/
|
|
@@ -125,19 +124,6 @@ const getWorkflowYAML = (app, version) => {
|
|
|
125
124
|
maps:
|
|
126
125
|
index: '{$self.output.data.index}'
|
|
127
126
|
signals: '{$self.output.data.signals}'
|
|
128
|
-
595:
|
|
129
|
-
schema:
|
|
130
|
-
type: object
|
|
131
|
-
properties:
|
|
132
|
-
duration:
|
|
133
|
-
type: number
|
|
134
|
-
description: sleep duration in seconds
|
|
135
|
-
index:
|
|
136
|
-
type: number
|
|
137
|
-
description: the current index
|
|
138
|
-
maps:
|
|
139
|
-
duration: '{$self.output.data.duration}'
|
|
140
|
-
index: '{$self.output.data.index}'
|
|
141
127
|
592:
|
|
142
128
|
schema:
|
|
143
129
|
type: object
|
|
@@ -232,19 +218,6 @@ const getWorkflowYAML = (app, version) => {
|
|
|
232
218
|
maps:
|
|
233
219
|
index: '{$self.output.data.index}'
|
|
234
220
|
signals: '{$self.output.data.signals}'
|
|
235
|
-
595:
|
|
236
|
-
schema:
|
|
237
|
-
type: object
|
|
238
|
-
properties:
|
|
239
|
-
duration:
|
|
240
|
-
type: number
|
|
241
|
-
description: sleep duration in seconds
|
|
242
|
-
index:
|
|
243
|
-
type: number
|
|
244
|
-
description: the current index
|
|
245
|
-
maps:
|
|
246
|
-
duration: '{$self.output.data.duration}'
|
|
247
|
-
index: '{$self.output.data.index}'
|
|
248
221
|
592:
|
|
249
222
|
schema:
|
|
250
223
|
type: object
|
|
@@ -323,57 +296,6 @@ const getWorkflowYAML = (app, version) => {
|
|
|
323
296
|
maps:
|
|
324
297
|
duration: '{siga1.output.data.duration}'
|
|
325
298
|
|
|
326
|
-
siga595:
|
|
327
|
-
title: Signal In - Sleep before trying again
|
|
328
|
-
type: await
|
|
329
|
-
topic: ${app}.sleep.execute
|
|
330
|
-
input:
|
|
331
|
-
schema:
|
|
332
|
-
type: object
|
|
333
|
-
properties:
|
|
334
|
-
duration:
|
|
335
|
-
type: number
|
|
336
|
-
index:
|
|
337
|
-
type: number
|
|
338
|
-
workflowId:
|
|
339
|
-
type: string
|
|
340
|
-
parentWorkflowId:
|
|
341
|
-
type: string
|
|
342
|
-
originJobId:
|
|
343
|
-
type: string
|
|
344
|
-
maps:
|
|
345
|
-
duration: '{sigw1.output.data.duration}'
|
|
346
|
-
index: '{sigw1.output.data.index}'
|
|
347
|
-
parentWorkflowId:
|
|
348
|
-
'@pipe':
|
|
349
|
-
- ['{$job.metadata.jid}', '-s']
|
|
350
|
-
- ['{@string.concat}']
|
|
351
|
-
originJobId:
|
|
352
|
-
'@pipe':
|
|
353
|
-
- ['{t1.output.data.originJobId}', '{t1.output.data.originJobId}', '{$job.metadata.jid}']
|
|
354
|
-
- ['{@conditional.ternary}']
|
|
355
|
-
|
|
356
|
-
workflowId:
|
|
357
|
-
'@pipe':
|
|
358
|
-
- ['-', '{$job.metadata.jid}', '-$sleep', '{sig.output.metadata.dad}', '-', '{sigw1.output.data.index}']
|
|
359
|
-
- ['{@string.concat}']
|
|
360
|
-
output:
|
|
361
|
-
schema:
|
|
362
|
-
type: object
|
|
363
|
-
properties:
|
|
364
|
-
done:
|
|
365
|
-
type: boolean
|
|
366
|
-
maps:
|
|
367
|
-
done: '{sigw1.output.data.done}'
|
|
368
|
-
|
|
369
|
-
sigc595:
|
|
370
|
-
title: Signal In - Goto Activity siga1
|
|
371
|
-
type: cycle
|
|
372
|
-
ancestor: siga1
|
|
373
|
-
input:
|
|
374
|
-
maps:
|
|
375
|
-
duration: '{siga1.output.data.duration}'
|
|
376
|
-
|
|
377
299
|
siga592:
|
|
378
300
|
title: Signal In - Sleep For a duration and then cycle/goto
|
|
379
301
|
type: hook
|
|
@@ -467,56 +389,6 @@ const getWorkflowYAML = (app, version) => {
|
|
|
467
389
|
maps:
|
|
468
390
|
duration: '{a1.output.data.duration}'
|
|
469
391
|
|
|
470
|
-
a595:
|
|
471
|
-
title: Sleep before trying again
|
|
472
|
-
type: await
|
|
473
|
-
topic: ${app}.sleep.execute
|
|
474
|
-
input:
|
|
475
|
-
schema:
|
|
476
|
-
type: object
|
|
477
|
-
properties:
|
|
478
|
-
duration:
|
|
479
|
-
type: number
|
|
480
|
-
index:
|
|
481
|
-
type: number
|
|
482
|
-
workflowId:
|
|
483
|
-
type: string
|
|
484
|
-
parentWorkflowId:
|
|
485
|
-
type: string
|
|
486
|
-
originJobId:
|
|
487
|
-
type: string
|
|
488
|
-
maps:
|
|
489
|
-
duration: '{w1.output.data.duration}'
|
|
490
|
-
index: '{w1.output.data.index}'
|
|
491
|
-
parentWorkflowId:
|
|
492
|
-
'@pipe':
|
|
493
|
-
- ['{$job.metadata.jid}', '-s']
|
|
494
|
-
- ['{@string.concat}']
|
|
495
|
-
originJobId:
|
|
496
|
-
'@pipe':
|
|
497
|
-
- ['{t1.output.data.originJobId}', '{t1.output.data.originJobId}', '{$job.metadata.jid}']
|
|
498
|
-
- ['{@conditional.ternary}']
|
|
499
|
-
workflowId:
|
|
500
|
-
'@pipe':
|
|
501
|
-
- ['-', '{$job.metadata.jid}', '-$sleep-', '{w1.output.data.index}']
|
|
502
|
-
- ['{@string.concat}']
|
|
503
|
-
output:
|
|
504
|
-
schema:
|
|
505
|
-
type: object
|
|
506
|
-
properties:
|
|
507
|
-
done:
|
|
508
|
-
type: boolean
|
|
509
|
-
maps:
|
|
510
|
-
done: '{w1.output.data.done}'
|
|
511
|
-
|
|
512
|
-
c595:
|
|
513
|
-
title: Goto Activity a1
|
|
514
|
-
type: cycle
|
|
515
|
-
ancestor: a1
|
|
516
|
-
input:
|
|
517
|
-
maps:
|
|
518
|
-
duration: '{a1.output.data.duration}'
|
|
519
|
-
|
|
520
392
|
a592:
|
|
521
393
|
title: Sleep For a duration and then cycle/goto
|
|
522
394
|
type: hook
|
|
@@ -574,9 +446,6 @@ const getWorkflowYAML = (app, version) => {
|
|
|
574
446
|
- to: siga594
|
|
575
447
|
conditions:
|
|
576
448
|
code: 594
|
|
577
|
-
- to: siga595
|
|
578
|
-
conditions:
|
|
579
|
-
code: 595
|
|
580
449
|
- to: siga592
|
|
581
450
|
conditions:
|
|
582
451
|
code: 592
|
|
@@ -585,8 +454,6 @@ const getWorkflowYAML = (app, version) => {
|
|
|
585
454
|
code: 599
|
|
586
455
|
siga594:
|
|
587
456
|
- to: sigc594
|
|
588
|
-
siga595:
|
|
589
|
-
- to: sigc595
|
|
590
457
|
siga592:
|
|
591
458
|
- to: sigc592
|
|
592
459
|
siga599:
|
|
@@ -597,9 +464,6 @@ const getWorkflowYAML = (app, version) => {
|
|
|
597
464
|
- to: a594
|
|
598
465
|
conditions:
|
|
599
466
|
code: 594
|
|
600
|
-
- to: a595
|
|
601
|
-
conditions:
|
|
602
|
-
code: 595
|
|
603
467
|
- to: a592
|
|
604
468
|
conditions:
|
|
605
469
|
code: 592
|
|
@@ -611,8 +475,6 @@ const getWorkflowYAML = (app, version) => {
|
|
|
611
475
|
code: [200, 598, 597, 596]
|
|
612
476
|
a594:
|
|
613
477
|
- to: c594
|
|
614
|
-
a595:
|
|
615
|
-
- to: c595
|
|
616
478
|
a592:
|
|
617
479
|
- to: c592
|
|
618
480
|
a599:
|
|
@@ -194,6 +194,9 @@ class MeshOSService {
|
|
|
194
194
|
return await this.find(options.options ?? {}, ...args);
|
|
195
195
|
}
|
|
196
196
|
static generateSearchQuery(query) {
|
|
197
|
+
if (!Array.isArray(query) || query.length === 0) {
|
|
198
|
+
return '*';
|
|
199
|
+
}
|
|
197
200
|
const my = new this();
|
|
198
201
|
let queryString = query.map(q => {
|
|
199
202
|
const { field, is, value, type } = q;
|
|
@@ -187,21 +187,6 @@ class WorkerService {
|
|
|
187
187
|
catch (err) {
|
|
188
188
|
//not an error...just a trigger to sleep
|
|
189
189
|
if (err instanceof errors_1.DurableSleepForError) {
|
|
190
|
-
return {
|
|
191
|
-
status: stream_1.StreamStatus.SUCCESS,
|
|
192
|
-
code: err.code,
|
|
193
|
-
metadata: { ...data.metadata },
|
|
194
|
-
data: {
|
|
195
|
-
code: err.code,
|
|
196
|
-
message: JSON.stringify({ duration: err.duration, index: err.index, dimension: err.dimension }),
|
|
197
|
-
duration: err.duration,
|
|
198
|
-
index: err.index,
|
|
199
|
-
dimension: err.dimension
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
//deprecated format; not an error...just a trigger to sleep
|
|
203
|
-
}
|
|
204
|
-
else if (err instanceof errors_1.DurableSleepError) {
|
|
205
190
|
return {
|
|
206
191
|
status: stream_1.StreamStatus.SUCCESS,
|
|
207
192
|
code: err.code,
|
|
@@ -112,15 +112,6 @@ export declare class WorkflowService {
|
|
|
112
112
|
* @returns {Promise<number>}
|
|
113
113
|
*/
|
|
114
114
|
static sleepFor(duration: string): Promise<number>;
|
|
115
|
-
/**
|
|
116
|
-
* Sleeps the workflow for a duration. As the function is reentrant,
|
|
117
|
-
* upon reentry, the function will traverse prior execution paths up
|
|
118
|
-
* until the sleep command and then resume execution from that point.
|
|
119
|
-
* @param {string} duration - for example: '1 minute', '2 hours', '3 days'
|
|
120
|
-
* @returns {Promise<number>}
|
|
121
|
-
* @deprecated - use `sleepFor` instead
|
|
122
|
-
*/
|
|
123
|
-
static sleep(duration: string): Promise<number>;
|
|
124
115
|
/**
|
|
125
116
|
* Waits for a signal to awaken
|
|
126
117
|
* @param {string[]} signals - the signals to wait for
|
|
@@ -338,35 +338,6 @@ class WorkflowService {
|
|
|
338
338
|
}
|
|
339
339
|
return seconds;
|
|
340
340
|
}
|
|
341
|
-
/**
|
|
342
|
-
* Sleeps the workflow for a duration. As the function is reentrant,
|
|
343
|
-
* upon reentry, the function will traverse prior execution paths up
|
|
344
|
-
* until the sleep command and then resume execution from that point.
|
|
345
|
-
* @param {string} duration - for example: '1 minute', '2 hours', '3 days'
|
|
346
|
-
* @returns {Promise<number>}
|
|
347
|
-
* @deprecated - use `sleepFor` instead
|
|
348
|
-
*/
|
|
349
|
-
static async sleep(duration) {
|
|
350
|
-
const seconds = (0, ms_1.default)(duration) / 1000;
|
|
351
|
-
const store = storage_1.asyncLocalStorage.getStore();
|
|
352
|
-
const COUNTER = store.get('counter');
|
|
353
|
-
const execIndex = COUNTER.counter = COUNTER.counter + 1;
|
|
354
|
-
const workflowId = store.get('workflowId');
|
|
355
|
-
const workflowTopic = store.get('workflowTopic');
|
|
356
|
-
const workflowDimension = store.get('workflowDimension') ?? '';
|
|
357
|
-
const namespace = store.get('namespace');
|
|
358
|
-
const sleepJobId = `-${workflowId}-$sleep${workflowDimension}-${execIndex}`;
|
|
359
|
-
try {
|
|
360
|
-
const hotMeshClient = await worker_1.WorkerService.getHotMesh(workflowTopic, { namespace });
|
|
361
|
-
await hotMeshClient.getState(`${hotMeshClient.appId}.sleep.execute`, sleepJobId);
|
|
362
|
-
//if no error is thrown, we've already slept, return the delay
|
|
363
|
-
return seconds;
|
|
364
|
-
}
|
|
365
|
-
catch (e) {
|
|
366
|
-
// spawn a new sleep job if error code 595 is thrown by the worker)
|
|
367
|
-
throw new errors_1.DurableSleepError(workflowId, seconds, execIndex, workflowDimension);
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
341
|
/**
|
|
371
342
|
* Waits for a signal to awaken
|
|
372
343
|
* @param {string[]} signals - the signals to wait for
|
|
@@ -66,7 +66,7 @@ declare class EngineService {
|
|
|
66
66
|
interrupt(topic: string, jobId: string, options?: JobInterruptOptions): Promise<string>;
|
|
67
67
|
scrub(jobId: string): Promise<void>;
|
|
68
68
|
hook(topic: string, data: JobData, status?: StreamStatus, code?: StreamCode): Promise<string>;
|
|
69
|
-
hookTime(jobId: string, gId: string,
|
|
69
|
+
hookTime(jobId: string, gId: string, topicOrActivity: string, type?: WorkListTaskType): Promise<string | void>;
|
|
70
70
|
hookAll(hookTopic: string, data: JobData, keyResolver: JobStatsInput, queryFacets?: string[]): Promise<string[]>;
|
|
71
71
|
pub(topic: string, data: JobData, context?: JobState): Promise<string>;
|
|
72
72
|
sub(topic: string, callback: JobMessageCallback): Promise<void>;
|