@hotmeshio/hotmesh 0.0.38 → 0.0.40
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 +13 -7
- package/build/modules/enums.d.ts +29 -23
- package/build/modules/enums.js +38 -29
- package/build/modules/errors.d.ts +1 -1
- package/build/modules/errors.js +9 -7
- package/build/modules/key.d.ts +1 -34
- package/build/modules/key.js +24 -47
- package/build/package.json +3 -3
- package/build/services/activities/activity.js +1 -1
- package/build/services/activities/hook.js +4 -9
- package/build/services/activities/trigger.d.ts +3 -2
- package/build/services/activities/trigger.js +10 -6
- package/build/services/durable/client.d.ts +9 -1
- package/build/services/durable/client.js +30 -14
- package/build/services/durable/handle.js +2 -2
- package/build/services/durable/worker.js +4 -3
- package/build/services/engine/index.d.ts +2 -1
- package/build/services/engine/index.js +6 -6
- package/build/services/router/index.js +16 -14
- package/build/services/store/index.d.ts +14 -9
- package/build/services/store/index.js +46 -23
- package/build/services/task/index.d.ts +10 -3
- package/build/services/task/index.js +35 -17
- package/build/types/durable.d.ts +3 -2
- package/build/types/hotmesh.d.ts +43 -2
- package/build/types/hotmesh.js +28 -0
- package/build/types/index.d.ts +3 -2
- package/build/types/index.js +3 -1
- package/build/types/logger.d.ts +1 -0
- package/build/types/logger.js +1 -0
- package/build/types/task.d.ts +1 -0
- package/build/types/task.js +2 -0
- package/modules/enums.ts +49 -35
- package/modules/errors.ts +17 -8
- package/modules/key.ts +3 -40
- package/package.json +3 -3
- package/services/activities/activity.ts +2 -2
- package/services/activities/hook.ts +18 -9
- package/services/activities/trigger.ts +10 -6
- package/services/durable/client.ts +31 -15
- package/services/durable/handle.ts +3 -3
- package/services/durable/worker.ts +4 -3
- package/services/engine/index.ts +13 -12
- package/services/router/index.ts +26 -24
- package/services/store/index.ts +59 -25
- package/services/task/index.ts +66 -24
- package/types/durable.ts +6 -5
- package/types/hotmesh.ts +47 -2
- package/types/index.ts +8 -1
- package/types/logger.ts +3 -1
- package/types/task.ts +1 -0
package/README.md
CHANGED
|
@@ -19,8 +19,17 @@ HotMesh inverts the relationship to Redis: those functions that once used Redis
|
|
|
19
19
|
|
|
20
20
|
HotMesh creates an *ad hoc*, Redis-backed network of functions and organizes them into a unified service mesh. *Any service with access to Redis can join in the network, bypassing the legacy clutter.*
|
|
21
21
|
|
|
22
|
+
<img src="./docs/img/hotmesh_emergent_properties.png" alt="HotMesh elevates Redis to a Service Mesh, Message Router, Integration Bus, and Business Process Engine." style="max-width:100%;width:600px;">
|
|
23
|
+
|
|
24
|
+
|
|
22
25
|
## Design
|
|
23
|
-
|
|
26
|
+
HotMesh uses your existing Redis installation. If you already have an instance available, you have all the infrastructure you'll ever need.
|
|
27
|
+
|
|
28
|
+
### Design | Pluck
|
|
29
|
+
The simplest way to get started is to use the [HotMesh Pluck](https://github.com/hotmeshio/pluck-typescript) package. It's designed for usability, and is backed by the HotMesh system if you need advanced features. It includes a detailed [SDK](https://hotmeshio.github.io/pluck-typescript/) and a wide variety of examples.
|
|
30
|
+
|
|
31
|
+
### Design | Durable
|
|
32
|
+
HotMesh's *Durable* module is a TypeScript Library modeled after Temporal.io. If you're familiar with their SDK, the principles are the same.
|
|
24
33
|
|
|
25
34
|
1. Start by defining **activities**. Activities can be written in any style, using any framework, and can even be legacy functions you've already written. The only requirement is that they return a Promise. *Note how the `saludar` example throws an error 50% of the time. It doesn't matter how unpredictable your functions are, HotMesh will retry as necessary until they succeed.*
|
|
26
35
|
```javascript
|
|
@@ -99,7 +108,7 @@ The simplest way to get started is to use the `Durable` module. It's organized u
|
|
|
99
108
|
}
|
|
100
109
|
```
|
|
101
110
|
|
|
102
|
-
|
|
111
|
+
#### Workflow Extensions
|
|
103
112
|
Redis governance delivers more than just reliability. Externalizing state fundamentally changes the execution profile for your functions, allowing you to design long-running, durable workflows. The `Durable` base class (shown in the examples above) provides additional methods for solving the most common state management challenges.
|
|
104
113
|
|
|
105
114
|
- `waitForSignal` Pause your function and wait for external event(s) before continuing. The *waitForSignal* method will collate and cache the signals and only awaken your function once all signals have arrived.
|
|
@@ -177,8 +186,8 @@ Redis governance delivers more than just reliability. Externalizing state fundam
|
|
|
177
186
|
|
|
178
187
|
Refer to the [hotmeshio/samples-typescript](https://github.com/hotmeshio/samples-typescript) repo for usage examples.
|
|
179
188
|
|
|
180
|
-
|
|
181
|
-
|
|
189
|
+
### Design | Advanced
|
|
190
|
+
The *Pluck* and *Durable* modules are the easiest way to use HotMesh. But if you need full control over your function lifecycles (including high-volume, high-speed use cases), you can use HotMesh's underlying YAML models to optimize your durable workflows. The following model depicts a sequence of activities orchestrated by HotMesh. Any function you associate with a `topic` in your YAML definition is guaranteed to be durable.
|
|
182
191
|
|
|
183
192
|
```yaml
|
|
184
193
|
app:
|
|
@@ -310,6 +319,3 @@ HotMesh is a distributed orchestration engine. Refer to the [Distributed Orchest
|
|
|
310
319
|
|
|
311
320
|
## System Lifecycle
|
|
312
321
|
Gain insight into HotMesh's monitoring, exception handling, and alarm configurations via the [System Lifecycle Guide](https://github.com/hotmeshio/sdk-typescript/tree/main/docs/system_lifecycle.md).
|
|
313
|
-
|
|
314
|
-
## Alpha Release
|
|
315
|
-
So what exacty is an [alpha release](https://github.com/hotmeshio/sdk-typescript/tree/main/docs/alpha.md)?
|
package/build/modules/enums.d.ts
CHANGED
|
@@ -1,23 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const
|
|
3
|
-
export declare const
|
|
4
|
-
export declare const
|
|
5
|
-
export declare const
|
|
6
|
-
export declare const
|
|
7
|
-
export declare const
|
|
8
|
-
export declare const
|
|
9
|
-
export declare const
|
|
10
|
-
export declare const
|
|
11
|
-
export declare const
|
|
12
|
-
export declare const
|
|
13
|
-
export declare const
|
|
14
|
-
export declare const
|
|
15
|
-
export declare const
|
|
16
|
-
export declare const
|
|
17
|
-
export declare const
|
|
18
|
-
export declare const
|
|
19
|
-
export declare const
|
|
20
|
-
export declare const
|
|
21
|
-
export declare const
|
|
22
|
-
export declare const
|
|
23
|
-
export declare const
|
|
1
|
+
import { LogLevel } from "../types/logger";
|
|
2
|
+
export declare const HMSH_LOGLEVEL: LogLevel;
|
|
3
|
+
export declare const HMSH_CODE_SUCCESS = 200;
|
|
4
|
+
export declare const HMSH_CODE_PENDING = 202;
|
|
5
|
+
export declare const HMSH_CODE_NOTFOUND = 404;
|
|
6
|
+
export declare const HMSH_CODE_INTERRUPT = 410;
|
|
7
|
+
export declare const HMSH_CODE_UNKNOWN = 500;
|
|
8
|
+
export declare const HMSH_CODE_TIMEOUT = 504;
|
|
9
|
+
export declare const HMSH_CODE_UNACKED = 999;
|
|
10
|
+
export declare const HMSH_CODE_DURABLE_SLEEPFOR = 592;
|
|
11
|
+
export declare const HMSH_CODE_DURABLE_INCOMPLETE = 593;
|
|
12
|
+
export declare const HMSH_CODE_DURABLE_WAITFOR = 594;
|
|
13
|
+
export declare const HMSH_CODE_DURABLE_TIMEOUT = 596;
|
|
14
|
+
export declare const HMSH_CODE_DURABLE_MAXED = 597;
|
|
15
|
+
export declare const HMSH_CODE_DURABLE_FATAL = 598;
|
|
16
|
+
export declare const HMSH_CODE_DURABLE_RETRYABLE = 599;
|
|
17
|
+
export declare const HMSH_STATUS_UNKNOWN = "unknown";
|
|
18
|
+
export declare const HMSH_OTT_WAIT_TIME: number;
|
|
19
|
+
export declare const HMSH_EXPIRE_JOB_SECONDS: number;
|
|
20
|
+
export declare const HMSH_MAX_RETRIES: number;
|
|
21
|
+
export declare const HMSH_MAX_TIMEOUT_MS: number;
|
|
22
|
+
export declare const HMSH_GRADUATED_INTERVAL_MS: number;
|
|
23
|
+
export declare const HMSH_BLOCK_TIME_MS: number;
|
|
24
|
+
export declare const HMSH_XCLAIM_DELAY_MS: number;
|
|
25
|
+
export declare const HMSH_XCLAIM_COUNT: number;
|
|
26
|
+
export declare const HMSH_XPENDING_COUNT: number;
|
|
27
|
+
export declare const HMSH_EXPIRE_DURATION: number;
|
|
28
|
+
export declare const HMSH_FIDELITY_SECONDS: number;
|
|
29
|
+
export declare const HMSH_SCOUT_INTERVAL_SECONDS: number;
|
package/build/modules/enums.js
CHANGED
|
@@ -1,31 +1,40 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
//
|
|
5
|
-
exports.
|
|
6
|
-
|
|
7
|
-
exports.
|
|
8
|
-
exports.
|
|
9
|
-
exports.
|
|
10
|
-
|
|
11
|
-
exports.
|
|
12
|
-
exports.
|
|
13
|
-
exports.
|
|
14
|
-
exports.
|
|
15
|
-
exports.
|
|
16
|
-
exports.
|
|
17
|
-
exports.
|
|
18
|
-
exports.
|
|
19
|
-
exports.
|
|
20
|
-
exports.
|
|
21
|
-
exports.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
exports.
|
|
25
|
-
|
|
26
|
-
exports.
|
|
27
|
-
exports.
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
//
|
|
31
|
-
exports.
|
|
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;
|
|
4
|
+
// HOTMESH SYSTEM
|
|
5
|
+
exports.HMSH_LOGLEVEL = process.env.HMSH_LOGLEVEL || 'info';
|
|
6
|
+
// STATUS CODES AND MESSAGES
|
|
7
|
+
exports.HMSH_CODE_SUCCESS = 200;
|
|
8
|
+
exports.HMSH_CODE_PENDING = 202;
|
|
9
|
+
exports.HMSH_CODE_NOTFOUND = 404;
|
|
10
|
+
exports.HMSH_CODE_INTERRUPT = 410;
|
|
11
|
+
exports.HMSH_CODE_UNKNOWN = 500;
|
|
12
|
+
exports.HMSH_CODE_TIMEOUT = 504;
|
|
13
|
+
exports.HMSH_CODE_UNACKED = 999;
|
|
14
|
+
exports.HMSH_CODE_DURABLE_SLEEPFOR = 592;
|
|
15
|
+
exports.HMSH_CODE_DURABLE_INCOMPLETE = 593;
|
|
16
|
+
exports.HMSH_CODE_DURABLE_WAITFOR = 594;
|
|
17
|
+
exports.HMSH_CODE_DURABLE_TIMEOUT = 596;
|
|
18
|
+
exports.HMSH_CODE_DURABLE_MAXED = 597;
|
|
19
|
+
exports.HMSH_CODE_DURABLE_FATAL = 598;
|
|
20
|
+
exports.HMSH_CODE_DURABLE_RETRYABLE = 599;
|
|
21
|
+
exports.HMSH_STATUS_UNKNOWN = 'unknown';
|
|
22
|
+
// ENGINE
|
|
23
|
+
exports.HMSH_OTT_WAIT_TIME = parseInt(process.env.HMSH_OTT_WAIT_TIME, 10) || 1000;
|
|
24
|
+
exports.HMSH_EXPIRE_JOB_SECONDS = parseInt(process.env.HMSH_EXPIRE_JOB_SECONDS, 10) || 1;
|
|
25
|
+
// STREAM ROUTER
|
|
26
|
+
exports.HMSH_MAX_RETRIES = parseInt(process.env.HMSH_MAX_RETRIES, 10) || 3;
|
|
27
|
+
exports.HMSH_MAX_TIMEOUT_MS = parseInt(process.env.HMSH_MAX_TIMEOUT_MS, 10) || 60000;
|
|
28
|
+
exports.HMSH_GRADUATED_INTERVAL_MS = parseInt(process.env.HMSH_GRADUATED_INTERVAL_MS, 10) || 5000;
|
|
29
|
+
const BASE_BLOCK_DURATION = 10000; // Modified for clarity
|
|
30
|
+
const TEST_BLOCK_DURATION = 1000; // Modified for clarity
|
|
31
|
+
exports.HMSH_BLOCK_TIME_MS = process.env.HMSH_BLOCK_TIME_MS ? parseInt(process.env.HMSH_BLOCK_TIME_MS, 10) : (process.env.NODE_ENV === 'test' ? TEST_BLOCK_DURATION : BASE_BLOCK_DURATION);
|
|
32
|
+
exports.HMSH_XCLAIM_DELAY_MS = parseInt(process.env.HMSH_XCLAIM_DELAY_MS, 10) || 1000 * 60;
|
|
33
|
+
exports.HMSH_XCLAIM_COUNT = parseInt(process.env.HMSH_XCLAIM_COUNT, 10) || 3;
|
|
34
|
+
exports.HMSH_XPENDING_COUNT = parseInt(process.env.HMSH_XPENDING_COUNT, 10) || 10;
|
|
35
|
+
// TASK WORKER
|
|
36
|
+
exports.HMSH_EXPIRE_DURATION = parseInt(process.env.HMSH_EXPIRE_DURATION, 10) || 1;
|
|
37
|
+
const BASE_FIDELITY_SECONDS = 5;
|
|
38
|
+
const TEST_FIDELITY_SECONDS = 5;
|
|
39
|
+
exports.HMSH_FIDELITY_SECONDS = process.env.HMSH_FIDELITY_SECONDS ? parseInt(process.env.HMSH_FIDELITY_SECONDS, 10) : (process.env.NODE_ENV === 'test' ? TEST_FIDELITY_SECONDS : BASE_FIDELITY_SECONDS);
|
|
40
|
+
exports.HMSH_SCOUT_INTERVAL_SECONDS = parseInt(process.env.HMSH_SCOUT_INTERVAL_SECONDS, 10) || 60;
|
|
@@ -2,7 +2,7 @@ import { ActivityDuplex } from "../types/activity";
|
|
|
2
2
|
import { CollationFaultType, CollationStage } from "../types/collator";
|
|
3
3
|
declare class GetStateError extends Error {
|
|
4
4
|
jobId: string;
|
|
5
|
-
code:
|
|
5
|
+
code: number;
|
|
6
6
|
constructor(jobId: string);
|
|
7
7
|
}
|
|
8
8
|
declare class SetStateError extends Error {
|
package/build/modules/errors.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SetStateError = exports.RegisterTimeoutError = exports.MapDataError = exports.InactiveJobError = exports.GetStateError = exports.GenerationalError = exports.ExecActivityError = exports.DuplicateJobError = exports.DurableWaitForSignalError = exports.DurableTimeoutError = exports.DurableSleepForError = exports.DurableSleepError = exports.DurableRetryError = exports.DurableMaxedError = exports.DurableIncompleteSignalError = exports.DurableFatalError = exports.CollationError = void 0;
|
|
4
|
+
const enums_1 = require("./enums");
|
|
4
5
|
class GetStateError extends Error {
|
|
5
6
|
constructor(jobId) {
|
|
6
7
|
super(`${jobId} Not Found`);
|
|
8
|
+
this.code = enums_1.HMSH_CODE_NOTFOUND;
|
|
7
9
|
this.jobId = jobId;
|
|
8
10
|
}
|
|
9
11
|
}
|
|
@@ -19,7 +21,7 @@ exports.SetStateError = SetStateError;
|
|
|
19
21
|
class DurableIncompleteSignalError extends Error {
|
|
20
22
|
constructor(message) {
|
|
21
23
|
super(message);
|
|
22
|
-
this.code =
|
|
24
|
+
this.code = enums_1.HMSH_CODE_DURABLE_INCOMPLETE;
|
|
23
25
|
}
|
|
24
26
|
}
|
|
25
27
|
exports.DurableIncompleteSignalError = DurableIncompleteSignalError;
|
|
@@ -28,7 +30,7 @@ class DurableWaitForSignalError extends Error {
|
|
|
28
30
|
constructor(message, signals) {
|
|
29
31
|
super(message);
|
|
30
32
|
this.signals = signals;
|
|
31
|
-
this.code =
|
|
33
|
+
this.code = enums_1.HMSH_CODE_DURABLE_WAITFOR;
|
|
32
34
|
}
|
|
33
35
|
}
|
|
34
36
|
exports.DurableWaitForSignalError = DurableWaitForSignalError;
|
|
@@ -49,35 +51,35 @@ class DurableSleepForError extends Error {
|
|
|
49
51
|
this.duration = duration;
|
|
50
52
|
this.index = index;
|
|
51
53
|
this.dimension = dimension;
|
|
52
|
-
this.code =
|
|
54
|
+
this.code = enums_1.HMSH_CODE_DURABLE_SLEEPFOR;
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
exports.DurableSleepForError = DurableSleepForError;
|
|
56
58
|
class DurableTimeoutError extends Error {
|
|
57
59
|
constructor(message) {
|
|
58
60
|
super(message);
|
|
59
|
-
this.code =
|
|
61
|
+
this.code = enums_1.HMSH_CODE_DURABLE_TIMEOUT;
|
|
60
62
|
}
|
|
61
63
|
}
|
|
62
64
|
exports.DurableTimeoutError = DurableTimeoutError;
|
|
63
65
|
class DurableMaxedError extends Error {
|
|
64
66
|
constructor(message) {
|
|
65
67
|
super(message);
|
|
66
|
-
this.code =
|
|
68
|
+
this.code = enums_1.HMSH_CODE_DURABLE_MAXED;
|
|
67
69
|
}
|
|
68
70
|
}
|
|
69
71
|
exports.DurableMaxedError = DurableMaxedError;
|
|
70
72
|
class DurableFatalError extends Error {
|
|
71
73
|
constructor(message) {
|
|
72
74
|
super(message);
|
|
73
|
-
this.code =
|
|
75
|
+
this.code = enums_1.HMSH_CODE_DURABLE_FATAL;
|
|
74
76
|
}
|
|
75
77
|
}
|
|
76
78
|
exports.DurableFatalError = DurableFatalError;
|
|
77
79
|
class DurableRetryError extends Error {
|
|
78
80
|
constructor(message) {
|
|
79
81
|
super(message);
|
|
80
|
-
this.code =
|
|
82
|
+
this.code = enums_1.HMSH_CODE_DURABLE_RETRYABLE;
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
85
|
exports.DurableRetryError = DurableRetryError;
|
package/build/modules/key.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { KeyStoreParams, KeyType } from '../types/hotmesh';
|
|
1
2
|
/**
|
|
2
3
|
* Keys
|
|
3
4
|
*
|
|
@@ -26,40 +27,6 @@
|
|
|
26
27
|
* hmsh:<appid>:sym:vals: -> {hash} list of symbols for job values across all app versions
|
|
27
28
|
*/
|
|
28
29
|
declare const HMNS = "hmsh";
|
|
29
|
-
declare enum KeyType {
|
|
30
|
-
APP = "APP",
|
|
31
|
-
ENGINE_ID = "ENGINE",
|
|
32
|
-
HOOKS = "HOOKS",
|
|
33
|
-
JOB_DEPENDENTS = "JOB_DEPENDENTS",
|
|
34
|
-
JOB_STATE = "JOB_STATE",
|
|
35
|
-
JOB_STATS_GENERAL = "JOB_STATS_GENERAL",
|
|
36
|
-
JOB_STATS_MEDIAN = "JOB_STATS_MEDIAN",
|
|
37
|
-
JOB_STATS_INDEX = "JOB_STATS_INDEX",
|
|
38
|
-
HOTMESH = "HOTMESH",
|
|
39
|
-
QUORUM = "QUORUM",
|
|
40
|
-
SCHEMAS = "SCHEMAS",
|
|
41
|
-
SIGNALS = "SIGNALS",
|
|
42
|
-
STREAMS = "STREAMS",
|
|
43
|
-
SUBSCRIPTIONS = "SUBSCRIPTIONS",
|
|
44
|
-
SUBSCRIPTION_PATTERNS = "SUBSCRIPTION_PATTERNS",
|
|
45
|
-
SYMKEYS = "SYMKEYS",
|
|
46
|
-
SYMVALS = "SYMVALS",
|
|
47
|
-
TIME_RANGE = "TIME_RANGE",
|
|
48
|
-
WORK_ITEMS = "WORK_ITEMS"
|
|
49
|
-
}
|
|
50
|
-
type KeyStoreParams = {
|
|
51
|
-
appId?: string;
|
|
52
|
-
engineId?: string;
|
|
53
|
-
appVersion?: string;
|
|
54
|
-
jobId?: string;
|
|
55
|
-
activityId?: string;
|
|
56
|
-
jobKey?: string;
|
|
57
|
-
dateTime?: string;
|
|
58
|
-
facet?: string;
|
|
59
|
-
topic?: string;
|
|
60
|
-
timeValue?: number;
|
|
61
|
-
scoutType?: 'signal' | 'time';
|
|
62
|
-
};
|
|
63
30
|
declare class KeyService {
|
|
64
31
|
/**
|
|
65
32
|
* returns a key that can be used to access a value in the key/value store
|
package/build/modules/key.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.HMNS = exports.KeyType = exports.KeyService = void 0;
|
|
4
|
+
const hotmesh_1 = require("../types/hotmesh");
|
|
5
|
+
Object.defineProperty(exports, "KeyType", { enumerable: true, get: function () { return hotmesh_1.KeyType; } });
|
|
2
6
|
/**
|
|
3
7
|
* Keys
|
|
4
8
|
*
|
|
@@ -26,35 +30,8 @@
|
|
|
26
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)
|
|
27
31
|
* hmsh:<appid>:sym:vals: -> {hash} list of symbols for job values across all app versions
|
|
28
32
|
*/
|
|
29
|
-
|
|
30
|
-
exports.HMNS = exports.KeyType = exports.KeyService = void 0;
|
|
31
|
-
//default namespace for hotmesh
|
|
32
|
-
const HMNS = "hmsh";
|
|
33
|
+
const HMNS = "hmsh"; //default
|
|
33
34
|
exports.HMNS = HMNS;
|
|
34
|
-
//these are the entity types that are stored in the key/value store
|
|
35
|
-
var KeyType;
|
|
36
|
-
(function (KeyType) {
|
|
37
|
-
KeyType["APP"] = "APP";
|
|
38
|
-
KeyType["ENGINE_ID"] = "ENGINE";
|
|
39
|
-
KeyType["HOOKS"] = "HOOKS";
|
|
40
|
-
KeyType["JOB_DEPENDENTS"] = "JOB_DEPENDENTS";
|
|
41
|
-
KeyType["JOB_STATE"] = "JOB_STATE";
|
|
42
|
-
KeyType["JOB_STATS_GENERAL"] = "JOB_STATS_GENERAL";
|
|
43
|
-
KeyType["JOB_STATS_MEDIAN"] = "JOB_STATS_MEDIAN";
|
|
44
|
-
KeyType["JOB_STATS_INDEX"] = "JOB_STATS_INDEX";
|
|
45
|
-
KeyType["HOTMESH"] = "HOTMESH";
|
|
46
|
-
KeyType["QUORUM"] = "QUORUM";
|
|
47
|
-
KeyType["SCHEMAS"] = "SCHEMAS";
|
|
48
|
-
KeyType["SIGNALS"] = "SIGNALS";
|
|
49
|
-
KeyType["STREAMS"] = "STREAMS";
|
|
50
|
-
KeyType["SUBSCRIPTIONS"] = "SUBSCRIPTIONS";
|
|
51
|
-
KeyType["SUBSCRIPTION_PATTERNS"] = "SUBSCRIPTION_PATTERNS";
|
|
52
|
-
KeyType["SYMKEYS"] = "SYMKEYS";
|
|
53
|
-
KeyType["SYMVALS"] = "SYMVALS";
|
|
54
|
-
KeyType["TIME_RANGE"] = "TIME_RANGE";
|
|
55
|
-
KeyType["WORK_ITEMS"] = "WORK_ITEMS";
|
|
56
|
-
})(KeyType || (KeyType = {}));
|
|
57
|
-
exports.KeyType = KeyType;
|
|
58
35
|
class KeyService {
|
|
59
36
|
/**
|
|
60
37
|
* returns a key that can be used to access a value in the key/value store
|
|
@@ -69,47 +46,47 @@ class KeyService {
|
|
|
69
46
|
*/
|
|
70
47
|
static mintKey(namespace, keyType, params) {
|
|
71
48
|
switch (keyType) {
|
|
72
|
-
case KeyType.HOTMESH:
|
|
49
|
+
case hotmesh_1.KeyType.HOTMESH:
|
|
73
50
|
return namespace;
|
|
74
|
-
case KeyType.ENGINE_ID:
|
|
51
|
+
case hotmesh_1.KeyType.ENGINE_ID:
|
|
75
52
|
return `${namespace}:${params.appId}:e:${params.engineId}`;
|
|
76
|
-
case KeyType.WORK_ITEMS:
|
|
53
|
+
case hotmesh_1.KeyType.WORK_ITEMS:
|
|
77
54
|
return `${namespace}:${params.appId}:w:${params.scoutType || ''}`;
|
|
78
|
-
case KeyType.TIME_RANGE:
|
|
55
|
+
case hotmesh_1.KeyType.TIME_RANGE:
|
|
79
56
|
return `${namespace}:${params.appId}:t:${params.timeValue || ''}`;
|
|
80
|
-
case KeyType.APP:
|
|
57
|
+
case hotmesh_1.KeyType.APP:
|
|
81
58
|
return `${namespace}:a:${params.appId || ''}`;
|
|
82
|
-
case KeyType.QUORUM:
|
|
59
|
+
case hotmesh_1.KeyType.QUORUM:
|
|
83
60
|
return `${namespace}:${params.appId}:q:${params.engineId || ''}`;
|
|
84
|
-
case KeyType.JOB_STATE:
|
|
61
|
+
case hotmesh_1.KeyType.JOB_STATE:
|
|
85
62
|
return `${namespace}:${params.appId}:j:${params.jobId}`;
|
|
86
|
-
case KeyType.JOB_DEPENDENTS:
|
|
63
|
+
case hotmesh_1.KeyType.JOB_DEPENDENTS:
|
|
87
64
|
return `${namespace}:${params.appId}:d:${params.jobId}`;
|
|
88
|
-
case KeyType.JOB_STATS_GENERAL:
|
|
65
|
+
case hotmesh_1.KeyType.JOB_STATS_GENERAL:
|
|
89
66
|
return `${namespace}:${params.appId}:s:${params.jobKey}:${params.dateTime}`;
|
|
90
|
-
case KeyType.JOB_STATS_MEDIAN:
|
|
67
|
+
case hotmesh_1.KeyType.JOB_STATS_MEDIAN:
|
|
91
68
|
return `${namespace}:${params.appId}:s:${params.jobKey}:${params.dateTime}:${params.facet}`;
|
|
92
|
-
case KeyType.JOB_STATS_INDEX:
|
|
69
|
+
case hotmesh_1.KeyType.JOB_STATS_INDEX:
|
|
93
70
|
return `${namespace}:${params.appId}:s:${params.jobKey}:${params.dateTime}:${params.facet}`;
|
|
94
|
-
case KeyType.SCHEMAS:
|
|
71
|
+
case hotmesh_1.KeyType.SCHEMAS:
|
|
95
72
|
return `${namespace}:${params.appId}:v:${params.appVersion}:schemas`;
|
|
96
|
-
case KeyType.SUBSCRIPTIONS:
|
|
73
|
+
case hotmesh_1.KeyType.SUBSCRIPTIONS:
|
|
97
74
|
return `${namespace}:${params.appId}:v:${params.appVersion}:subscriptions`;
|
|
98
|
-
case KeyType.SUBSCRIPTION_PATTERNS:
|
|
75
|
+
case hotmesh_1.KeyType.SUBSCRIPTION_PATTERNS:
|
|
99
76
|
return `${namespace}:${params.appId}:v:${params.appVersion}:transitions`;
|
|
100
|
-
case KeyType.HOOKS:
|
|
77
|
+
case hotmesh_1.KeyType.HOOKS:
|
|
101
78
|
//`hooks` provide the pattern to resolve a value
|
|
102
79
|
return `${namespace}:${params.appId}:hooks`;
|
|
103
|
-
case KeyType.SIGNALS:
|
|
80
|
+
case hotmesh_1.KeyType.SIGNALS:
|
|
104
81
|
//`signals` provide the registry of resolved values that link back to paused jobs
|
|
105
82
|
return `${namespace}:${params.appId}:signals`;
|
|
106
|
-
case KeyType.SYMKEYS:
|
|
83
|
+
case hotmesh_1.KeyType.SYMKEYS:
|
|
107
84
|
//`symbol keys` provide the registry of replacement values for job keys
|
|
108
85
|
return `${namespace}:${params.appId}:sym:keys:${params.activityId || ''}`;
|
|
109
|
-
case KeyType.SYMVALS:
|
|
86
|
+
case hotmesh_1.KeyType.SYMVALS:
|
|
110
87
|
//`symbol vals` provide the registry of replacement values for job vals
|
|
111
88
|
return `${namespace}:${params.appId}:sym:vals:`;
|
|
112
|
-
case KeyType.STREAMS:
|
|
89
|
+
case hotmesh_1.KeyType.STREAMS:
|
|
113
90
|
return `${namespace}:${params.appId || ''}:x:${params.topic || ''}`;
|
|
114
91
|
default:
|
|
115
92
|
throw new Error("Invalid key type.");
|
package/build/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotmeshio/hotmesh",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.40",
|
|
4
4
|
"description": "Unbreakable Workflows",
|
|
5
|
-
"main": "build/index.js",
|
|
6
|
-
"types": "build/index.d.ts",
|
|
5
|
+
"main": "./build/index.js",
|
|
6
|
+
"types": "./build/types/index.d.ts",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
9
|
"url": "https://github.com/hotmeshio/sdk-typescript.git"
|
|
@@ -346,7 +346,7 @@ class Activity {
|
|
|
346
346
|
return context;
|
|
347
347
|
}
|
|
348
348
|
initPolicies(context) {
|
|
349
|
-
const expire = pipe_1.Pipe.resolve(this.config.expire ?? enums_1.
|
|
349
|
+
const expire = pipe_1.Pipe.resolve(this.config.expire ?? enums_1.HMSH_EXPIRE_DURATION, context);
|
|
350
350
|
context.metadata.expire = expire;
|
|
351
351
|
}
|
|
352
352
|
bindActivityData(type) {
|
|
@@ -103,17 +103,12 @@ class Hook extends activity_1.Activity {
|
|
|
103
103
|
}
|
|
104
104
|
async registerHook(multi) {
|
|
105
105
|
if (this.config.hook?.topic) {
|
|
106
|
-
|
|
107
|
-
return await taskService.registerWebHook(this.config.hook.topic, this.context, this.resolveDad(), multi);
|
|
106
|
+
return await this.engine.taskService.registerWebHook(this.config.hook.topic, this.context, this.resolveDad(), multi);
|
|
108
107
|
}
|
|
109
108
|
else if (this.config.sleep) {
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
const activityId = this.metadata.aid;
|
|
114
|
-
const dId = this.metadata.dad;
|
|
115
|
-
await this.engine.taskService.registerTimeHook(jobId, gId, `${activityId}${dId || ''}`, 'sleep', durationInSeconds);
|
|
116
|
-
return jobId;
|
|
109
|
+
const duration = pipe_1.Pipe.resolve(this.config.sleep, this.context);
|
|
110
|
+
await this.engine.taskService.registerTimeHook(this.context.metadata.jid, this.context.metadata.gid, `${this.metadata.aid}${this.metadata.dad || ''}`, 'sleep', duration);
|
|
111
|
+
return this.context.metadata.jid;
|
|
117
112
|
}
|
|
118
113
|
}
|
|
119
114
|
//******** SIGNAL RE-ENTRY POINT ********//
|
|
@@ -18,9 +18,10 @@ declare class Trigger extends Activity {
|
|
|
18
18
|
resolveJobKey(context: Partial<JobState>): string;
|
|
19
19
|
setStateNX(): Promise<void>;
|
|
20
20
|
/**
|
|
21
|
-
* Registers this job as a dependent of the parent job
|
|
21
|
+
* Registers this job as a dependent of the parent job; when the
|
|
22
|
+
* parent job is interrupted, this job will be interrupted
|
|
22
23
|
*/
|
|
23
|
-
|
|
24
|
+
registerJobDependency(multi?: RedisMulti): Promise<void>;
|
|
24
25
|
setStats(multi?: RedisMulti): Promise<void>;
|
|
25
26
|
}
|
|
26
27
|
export { Trigger };
|
|
@@ -29,7 +29,7 @@ class Trigger extends activity_1.Activity {
|
|
|
29
29
|
const multi = this.store.getMulti();
|
|
30
30
|
await this.setState(multi);
|
|
31
31
|
await this.setStats(multi);
|
|
32
|
-
await this.
|
|
32
|
+
await this.registerJobDependency(multi);
|
|
33
33
|
await multi.exec();
|
|
34
34
|
telemetry.mapActivityAttributes();
|
|
35
35
|
const jobStatus = Number(this.context.metadata.js);
|
|
@@ -152,13 +152,17 @@ class Trigger extends activity_1.Activity {
|
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
/**
|
|
155
|
-
* Registers this job as a dependent of the parent job
|
|
155
|
+
* Registers this job as a dependent of the parent job; when the
|
|
156
|
+
* parent job is interrupted, this job will be interrupted
|
|
156
157
|
*/
|
|
157
|
-
async
|
|
158
|
-
const depKey = this.config.stats?.parent;
|
|
159
|
-
|
|
158
|
+
async registerJobDependency(multi) {
|
|
159
|
+
const depKey = this.config.stats?.parent ?? this.context.metadata.pj;
|
|
160
|
+
let resolvedDepKey = depKey ? pipe_1.Pipe.resolve(depKey, this.context) : '';
|
|
161
|
+
if (!resolvedDepKey) {
|
|
162
|
+
resolvedDepKey = this.context.metadata.pj;
|
|
163
|
+
}
|
|
160
164
|
if (resolvedDepKey) {
|
|
161
|
-
await this.store.
|
|
165
|
+
await this.store.registerJobDependency(resolvedDepKey, this.context.metadata.tpc, this.context.metadata.jid, this.context.metadata.gid, multi);
|
|
162
166
|
}
|
|
163
167
|
}
|
|
164
168
|
async setStats(multi) {
|
|
@@ -3,10 +3,18 @@ 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[];
|
|
6
7
|
options: WorkflowOptions;
|
|
7
8
|
static instances: Map<string, HotMesh | Promise<HotMesh>>;
|
|
8
9
|
constructor(config: ClientConfig);
|
|
9
|
-
getHotMeshClient: (
|
|
10
|
+
getHotMeshClient: (workflowTopic: string, namespace?: string) => Promise<HotMesh>;
|
|
11
|
+
/**
|
|
12
|
+
* Creates a stream (Redis `XGROUP.CREATE`) where events can be published (XADD).
|
|
13
|
+
* It is possible that the worker that will read from this stream channel
|
|
14
|
+
* has not yet been initialized, so this call ensures that the channel
|
|
15
|
+
* exists and is ready to serve as a container for events.
|
|
16
|
+
*/
|
|
17
|
+
createStream: (hotMeshClient: HotMesh, workflowTopic: string, namespace?: string) => Promise<void>;
|
|
10
18
|
/**
|
|
11
19
|
* For those deployments with a redis stack backend (with the FT module),
|
|
12
20
|
* this method will configure the search index for the workflow.
|
|
@@ -10,13 +10,22 @@ const types_1 = require("../../types");
|
|
|
10
10
|
const enums_1 = require("../../modules/enums");
|
|
11
11
|
class ClientService {
|
|
12
12
|
constructor(config) {
|
|
13
|
-
this.
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
this.topics = [];
|
|
14
|
+
this.getHotMeshClient = async (workflowTopic, namespace) => {
|
|
15
|
+
//use the cached instance
|
|
16
|
+
const instanceId = 'SINGLETON';
|
|
17
|
+
if (ClientService.instances.has(instanceId)) {
|
|
18
|
+
const hotMeshClient = await ClientService.instances.get(instanceId);
|
|
19
|
+
if (!this.topics.includes(workflowTopic)) {
|
|
20
|
+
this.topics.push(workflowTopic);
|
|
21
|
+
await this.createStream(hotMeshClient, workflowTopic, namespace);
|
|
22
|
+
}
|
|
23
|
+
return hotMeshClient;
|
|
17
24
|
}
|
|
25
|
+
//create and cache an instance
|
|
18
26
|
const hotMeshClient = hotmesh_1.HotMeshService.init({
|
|
19
27
|
appId: namespace ?? factory_1.APP_ID,
|
|
28
|
+
logLevel: enums_1.HMSH_LOGLEVEL,
|
|
20
29
|
engine: {
|
|
21
30
|
redis: {
|
|
22
31
|
class: this.connection.class,
|
|
@@ -24,10 +33,20 @@ class ClientService {
|
|
|
24
33
|
}
|
|
25
34
|
}
|
|
26
35
|
});
|
|
27
|
-
ClientService.instances.set(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
ClientService.instances.set(instanceId, hotMeshClient);
|
|
37
|
+
await this.createStream(await hotMeshClient, workflowTopic, namespace);
|
|
38
|
+
await this.activateWorkflow(await hotMeshClient, namespace ?? factory_1.APP_ID);
|
|
39
|
+
return hotMeshClient;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Creates a stream (Redis `XGROUP.CREATE`) where events can be published (XADD).
|
|
43
|
+
* It is possible that the worker that will read from this stream channel
|
|
44
|
+
* has not yet been initialized, so this call ensures that the channel
|
|
45
|
+
* exists and is ready to serve as a container for events.
|
|
46
|
+
*/
|
|
47
|
+
this.createStream = async (hotMeshClient, workflowTopic, namespace) => {
|
|
48
|
+
const store = hotMeshClient.engine.store;
|
|
49
|
+
const params = { appId: namespace ?? factory_1.APP_ID, topic: workflowTopic };
|
|
31
50
|
const streamKey = store.mintKey(key_1.KeyType.STREAMS, params);
|
|
32
51
|
try {
|
|
33
52
|
await store.xgroup('CREATE', streamKey, 'WORKER', '$', 'MKSTREAM');
|
|
@@ -35,8 +54,6 @@ class ClientService {
|
|
|
35
54
|
catch (err) {
|
|
36
55
|
//ignore if already exists
|
|
37
56
|
}
|
|
38
|
-
await this.activateWorkflow(await hotMeshClient, namespace ?? factory_1.APP_ID);
|
|
39
|
-
return hotMeshClient;
|
|
40
57
|
};
|
|
41
58
|
/**
|
|
42
59
|
* For those deployments with a redis stack backend (with the FT module),
|
|
@@ -89,7 +106,7 @@ class ClientService {
|
|
|
89
106
|
const payload = {
|
|
90
107
|
arguments: [...options.args],
|
|
91
108
|
originJobId: options.originJobId,
|
|
92
|
-
expire: options.expire ?? enums_1.
|
|
109
|
+
expire: options.expire ?? enums_1.HMSH_EXPIRE_JOB_SECONDS,
|
|
93
110
|
parentWorkflowId: options.parentWorkflowId,
|
|
94
111
|
workflowId: options.workflowId || hotmesh_1.HotMeshService.guid(),
|
|
95
112
|
workflowTopic: workflowTopic,
|
|
@@ -133,9 +150,8 @@ class ClientService {
|
|
|
133
150
|
if (options.search?.data) {
|
|
134
151
|
const searchSessionId = `-search-${hotmesh_1.HotMeshService.guid()}-0`;
|
|
135
152
|
const search = new search_1.Search(options.workflowId, hotMeshClient, searchSessionId);
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
}
|
|
153
|
+
const entries = Object.entries(options.search.data).flat();
|
|
154
|
+
await search.set(...entries);
|
|
139
155
|
}
|
|
140
156
|
return msgId;
|
|
141
157
|
},
|
|
@@ -91,7 +91,7 @@ class WorkflowHandleService {
|
|
|
91
91
|
const state = await this.hotMesh.getState(`${this.hotMesh.appId}.execute`, this.workflowId);
|
|
92
92
|
if (state.metadata.err) {
|
|
93
93
|
const error = JSON.parse(state.metadata.err);
|
|
94
|
-
if (error.code === enums_1.
|
|
94
|
+
if (error.code === enums_1.HMSH_CODE_INTERRUPT || !state.data) {
|
|
95
95
|
return reject({ ...error, job_id: this.workflowId });
|
|
96
96
|
}
|
|
97
97
|
}
|
|
@@ -107,7 +107,7 @@ class WorkflowHandleService {
|
|
|
107
107
|
this.hotMesh.sub(topic, async (topic, state) => {
|
|
108
108
|
if (state.metadata.err) {
|
|
109
109
|
const error = JSON.parse(state.metadata.err);
|
|
110
|
-
if (error.code === enums_1.
|
|
110
|
+
if (error.code === enums_1.HMSH_CODE_INTERRUPT || !state.data) {
|
|
111
111
|
return await complete(null, state.metadata.err);
|
|
112
112
|
}
|
|
113
113
|
}
|