@hotmeshio/hotmesh 0.0.56 → 0.0.58
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 +10 -10
- package/build/modules/enums.js +10 -1
- package/build/modules/key.d.ts +38 -0
- package/build/modules/key.js +46 -4
- package/build/modules/utils.d.ts +9 -0
- package/build/modules/utils.js +19 -1
- package/build/package.json +1 -1
- package/build/services/activities/activity.d.ts +28 -0
- package/build/services/activities/activity.js +46 -1
- package/build/services/activities/await.js +4 -0
- package/build/services/activities/cycle.d.ts +7 -0
- package/build/services/activities/cycle.js +16 -1
- package/build/services/activities/hook.d.ts +6 -0
- package/build/services/activities/hook.js +12 -2
- package/build/services/activities/interrupt.js +8 -0
- package/build/services/activities/signal.d.ts +6 -0
- package/build/services/activities/signal.js +15 -0
- package/build/services/activities/trigger.d.ts +4 -0
- package/build/services/activities/trigger.js +7 -1
- package/build/services/activities/worker.js +4 -0
- package/build/services/collator/index.d.ts +70 -0
- package/build/services/collator/index.js +91 -1
- package/build/services/compiler/deployer.js +38 -6
- package/build/services/compiler/index.d.ts +15 -0
- package/build/services/compiler/index.js +20 -0
- package/build/services/compiler/validator.d.ts +3 -0
- package/build/services/compiler/validator.js +25 -0
- package/build/services/connector/clients/ioredis.js +2 -0
- package/build/services/connector/clients/redis.js +2 -0
- package/build/services/connector/index.js +2 -0
- package/build/services/durable/client.d.ts +20 -0
- package/build/services/durable/client.js +25 -0
- package/build/services/durable/exporter.d.ts +22 -0
- package/build/services/durable/exporter.js +30 -1
- package/build/services/durable/handle.d.ts +36 -0
- package/build/services/durable/handle.js +46 -0
- package/build/services/durable/index.d.ts +4 -0
- package/build/services/durable/index.js +4 -0
- package/build/services/durable/schemas/factory.d.ts +29 -0
- package/build/services/durable/schemas/factory.js +29 -0
- package/build/services/durable/search.d.ts +97 -0
- package/build/services/durable/search.js +108 -10
- package/build/services/durable/worker.js +35 -6
- package/build/services/durable/workflow.d.ts +118 -0
- package/build/services/durable/workflow.js +153 -6
- package/build/services/engine/index.d.ts +5 -0
- package/build/services/engine/index.js +43 -1
- package/build/services/exporter/index.d.ts +27 -0
- package/build/services/exporter/index.js +33 -0
- package/build/services/hotmesh/index.js +8 -0
- package/build/services/logger/index.js +2 -0
- package/build/services/mapper/index.d.ts +14 -0
- package/build/services/mapper/index.js +14 -0
- package/build/services/pipe/functions/date.d.ts +7 -0
- package/build/services/pipe/functions/date.js +7 -0
- package/build/services/pipe/functions/math.js +2 -0
- package/build/services/pipe/index.d.ts +15 -0
- package/build/services/pipe/index.js +23 -2
- package/build/services/quorum/index.d.ts +7 -0
- package/build/services/quorum/index.js +21 -0
- package/build/services/reporter/index.d.ts +5 -0
- package/build/services/reporter/index.js +9 -0
- package/build/services/router/index.d.ts +9 -0
- package/build/services/router/index.js +30 -2
- package/build/services/serializer/index.js +23 -6
- package/build/services/store/cache.d.ts +19 -0
- package/build/services/store/cache.js +19 -0
- package/build/services/store/clients/ioredis.js +1 -0
- package/build/services/store/index.d.ts +55 -0
- package/build/services/store/index.js +81 -5
- package/build/services/stream/clients/ioredis.js +4 -1
- package/build/services/task/index.d.ts +9 -0
- package/build/services/task/index.js +31 -0
- package/build/services/telemetry/index.d.ts +7 -0
- package/build/services/telemetry/index.js +13 -1
- package/build/services/worker/index.d.ts +4 -0
- package/build/services/worker/index.js +6 -2
- package/build/types/activity.d.ts +81 -0
- package/build/types/durable.d.ts +256 -0
- package/build/types/exporter.d.ts +13 -0
- package/build/types/hotmesh.d.ts +10 -1
- package/build/types/hotmesh.js +3 -0
- package/build/types/index.js +1 -1
- package/build/types/job.d.ts +85 -0
- package/build/types/pipe.d.ts +65 -0
- package/build/types/quorum.d.ts +14 -0
- package/build/types/redis.d.ts +6 -0
- package/build/types/stream.d.ts +58 -0
- package/build/types/stream.js +4 -0
- package/package.json +1 -1
- package/types/durable.ts +10 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# HotMesh
|
|
2
2
|

|
|
3
3
|
|
|
4
|
-
HotMesh transforms Redis into
|
|
4
|
+
HotMesh transforms Redis into an Orchestration Engine.
|
|
5
5
|
|
|
6
6
|
*Write functions in your own preferred style, and let Redis govern their execution, reliably and durably.*
|
|
7
7
|
|
|
@@ -295,28 +295,28 @@ const hotMesh = await HotMesh.init({
|
|
|
295
295
|
```
|
|
296
296
|
|
|
297
297
|
### Observability
|
|
298
|
-
Workflows and activities are run according to the rules you define, offering [Graph-Oriented](https://github.com/hotmeshio/sdk-typescript/
|
|
298
|
+
Workflows and activities are run according to the rules you define, offering [Graph-Oriented](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/system_lifecycle.md#telemetry) telemetry insights into your legacy function executions.
|
|
299
299
|
|
|
300
300
|
## FAQ
|
|
301
|
-
Refer to the [FAQ](https://github.com/hotmeshio/sdk-typescript/
|
|
301
|
+
Refer to the [FAQ](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/faq.md) for terminology, definitions, and an exploration of how HotMesh facilitates orchestration use cases.
|
|
302
302
|
|
|
303
303
|
## Quick Start
|
|
304
|
-
Refer to the [Quick Start](https://github.com/hotmeshio/sdk-typescript/
|
|
304
|
+
Refer to the [Quick Start](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/quickstart.md) for sample YAML workflows you can copy, paste, and modify to get started.
|
|
305
305
|
|
|
306
306
|
## Developer Guide
|
|
307
|
-
For more details on the complete development process, including information about schemas, APIs, and deployment, consult the [Developer Guide](https://github.com/hotmeshio/sdk-typescript/
|
|
307
|
+
For more details on the complete development process, including information about schemas, APIs, and deployment, consult the [Developer Guide](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/developer_guide.md).
|
|
308
308
|
|
|
309
309
|
## Model Driven Development
|
|
310
|
-
[Model Driven Development](https://github.com/hotmeshio/sdk-typescript/
|
|
310
|
+
[Model Driven Development](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/model_driven_development.md) is an established strategy for managing process-oriented tasks. Check out this guide to understand its foundational principles.
|
|
311
311
|
|
|
312
312
|
## Data Mapping
|
|
313
|
-
Exchanging data between activities is central to HotMesh. For detailed information on supported functions and the functional mapping syntax (@pipes), see the [Data Mapping Overview](https://github.com/hotmeshio/sdk-typescript/
|
|
313
|
+
Exchanging data between activities is central to HotMesh. For detailed information on supported functions and the functional mapping syntax (@pipes), see the [Data Mapping Overview](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/data_mapping.md).
|
|
314
314
|
|
|
315
315
|
## Composition
|
|
316
|
-
While the simplest graphs are linear, detailing a consistent sequence of non-cyclical activities, graphs can be layered to represent intricate business scenarios. Some can even be designed to accommodate long-lasting workflows that span months. For more details, check out the [Composable Workflow Guide](https://github.com/hotmeshio/sdk-typescript/
|
|
316
|
+
While the simplest graphs are linear, detailing a consistent sequence of non-cyclical activities, graphs can be layered to represent intricate business scenarios. Some can even be designed to accommodate long-lasting workflows that span months. For more details, check out the [Composable Workflow Guide](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/composable_workflow.md).
|
|
317
317
|
|
|
318
318
|
## Distributed Orchestration
|
|
319
|
-
HotMesh is a distributed orchestration engine. Refer to the [Distributed Orchestration Guide](https://github.com/hotmeshio/sdk-typescript/
|
|
319
|
+
HotMesh is a distributed orchestration engine. Refer to the [Distributed Orchestration Guide](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/distributed_orchestration.md) for a detailed breakdown of the approach.
|
|
320
320
|
|
|
321
321
|
## System Lifecycle
|
|
322
|
-
Gain insight into HotMesh's monitoring, exception handling, and alarm configurations via the [System Lifecycle Guide](https://github.com/hotmeshio/sdk-typescript/
|
|
322
|
+
Gain insight into HotMesh's monitoring, exception handling, and alarm configurations via the [System Lifecycle Guide](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/system_lifecycle.md).
|
package/build/modules/enums.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
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_DURABLE_EXP_BACKOFF = exports.HMSH_DURABLE_MAX_INTERVAL = exports.HMSH_DURABLE_MAX_ATTEMPTS = 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_QUORUM_ROLLCALL_CYCLES = 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_WAIT = exports.HMSH_CODE_DURABLE_PROXY = exports.HMSH_CODE_DURABLE_CHILD = exports.HMSH_CODE_DURABLE_ALL = exports.HMSH_CODE_DURABLE_SLEEP = 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
|
+
// HOTMESH SYSTEM
|
|
4
5
|
exports.HMSH_LOGLEVEL = process.env.HMSH_LOGLEVEL || 'info';
|
|
6
|
+
// HOTMESH STATUS CODES
|
|
5
7
|
exports.HMSH_CODE_SUCCESS = 200;
|
|
6
8
|
exports.HMSH_CODE_PENDING = 202;
|
|
7
9
|
exports.HMSH_CODE_NOTFOUND = 404;
|
|
@@ -9,6 +11,7 @@ exports.HMSH_CODE_INTERRUPT = 410;
|
|
|
9
11
|
exports.HMSH_CODE_UNKNOWN = 500;
|
|
10
12
|
exports.HMSH_CODE_TIMEOUT = 504;
|
|
11
13
|
exports.HMSH_CODE_UNACKED = 999;
|
|
14
|
+
// DURABLE STATUS CODES
|
|
12
15
|
exports.HMSH_CODE_DURABLE_SLEEP = 588;
|
|
13
16
|
exports.HMSH_CODE_DURABLE_ALL = 589;
|
|
14
17
|
exports.HMSH_CODE_DURABLE_CHILD = 590;
|
|
@@ -18,15 +21,20 @@ exports.HMSH_CODE_DURABLE_TIMEOUT = 596;
|
|
|
18
21
|
exports.HMSH_CODE_DURABLE_MAXED = 597;
|
|
19
22
|
exports.HMSH_CODE_DURABLE_FATAL = 598;
|
|
20
23
|
exports.HMSH_CODE_DURABLE_RETRYABLE = 599;
|
|
24
|
+
// HOTMESH MESSAGES
|
|
21
25
|
exports.HMSH_STATUS_UNKNOWN = 'unknown';
|
|
22
|
-
|
|
26
|
+
// QUORUM
|
|
27
|
+
exports.HMSH_QUORUM_ROLLCALL_CYCLES = 12; //max iterations
|
|
23
28
|
exports.HMSH_QUORUM_DELAY_MS = 250;
|
|
24
29
|
exports.HMSH_ACTIVATION_MAX_RETRY = 3;
|
|
30
|
+
// ENGINE
|
|
25
31
|
exports.HMSH_OTT_WAIT_TIME = parseInt(process.env.HMSH_OTT_WAIT_TIME, 10) || 1000;
|
|
26
32
|
exports.HMSH_EXPIRE_JOB_SECONDS = parseInt(process.env.HMSH_EXPIRE_JOB_SECONDS, 10) || 1;
|
|
33
|
+
// STREAM ROUTER
|
|
27
34
|
exports.HMSH_MAX_RETRIES = parseInt(process.env.HMSH_MAX_RETRIES, 10) || 3;
|
|
28
35
|
exports.HMSH_MAX_TIMEOUT_MS = parseInt(process.env.HMSH_MAX_TIMEOUT_MS, 10) || 60000;
|
|
29
36
|
exports.HMSH_GRADUATED_INTERVAL_MS = parseInt(process.env.HMSH_GRADUATED_INTERVAL_MS, 10) || 5000;
|
|
37
|
+
// DURABLE
|
|
30
38
|
exports.HMSH_DURABLE_MAX_ATTEMPTS = 3;
|
|
31
39
|
exports.HMSH_DURABLE_MAX_INTERVAL = '120s';
|
|
32
40
|
exports.HMSH_DURABLE_EXP_BACKOFF = 10;
|
|
@@ -36,6 +44,7 @@ exports.HMSH_BLOCK_TIME_MS = process.env.HMSH_BLOCK_TIME_MS ? parseInt(process.e
|
|
|
36
44
|
exports.HMSH_XCLAIM_DELAY_MS = parseInt(process.env.HMSH_XCLAIM_DELAY_MS, 10) || 1000 * 60;
|
|
37
45
|
exports.HMSH_XCLAIM_COUNT = parseInt(process.env.HMSH_XCLAIM_COUNT, 10) || 3;
|
|
38
46
|
exports.HMSH_XPENDING_COUNT = parseInt(process.env.HMSH_XPENDING_COUNT, 10) || 10;
|
|
47
|
+
// TASK WORKER
|
|
39
48
|
exports.HMSH_EXPIRE_DURATION = parseInt(process.env.HMSH_EXPIRE_DURATION, 10) || 1;
|
|
40
49
|
const BASE_FIDELITY_SECONDS = 5;
|
|
41
50
|
const TEST_FIDELITY_SECONDS = 1;
|
package/build/modules/key.d.ts
CHANGED
|
@@ -1,10 +1,48 @@
|
|
|
1
1
|
import { KeyStoreParams, KeyType } from '../types/hotmesh';
|
|
2
|
+
/**
|
|
3
|
+
* Keys
|
|
4
|
+
*
|
|
5
|
+
* hmsh -> {hash} hotmesh config {version: "0.0.1", namespace: "hmsh"}
|
|
6
|
+
* hmsh:a:<appid> -> {hash} app profile { "id": "appid", "version": "2", "versions/1": "GMT", "versions/2": "GMT"}
|
|
7
|
+
* hmsh:<appid>:e:<engineId> -> {string} setnx to ensure only one engine of given id
|
|
8
|
+
* hmsh:<appid>:w: -> {zset} work items/tasks an engine must do like garbage collect or hook a set of matching records (hookAll)
|
|
9
|
+
* hmsh:<appid>:t: -> {zset} an ordered set of list (work lists) ids
|
|
10
|
+
* hmsh:<appid>:t:<timeValue?> -> {list} a worklist of `jobId+activityId` items that should be awakened
|
|
11
|
+
* hmsh:<appid>:q: -> {hash} quorum-wide messages
|
|
12
|
+
* hmsh:<appid>:q:<ngnid> -> {hash} engine-targeted messages (targeted quorum-oriented message)
|
|
13
|
+
* hmsh:<appid>:j:<jobid> -> {hash} job data
|
|
14
|
+
* hmsh:<appid>:j:<jobid>:<activityid> -> {hash} job activity data (a1)
|
|
15
|
+
* hmsh:<appid>:s:<jobkey>:<dateTime> -> {hash} job stats (general)
|
|
16
|
+
* hmsh:<appid>:s:<jobkey>:<dateTime>:mdn:<field/path>:<fieldvalue> -> {zset} job stats (median)
|
|
17
|
+
* hmsh:<appid>:s:<jobkey>:<dateTime>:index:<field/path>:<fieldvalue> -> {list} job stats (index of jobid[])
|
|
18
|
+
* hmsh:<appid>:v:<version>:activities -> {hash} schemas [cache]
|
|
19
|
+
* hmsh:<appid>:v:<version>:transitions -> {hash} transitions [cache]
|
|
20
|
+
* hmsh:<appid>:v:<version>:subscriptions -> {hash} subscriptions [cache]
|
|
21
|
+
* hmsh:<appid>:x: -> {xstream} when an engine is sent or reads a buffered task (engines read from their custom topic)
|
|
22
|
+
* hmsh:<appid>:x:<topic> -> {xstream} when a worker is sent or reads a buffered task (workers read from their custom topic)
|
|
23
|
+
* hmsh:<appid>:hooks -> {hash} hook patterns/rules; set at compile time
|
|
24
|
+
* hmsh:<appid>:signals -> {hash} dynamic hook signals (hget/hdel) when resolving (always self-clean); added/removed at runtime
|
|
25
|
+
* hmsh:<appid>:sym:keys: -> {hash} list of symbol ranges and :cursor assigned at version deploy time for job keys
|
|
26
|
+
* hmsh:<appid>:sym:keys:<activityid|$subscribes> -> {hash} list of symbols based upon schema enums (initially) and adaptively optimized (later) during runtime; if '$subscribes' is used as the activityid, it is a top-level `job` symbol set (for job keys)
|
|
27
|
+
* hmsh:<appid>:sym:vals: -> {hash} list of symbols for job values across all app versions
|
|
28
|
+
*/
|
|
2
29
|
declare const HMNS = "hmsh";
|
|
3
30
|
declare const KEYSEP = ":";
|
|
4
31
|
declare const VALSEP = "::";
|
|
5
32
|
declare const WEBSEP = "::";
|
|
6
33
|
declare const TYPSEP = "::";
|
|
7
34
|
declare class KeyService {
|
|
35
|
+
/**
|
|
36
|
+
* returns a key that can be used to access a value in the key/value store
|
|
37
|
+
* appropriate for the given key type; the keys have an implicit hierarchy
|
|
38
|
+
* and are used to organize data in the store in a tree-like structure
|
|
39
|
+
* via the use of colons as separators. The top-level entity is the hmsh manifest.
|
|
40
|
+
* This file will reveal the full scope of what is on the server (apps, versions, etc)
|
|
41
|
+
* @param namespace
|
|
42
|
+
* @param keyType
|
|
43
|
+
* @param params
|
|
44
|
+
* @returns {string}
|
|
45
|
+
*/
|
|
8
46
|
static mintKey(namespace: string, keyType: KeyType, params: KeyStoreParams): string;
|
|
9
47
|
}
|
|
10
48
|
export { KeyService, KeyType, KeyStoreParams, HMNS, KEYSEP, TYPSEP, WEBSEP, VALSEP };
|
package/build/modules/key.js
CHANGED
|
@@ -3,17 +3,55 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.VALSEP = exports.WEBSEP = exports.TYPSEP = exports.KEYSEP = exports.HMNS = exports.KeyType = exports.KeyService = void 0;
|
|
4
4
|
const hotmesh_1 = require("../types/hotmesh");
|
|
5
5
|
Object.defineProperty(exports, "KeyType", { enumerable: true, get: function () { return hotmesh_1.KeyType; } });
|
|
6
|
+
/**
|
|
7
|
+
* Keys
|
|
8
|
+
*
|
|
9
|
+
* hmsh -> {hash} hotmesh config {version: "0.0.1", namespace: "hmsh"}
|
|
10
|
+
* hmsh:a:<appid> -> {hash} app profile { "id": "appid", "version": "2", "versions/1": "GMT", "versions/2": "GMT"}
|
|
11
|
+
* hmsh:<appid>:e:<engineId> -> {string} setnx to ensure only one engine of given id
|
|
12
|
+
* hmsh:<appid>:w: -> {zset} work items/tasks an engine must do like garbage collect or hook a set of matching records (hookAll)
|
|
13
|
+
* hmsh:<appid>:t: -> {zset} an ordered set of list (work lists) ids
|
|
14
|
+
* hmsh:<appid>:t:<timeValue?> -> {list} a worklist of `jobId+activityId` items that should be awakened
|
|
15
|
+
* hmsh:<appid>:q: -> {hash} quorum-wide messages
|
|
16
|
+
* hmsh:<appid>:q:<ngnid> -> {hash} engine-targeted messages (targeted quorum-oriented message)
|
|
17
|
+
* hmsh:<appid>:j:<jobid> -> {hash} job data
|
|
18
|
+
* hmsh:<appid>:j:<jobid>:<activityid> -> {hash} job activity data (a1)
|
|
19
|
+
* hmsh:<appid>:s:<jobkey>:<dateTime> -> {hash} job stats (general)
|
|
20
|
+
* hmsh:<appid>:s:<jobkey>:<dateTime>:mdn:<field/path>:<fieldvalue> -> {zset} job stats (median)
|
|
21
|
+
* hmsh:<appid>:s:<jobkey>:<dateTime>:index:<field/path>:<fieldvalue> -> {list} job stats (index of jobid[])
|
|
22
|
+
* hmsh:<appid>:v:<version>:activities -> {hash} schemas [cache]
|
|
23
|
+
* hmsh:<appid>:v:<version>:transitions -> {hash} transitions [cache]
|
|
24
|
+
* hmsh:<appid>:v:<version>:subscriptions -> {hash} subscriptions [cache]
|
|
25
|
+
* hmsh:<appid>:x: -> {xstream} when an engine is sent or reads a buffered task (engines read from their custom topic)
|
|
26
|
+
* hmsh:<appid>:x:<topic> -> {xstream} when a worker is sent or reads a buffered task (workers read from their custom topic)
|
|
27
|
+
* hmsh:<appid>:hooks -> {hash} hook patterns/rules; set at compile time
|
|
28
|
+
* hmsh:<appid>:signals -> {hash} dynamic hook signals (hget/hdel) when resolving (always self-clean); added/removed at runtime
|
|
29
|
+
* hmsh:<appid>:sym:keys: -> {hash} list of symbol ranges and :cursor assigned at version deploy time for job keys
|
|
30
|
+
* hmsh:<appid>:sym:keys:<activityid|$subscribes> -> {hash} list of symbols based upon schema enums (initially) and adaptively optimized (later) during runtime; if '$subscribes' is used as the activityid, it is a top-level `job` symbol set (for job keys)
|
|
31
|
+
* hmsh:<appid>:sym:vals: -> {hash} list of symbols for job values across all app versions
|
|
32
|
+
*/
|
|
6
33
|
const HMNS = "hmsh";
|
|
7
34
|
exports.HMNS = HMNS;
|
|
8
|
-
const KEYSEP = ':';
|
|
35
|
+
const KEYSEP = ':'; //default delimiter for keys
|
|
9
36
|
exports.KEYSEP = KEYSEP;
|
|
10
|
-
const VALSEP = '::';
|
|
37
|
+
const VALSEP = '::'; //default delimiter for vals
|
|
11
38
|
exports.VALSEP = VALSEP;
|
|
12
|
-
const WEBSEP = '::';
|
|
39
|
+
const WEBSEP = '::'; //default delimiter for webhook vals
|
|
13
40
|
exports.WEBSEP = WEBSEP;
|
|
14
|
-
const TYPSEP = '::';
|
|
41
|
+
const TYPSEP = '::'; //delimiter for ZSET task typing (how should a list be used?)
|
|
15
42
|
exports.TYPSEP = TYPSEP;
|
|
16
43
|
class KeyService {
|
|
44
|
+
/**
|
|
45
|
+
* returns a key that can be used to access a value in the key/value store
|
|
46
|
+
* appropriate for the given key type; the keys have an implicit hierarchy
|
|
47
|
+
* and are used to organize data in the store in a tree-like structure
|
|
48
|
+
* via the use of colons as separators. The top-level entity is the hmsh manifest.
|
|
49
|
+
* This file will reveal the full scope of what is on the server (apps, versions, etc)
|
|
50
|
+
* @param namespace
|
|
51
|
+
* @param keyType
|
|
52
|
+
* @param params
|
|
53
|
+
* @returns {string}
|
|
54
|
+
*/
|
|
17
55
|
static mintKey(namespace, keyType, params) {
|
|
18
56
|
switch (keyType) {
|
|
19
57
|
case hotmesh_1.KeyType.HOTMESH:
|
|
@@ -45,12 +83,16 @@ class KeyService {
|
|
|
45
83
|
case hotmesh_1.KeyType.SUBSCRIPTION_PATTERNS:
|
|
46
84
|
return `${namespace}:${params.appId}:v:${params.appVersion}:transitions`;
|
|
47
85
|
case hotmesh_1.KeyType.HOOKS:
|
|
86
|
+
//`hooks` provide the pattern to resolve a value
|
|
48
87
|
return `${namespace}:${params.appId}:hooks`;
|
|
49
88
|
case hotmesh_1.KeyType.SIGNALS:
|
|
89
|
+
//`signals` provide the registry of resolved values that link back to paused jobs
|
|
50
90
|
return `${namespace}:${params.appId}:signals`;
|
|
51
91
|
case hotmesh_1.KeyType.SYMKEYS:
|
|
92
|
+
//`symbol keys` provide the registry of replacement values for job keys
|
|
52
93
|
return `${namespace}:${params.appId}:sym:keys:${params.activityId || ''}`;
|
|
53
94
|
case hotmesh_1.KeyType.SYMVALS:
|
|
95
|
+
//`symbol vals` provide the registry of replacement values for job vals
|
|
54
96
|
return `${namespace}:${params.appId}:sym:vals:`;
|
|
55
97
|
case hotmesh_1.KeyType.STREAMS:
|
|
56
98
|
return `${namespace}:${params.appId || ''}:x:${params.topic || ''}`;
|
package/build/modules/utils.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { StreamCode, StreamStatus } from "../types/stream";
|
|
|
7
7
|
import { SystemHealth } from '../types/quorum';
|
|
8
8
|
export declare function getSystemHealth(): Promise<SystemHealth>;
|
|
9
9
|
export declare function sleepFor(ms: number): Promise<unknown>;
|
|
10
|
+
export declare function sleepImmediate(): Promise<void>;
|
|
10
11
|
export declare function guid(): string;
|
|
11
12
|
export declare function deterministicRandom(seed: number): number;
|
|
12
13
|
export declare function identifyRedisType(redisInstance: any): 'redis' | 'ioredis' | null;
|
|
@@ -22,7 +23,15 @@ export declare function XSleepFor(ms: number): {
|
|
|
22
23
|
};
|
|
23
24
|
export declare function findTopKey(obj: AppTransitions, input: string): string | null;
|
|
24
25
|
export declare function findSubscriptionForTrigger(obj: AppSubscriptions, value: string): string | null;
|
|
26
|
+
/**
|
|
27
|
+
* Get the subscription topic for the flow to which @activityId belongs.
|
|
28
|
+
* TODO: resolve this value in the compiler...do not call this at runtime
|
|
29
|
+
*/
|
|
25
30
|
export declare function getSubscriptionTopic(activityId: string, store: StoreService<RedisClient, RedisMulti>, appVID: AppVID): Promise<string | undefined>;
|
|
31
|
+
/**
|
|
32
|
+
* returns the 12-digit format of the iso timestamp (e.g, 202101010000); returns
|
|
33
|
+
* an empty string if overridden by the user to not segment by time (infinity).
|
|
34
|
+
*/
|
|
26
35
|
export declare function getTimeSeries(granularity: string): string;
|
|
27
36
|
export declare function formatISODate(input: Date | string): string;
|
|
28
37
|
export declare function getSymKey(number: number): string;
|
package/build/modules/utils.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.restoreHierarchy = exports.getValueByPath = exports.getIndexedHash = exports.getSymVal = exports.getSymKey = exports.formatISODate = exports.getTimeSeries = exports.getSubscriptionTopic = exports.findSubscriptionForTrigger = exports.findTopKey = exports.XSleepFor = exports.matchesStatus = exports.matchesStatusCode = exports.identifyRedisTypeFromClass = exports.polyfill = exports.identifyRedisType = exports.deterministicRandom = exports.guid = exports.sleepFor = exports.getSystemHealth = void 0;
|
|
6
|
+
exports.restoreHierarchy = exports.getValueByPath = exports.getIndexedHash = exports.getSymVal = exports.getSymKey = exports.formatISODate = exports.getTimeSeries = exports.getSubscriptionTopic = exports.findSubscriptionForTrigger = exports.findTopKey = exports.XSleepFor = exports.matchesStatus = exports.matchesStatusCode = exports.identifyRedisTypeFromClass = exports.polyfill = exports.identifyRedisType = exports.deterministicRandom = exports.guid = exports.sleepImmediate = exports.sleepFor = exports.getSystemHealth = void 0;
|
|
7
7
|
const os_1 = __importDefault(require("os"));
|
|
8
8
|
const systeminformation_1 = __importDefault(require("systeminformation"));
|
|
9
9
|
const nanoid_1 = require("nanoid");
|
|
@@ -21,13 +21,16 @@ async function getSystemHealth() {
|
|
|
21
21
|
const freeMemory = os_1.default.freemem();
|
|
22
22
|
const usedMemory = totalMemory - freeMemory;
|
|
23
23
|
const cpus = os_1.default.cpus();
|
|
24
|
+
// CPU load calculation remains unchanged
|
|
24
25
|
const cpuLoad = cpus.map((cpu, i) => {
|
|
25
26
|
const total = Object.values(cpu.times).reduce((acc, tv) => acc + tv, 0);
|
|
26
27
|
const idle = cpu.times.idle;
|
|
27
28
|
const usage = ((total - idle) / total) * 100;
|
|
28
29
|
return { [`CPU ${i} Usage`]: `${usage.toFixed(2)}%` };
|
|
29
30
|
});
|
|
31
|
+
// Wrap each systeminformation call with safeExecute
|
|
30
32
|
const networkStats = await safeExecute(systeminformation_1.default.networkStats(), []);
|
|
33
|
+
// Construct the system health object with error handling in mind
|
|
31
34
|
const systemHealth = {
|
|
32
35
|
TotalMemoryGB: `${(totalMemory / 1024 / 1024 / 1024).toFixed(2)} GB`,
|
|
33
36
|
FreeMemoryGB: `${(freeMemory / 1024 / 1024 / 1024).toFixed(2)} GB`,
|
|
@@ -42,6 +45,10 @@ async function sleepFor(ms) {
|
|
|
42
45
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
43
46
|
}
|
|
44
47
|
exports.sleepFor = sleepFor;
|
|
48
|
+
function sleepImmediate() {
|
|
49
|
+
return new Promise((resolve) => setImmediate(resolve));
|
|
50
|
+
}
|
|
51
|
+
exports.sleepImmediate = sleepImmediate;
|
|
45
52
|
function guid() {
|
|
46
53
|
return (0, nanoid_1.nanoid)().replace(/[_-]/g, '0');
|
|
47
54
|
}
|
|
@@ -74,6 +81,7 @@ function identifyRedisType(redisInstance) {
|
|
|
74
81
|
return null;
|
|
75
82
|
}
|
|
76
83
|
exports.identifyRedisType = identifyRedisType;
|
|
84
|
+
//todo: the polyfill methods will all be deleted in the `beta` release.
|
|
77
85
|
exports.polyfill = {
|
|
78
86
|
resolveActivityType(activityType) {
|
|
79
87
|
if (activityType === 'activity') {
|
|
@@ -94,6 +102,7 @@ function identifyRedisTypeFromClass(redisClass) {
|
|
|
94
102
|
exports.identifyRedisTypeFromClass = identifyRedisTypeFromClass;
|
|
95
103
|
function matchesStatusCode(code, pattern) {
|
|
96
104
|
if (typeof pattern === 'string') {
|
|
105
|
+
// Convert '*' wildcard to its regex equivalent (\d)
|
|
97
106
|
const regexPattern = `^${pattern.replace(/\*/g, "\\d")}$`;
|
|
98
107
|
return new RegExp(regexPattern).test(code.toString());
|
|
99
108
|
}
|
|
@@ -105,6 +114,7 @@ function matchesStatus(status, targetStatus) {
|
|
|
105
114
|
}
|
|
106
115
|
exports.matchesStatus = matchesStatus;
|
|
107
116
|
function XSleepFor(ms) {
|
|
117
|
+
//can be interrupted with `clearTimeout`
|
|
108
118
|
let timerId;
|
|
109
119
|
let promise = new Promise((resolve) => {
|
|
110
120
|
timerId = setTimeout(resolve, ms);
|
|
@@ -131,6 +141,10 @@ function findSubscriptionForTrigger(obj, value) {
|
|
|
131
141
|
return null;
|
|
132
142
|
}
|
|
133
143
|
exports.findSubscriptionForTrigger = findSubscriptionForTrigger;
|
|
144
|
+
/**
|
|
145
|
+
* Get the subscription topic for the flow to which @activityId belongs.
|
|
146
|
+
* TODO: resolve this value in the compiler...do not call this at runtime
|
|
147
|
+
*/
|
|
134
148
|
async function getSubscriptionTopic(activityId, store, appVID) {
|
|
135
149
|
const appTransitions = await store.getTransitions(appVID);
|
|
136
150
|
const appSubscriptions = await store.getSubscriptions(appVID);
|
|
@@ -139,6 +153,10 @@ async function getSubscriptionTopic(activityId, store, appVID) {
|
|
|
139
153
|
return topic;
|
|
140
154
|
}
|
|
141
155
|
exports.getSubscriptionTopic = getSubscriptionTopic;
|
|
156
|
+
/**
|
|
157
|
+
* returns the 12-digit format of the iso timestamp (e.g, 202101010000); returns
|
|
158
|
+
* an empty string if overridden by the user to not segment by time (infinity).
|
|
159
|
+
*/
|
|
142
160
|
function getTimeSeries(granularity) {
|
|
143
161
|
if (granularity.toString() === 'infinity') {
|
|
144
162
|
return '0';
|
package/build/package.json
CHANGED
|
@@ -7,6 +7,9 @@ import { JobState, JobStatus } from '../../types/job';
|
|
|
7
7
|
import { MultiResponseFlags, RedisClient, RedisMulti } from '../../types/redis';
|
|
8
8
|
import { StringAnyType } from '../../types/serializer';
|
|
9
9
|
import { StreamCode, StreamData, StreamStatus } from '../../types/stream';
|
|
10
|
+
/**
|
|
11
|
+
* The base class for all activities
|
|
12
|
+
*/
|
|
10
13
|
declare class Activity {
|
|
11
14
|
config: ActivityType;
|
|
12
15
|
data: ActivityData;
|
|
@@ -23,7 +26,15 @@ declare class Activity {
|
|
|
23
26
|
adjacentIndex: number;
|
|
24
27
|
constructor(config: ActivityType, data: ActivityData, metadata: ActivityMetadata, hook: ActivityData | null, engine: EngineService, context?: JobState);
|
|
25
28
|
setLeg(leg: ActivityLeg): void;
|
|
29
|
+
/**
|
|
30
|
+
* Upon entering leg 1 of a duplexed activty, verify
|
|
31
|
+
* all aspects of the entry including job and activty state
|
|
32
|
+
*/
|
|
26
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
|
+
*/
|
|
27
38
|
verifyReentry(): Promise<number>;
|
|
28
39
|
processEvent(status?: StreamStatus, code?: StreamCode, type?: 'hook' | 'output'): Promise<void>;
|
|
29
40
|
processPending(telemetry: TelemetryService, type: 'hook' | 'output'): Promise<MultiResponseFlags>;
|
|
@@ -35,7 +46,14 @@ declare class Activity {
|
|
|
35
46
|
mapInputData(): void;
|
|
36
47
|
mapOutputData(): void;
|
|
37
48
|
registerTimeout(): Promise<void>;
|
|
49
|
+
/**
|
|
50
|
+
* Any StreamMessage with a status of ERROR is bound to the activity
|
|
51
|
+
*/
|
|
38
52
|
bindActivityError(data: Record<string, unknown>): void;
|
|
53
|
+
/**
|
|
54
|
+
* unhandled activity errors (activities that return an ERROR StreamMessage
|
|
55
|
+
* status and have no adjacent children to transition to) are bound to the job
|
|
56
|
+
*/
|
|
39
57
|
bindJobError(data: Record<string, unknown>): void;
|
|
40
58
|
getTriggerConfig(): Promise<ActivityType>;
|
|
41
59
|
getJobStatus(): null | number;
|
|
@@ -50,6 +68,12 @@ declare class Activity {
|
|
|
50
68
|
bindJobMetadataPaths(): string[];
|
|
51
69
|
bindActivityMetadataPaths(): string[];
|
|
52
70
|
getState(): Promise<void>;
|
|
71
|
+
/**
|
|
72
|
+
* if the job is created/deleted/created with the same key,
|
|
73
|
+
* the 'gid' ensures no stale messages (such as sleep delays)
|
|
74
|
+
* enter the workstream. Any message with a mismatched gid
|
|
75
|
+
* belongs to a prior job and can safely be ignored/dropped.
|
|
76
|
+
*/
|
|
53
77
|
assertGenerationalId(jobGID: string, msgGID?: string): void;
|
|
54
78
|
initDimensionalAddress(dad: string): void;
|
|
55
79
|
initSelf(context: StringAnyType): JobState;
|
|
@@ -59,6 +83,10 @@ declare class Activity {
|
|
|
59
83
|
resolveAdjacentDad(): string;
|
|
60
84
|
filterAdjacent(): Promise<StreamData[]>;
|
|
61
85
|
transition(adjacencyList: StreamData[], jobStatus: JobStatus): Promise<string[]>;
|
|
86
|
+
/**
|
|
87
|
+
* A job with a vale < -100_000_000 is considered interrupted,
|
|
88
|
+
* as the interruption event decrements the job status by 1billion.
|
|
89
|
+
*/
|
|
62
90
|
jobWasInterrupted(jobStatus: JobStatus): boolean;
|
|
63
91
|
}
|
|
64
92
|
export { Activity, ActivityType };
|
|
@@ -10,6 +10,9 @@ const pipe_1 = require("../pipe");
|
|
|
10
10
|
const serializer_1 = require("../serializer");
|
|
11
11
|
const telemetry_1 = require("../telemetry");
|
|
12
12
|
const stream_1 = require("../../types/stream");
|
|
13
|
+
/**
|
|
14
|
+
* The base class for all activities
|
|
15
|
+
*/
|
|
13
16
|
class Activity {
|
|
14
17
|
constructor(config, data, metadata, hook, engine, context) {
|
|
15
18
|
this.status = stream_1.StreamStatus.SUCCESS;
|
|
@@ -27,12 +30,20 @@ class Activity {
|
|
|
27
30
|
setLeg(leg) {
|
|
28
31
|
this.leg = leg;
|
|
29
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* Upon entering leg 1 of a duplexed activty, verify
|
|
35
|
+
* all aspects of the entry including job and activty state
|
|
36
|
+
*/
|
|
30
37
|
async verifyEntry() {
|
|
31
38
|
this.setLeg(1);
|
|
32
39
|
await this.getState();
|
|
33
40
|
collator_1.CollatorService.assertJobActive(this.context.metadata.js, this.context.metadata.jid, this.metadata.aid);
|
|
34
41
|
await collator_1.CollatorService.notarizeEntry(this);
|
|
35
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
|
+
*/
|
|
36
47
|
async verifyReentry() {
|
|
37
48
|
const guid = this.context.metadata.guid;
|
|
38
49
|
this.setLeg(2);
|
|
@@ -40,6 +51,7 @@ class Activity {
|
|
|
40
51
|
collator_1.CollatorService.assertJobActive(this.context.metadata.js, this.context.metadata.jid, this.metadata.aid);
|
|
41
52
|
return await collator_1.CollatorService.notarizeReentry(this, guid);
|
|
42
53
|
}
|
|
54
|
+
//******** DUPLEX RE-ENTRY POINT ********//
|
|
43
55
|
async processEvent(status = stream_1.StreamStatus.SUCCESS, code = 200, type = 'output') {
|
|
44
56
|
this.setLeg(2);
|
|
45
57
|
const jid = this.context.metadata.jid;
|
|
@@ -132,6 +144,7 @@ class Activity {
|
|
|
132
144
|
telemetry.mapActivityAttributes();
|
|
133
145
|
const jobStatus = this.resolveStatus(multiResponse);
|
|
134
146
|
const attrs = { 'app.job.jss': jobStatus };
|
|
147
|
+
//adjacencyList membership has already been set at this point (according to activity status)
|
|
135
148
|
const messageIds = await this.transition(this.adjacencyList, jobStatus);
|
|
136
149
|
if (messageIds?.length) {
|
|
137
150
|
attrs['app.activity.mids'] = messageIds.join(',');
|
|
@@ -154,6 +167,10 @@ class Activity {
|
|
|
154
167
|
if (output) {
|
|
155
168
|
for (const key in output) {
|
|
156
169
|
const f1 = key.indexOf('[');
|
|
170
|
+
//keys with array notation suffix `somekey[]` represent
|
|
171
|
+
//dynamically-keyed mappings whose `value` must be moved to the output.
|
|
172
|
+
//The `value` must be an object with keys appropriate to the
|
|
173
|
+
//notation type: `somekey[0] (array)`, `somekey[-] (mark)`, OR `somekey[_] (search)`
|
|
157
174
|
if (f1 > -1) {
|
|
158
175
|
const amount = key.substring(f1 + 1).split(']')[0];
|
|
159
176
|
if (!isNaN(Number(amount))) {
|
|
@@ -180,6 +197,7 @@ class Activity {
|
|
|
180
197
|
}
|
|
181
198
|
}
|
|
182
199
|
mapOutputData() {
|
|
200
|
+
//activity YAML may include output map data that produces/extends activity output data.
|
|
183
201
|
if (this.config.output?.maps) {
|
|
184
202
|
const mapper = new mapper_1.MapperService(this.config.output.maps, this.context);
|
|
185
203
|
const actOutData = mapper.mapRules();
|
|
@@ -189,12 +207,21 @@ class Activity {
|
|
|
189
207
|
}
|
|
190
208
|
}
|
|
191
209
|
async registerTimeout() {
|
|
210
|
+
//set timeout in support of hook and/or duplex
|
|
192
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Any StreamMessage with a status of ERROR is bound to the activity
|
|
214
|
+
*/
|
|
193
215
|
bindActivityError(data) {
|
|
194
216
|
const md = this.context[this.metadata.aid].output.metadata;
|
|
195
217
|
md.err = JSON.stringify(this.data);
|
|
218
|
+
//(temporary...useful for mapping error parts in the app.yaml)
|
|
196
219
|
md.$error = { ...data, is_stream_error: true };
|
|
197
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* unhandled activity errors (activities that return an ERROR StreamMessage
|
|
223
|
+
* status and have no adjacent children to transition to) are bound to the job
|
|
224
|
+
*/
|
|
198
225
|
bindJobError(data) {
|
|
199
226
|
this.context.metadata.err = JSON.stringify({ ...data, is_stream_error: true });
|
|
200
227
|
}
|
|
@@ -209,6 +236,7 @@ class Activity {
|
|
|
209
236
|
await this.store.setStatus(amount, this.context.metadata.jid, appId, multi);
|
|
210
237
|
}
|
|
211
238
|
authorizeEntry(state) {
|
|
239
|
+
//pre-authorize activity state to allow entry for adjacent activities
|
|
212
240
|
return this.adjacencyList?.map((streamData) => {
|
|
213
241
|
const { metadata: { aid } } = streamData;
|
|
214
242
|
state[`${aid}/output/metadata/as`] = collator_1.CollatorService.getSeed();
|
|
@@ -228,6 +256,7 @@ class Activity {
|
|
|
228
256
|
const presets = this.authorizeEntry(state);
|
|
229
257
|
this.bindDimensionalAddress(state);
|
|
230
258
|
this.bindActivityState(state);
|
|
259
|
+
//symbolNames holds symkeys
|
|
231
260
|
const symbolNames = [
|
|
232
261
|
`$${this.config.subscribes}`,
|
|
233
262
|
this.metadata.aid,
|
|
@@ -237,6 +266,7 @@ class Activity {
|
|
|
237
266
|
return await this.store.setState(state, this.getJobStatus(), jobId, symbolNames, dIds, multi);
|
|
238
267
|
}
|
|
239
268
|
bindJobMetadata() {
|
|
269
|
+
//both legs of the most recently run activity (1 and 2) modify ju (job_updated)
|
|
240
270
|
this.context.metadata.ju = (0, utils_1.formatISODate)(new Date());
|
|
241
271
|
}
|
|
242
272
|
bindActivityMetadata() {
|
|
@@ -323,6 +353,7 @@ class Activity {
|
|
|
323
353
|
telemetry_1.TelemetryService.addTargetTelemetryPaths(consumes, this.config, this.metadata, this.leg);
|
|
324
354
|
let { dad, jid } = this.context.metadata;
|
|
325
355
|
const dIds = collator_1.CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], dad || '');
|
|
356
|
+
//`state` is a unidimensional hash; context is a tree
|
|
326
357
|
const [state, _status] = await this.store.getState(jid, consumes, dIds);
|
|
327
358
|
this.context = (0, utils_1.restoreHierarchy)(state);
|
|
328
359
|
this.assertGenerationalId(this.context?.metadata?.gid, gid);
|
|
@@ -330,6 +361,12 @@ class Activity {
|
|
|
330
361
|
this.initSelf(this.context);
|
|
331
362
|
this.initPolicies(this.context);
|
|
332
363
|
}
|
|
364
|
+
/**
|
|
365
|
+
* if the job is created/deleted/created with the same key,
|
|
366
|
+
* the 'gid' ensures no stale messages (such as sleep delays)
|
|
367
|
+
* enter the workstream. Any message with a mismatched gid
|
|
368
|
+
* belongs to a prior job and can safely be ignored/dropped.
|
|
369
|
+
*/
|
|
333
370
|
assertGenerationalId(jobGID, msgGID) {
|
|
334
371
|
if (msgGID !== jobGID) {
|
|
335
372
|
throw new errors_1.GenerationalError(jobGID, msgGID, this.context?.metadata?.jid ?? '', this.context?.metadata?.aid ?? '', this.context?.metadata?.dad ?? '');
|
|
@@ -356,9 +393,10 @@ class Activity {
|
|
|
356
393
|
if (!self.output.metadata) {
|
|
357
394
|
self.output.metadata = {};
|
|
358
395
|
}
|
|
396
|
+
//prebind the updated timestamp (mappings need the time)
|
|
359
397
|
self.output.metadata.au = (0, utils_1.formatISODate)(new Date());
|
|
360
398
|
context['$self'] = self;
|
|
361
|
-
context['$job'] = context;
|
|
399
|
+
context['$job'] = context; //NEVER call STRINGIFY! (now circular)
|
|
362
400
|
return context;
|
|
363
401
|
}
|
|
364
402
|
initPolicies(context) {
|
|
@@ -371,11 +409,13 @@ class Activity {
|
|
|
371
409
|
resolveDad() {
|
|
372
410
|
let dad = this.metadata.dad;
|
|
373
411
|
if (this.adjacentIndex > 0) {
|
|
412
|
+
//if adjacent index > 0 the activity is cycling; replace last index with cycle index
|
|
374
413
|
dad = `${dad.substring(0, dad.lastIndexOf(','))},${this.adjacentIndex}`;
|
|
375
414
|
}
|
|
376
415
|
return dad;
|
|
377
416
|
}
|
|
378
417
|
resolveAdjacentDad() {
|
|
418
|
+
//concat self and child dimension (all children (leg 1) begin life at 0)
|
|
379
419
|
return `${this.resolveDad()}${collator_1.CollatorService.getDimensionalSeed(0)}`;
|
|
380
420
|
}
|
|
381
421
|
;
|
|
@@ -383,6 +423,7 @@ class Activity {
|
|
|
383
423
|
const adjacencyList = [];
|
|
384
424
|
const transitions = await this.store.getTransitions(await this.engine.getVID());
|
|
385
425
|
const transition = transitions[`.${this.metadata.aid}`];
|
|
426
|
+
//resolve the dimensional address for adjacent children
|
|
386
427
|
const adjacentDad = this.resolveAdjacentDad();
|
|
387
428
|
if (transition) {
|
|
388
429
|
for (const toActivityId in transition) {
|
|
@@ -427,6 +468,10 @@ class Activity {
|
|
|
427
468
|
}
|
|
428
469
|
return mIds;
|
|
429
470
|
}
|
|
471
|
+
/**
|
|
472
|
+
* A job with a vale < -100_000_000 is considered interrupted,
|
|
473
|
+
* as the interruption event decrements the job status by 1billion.
|
|
474
|
+
*/
|
|
430
475
|
jobWasInterrupted(jobStatus) {
|
|
431
476
|
return jobStatus < -100000000;
|
|
432
477
|
}
|
|
@@ -12,6 +12,7 @@ class Await extends activity_1.Activity {
|
|
|
12
12
|
constructor(config, data, metadata, hook, engine, context) {
|
|
13
13
|
super(config, data, metadata, hook, engine, context);
|
|
14
14
|
}
|
|
15
|
+
//******** INITIAL ENTRY POINT (A) ********//
|
|
15
16
|
async process() {
|
|
16
17
|
this.logger.debug('await-process', { jid: this.context.metadata.jid, gid: this.context.metadata.gid, aid: this.metadata.aid });
|
|
17
18
|
let telemetry;
|
|
@@ -20,12 +21,15 @@ class Await extends activity_1.Activity {
|
|
|
20
21
|
telemetry = new telemetry_1.TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
|
|
21
22
|
telemetry.startActivitySpan(this.leg);
|
|
22
23
|
this.mapInputData();
|
|
24
|
+
//save state and authorize reentry
|
|
23
25
|
const multi = this.store.getMulti();
|
|
26
|
+
//todo: await this.registerTimeout();
|
|
24
27
|
const messageId = await this.execActivity(multi);
|
|
25
28
|
await collator_1.CollatorService.authorizeReentry(this, multi);
|
|
26
29
|
await this.setState(multi);
|
|
27
30
|
await this.setStatus(0, multi);
|
|
28
31
|
const multiResponse = await multi.exec();
|
|
32
|
+
//telemetry
|
|
29
33
|
telemetry.mapActivityAttributes();
|
|
30
34
|
const jobStatus = this.resolveStatus(multiResponse);
|
|
31
35
|
telemetry.setActivityAttributes({
|
|
@@ -7,6 +7,13 @@ declare class Cycle extends Activity {
|
|
|
7
7
|
config: CycleActivity;
|
|
8
8
|
constructor(config: ActivityType, data: ActivityData, metadata: ActivityMetadata, hook: ActivityData | null, engine: EngineService, context?: JobState);
|
|
9
9
|
process(): Promise<string>;
|
|
10
|
+
/**
|
|
11
|
+
* Trigger the target ancestor to execute in a cycle,
|
|
12
|
+
* without violating the constraints of the DAG. Immutable
|
|
13
|
+
* `individual activity state` will execute in a new dimensional
|
|
14
|
+
* thread while `shared job state` can change. This
|
|
15
|
+
* pattern allows for retries without violating the DAG.
|
|
16
|
+
*/
|
|
10
17
|
cycleAncestorActivity(multi: RedisMulti): Promise<string>;
|
|
11
18
|
}
|
|
12
19
|
export { Cycle };
|