@hotmeshio/hotmesh 0.0.2 → 0.0.4
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 -13
- package/build/package.json +3 -3
- package/build/services/activities/activity.d.ts +2 -0
- package/build/services/activities/activity.js +27 -9
- package/build/services/activities/worker.js +1 -0
- package/build/services/collator/index.d.ts +7 -5
- package/build/services/collator/index.js +29 -6
- package/build/services/compiler/deployer.d.ts +4 -3
- package/build/services/compiler/deployer.js +14 -0
- package/build/services/dimension/index.d.ts +1 -1
- package/build/services/durable/client.d.ts +3 -3
- package/build/services/durable/client.js +10 -1
- package/build/services/durable/handle.d.ts +1 -1
- package/build/services/durable/worker.js +20 -2
- package/build/services/engine/index.d.ts +1 -1
- package/build/services/engine/index.js +22 -5
- package/build/services/hotmesh/index.d.ts +1 -1
- package/build/services/hotmesh/index.js +2 -2
- package/build/services/serializer/index.d.ts +6 -1
- package/build/services/serializer/index.js +55 -22
- package/build/services/signaler/stream.js +1 -0
- package/build/services/store/index.d.ts +3 -3
- package/build/services/store/index.js +16 -24
- package/build/types/activity.d.ts +1 -0
- package/build/types/hotmesh.d.ts +5 -5
- package/package.json +3 -3
- package/services/activities/activity.ts +28 -9
- package/services/activities/worker.ts +1 -0
- package/services/collator/index.ts +38 -13
- package/services/compiler/deployer.ts +24 -9
- package/services/dimension/index.ts +1 -1
- package/services/durable/client.ts +13 -6
- package/services/durable/handle.ts +2 -2
- package/services/durable/worker.ts +16 -2
- package/services/engine/index.ts +23 -5
- package/services/hotmesh/index.ts +2 -2
- package/services/mapper/index.ts +0 -1
- package/services/serializer/index.ts +57 -22
- package/services/signaler/stream.ts +1 -0
- package/services/store/index.ts +15 -23
- package/types/activity.ts +1 -0
- package/types/hotmesh.ts +5 -5
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# HotMesh
|
|
2
2
|

|
|
3
3
|
|
|
4
|
-
Build sophisticated, durable workflows without the overhead of a dedicated server cluster. With HotMesh, your code remains front and center using [infrastructure](
|
|
4
|
+
Build sophisticated, durable workflows without the overhead of a dedicated server cluster. With HotMesh, your code remains front and center using [infrastructure](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/faq.md#what-is-hotmesh) you already own.
|
|
5
5
|
|
|
6
6
|
## Install
|
|
7
7
|
[](https://badge.fury.io/js/%40hotmeshio%2Fhotmesh)
|
|
@@ -98,7 +98,7 @@ run().catch((err) => {
|
|
|
98
98
|
});
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
-
>HotMesh delivers durable function execution using a
|
|
101
|
+
>HotMesh delivers durable function execution using a [distributed service mesh](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/distributed_orchestration.md). The design consumes leftover CPU on your microservices to execute workflows without the cost and complexity of a central server/control plane.
|
|
102
102
|
|
|
103
103
|
## Advanced Design
|
|
104
104
|
HotMesh's TypeScript SDK is the easiest way to make your functions durable. 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.
|
|
@@ -208,34 +208,34 @@ const hotMesh = await HotMesh.init({
|
|
|
208
208
|
```
|
|
209
209
|
|
|
210
210
|
### Observability
|
|
211
|
-
Workflows and activities are run according to the rules you define, offering [Graph-Oriented](
|
|
211
|
+
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.
|
|
212
212
|
|
|
213
213
|
## FAQ
|
|
214
|
-
Refer to the [FAQ](
|
|
214
|
+
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.
|
|
215
215
|
|
|
216
216
|
## Quick Start
|
|
217
|
-
Refer to the [Quick Start](
|
|
217
|
+
Refer to the [Quick Start](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/quickstart.md) for sample flows you can easily copy, paste, and modify to get started.
|
|
218
218
|
|
|
219
219
|
## Developer Guide
|
|
220
|
-
For more details on the complete development process, including information about schemas, APIs, and deployment, consult the [Developer Guide](
|
|
220
|
+
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).
|
|
221
221
|
|
|
222
222
|
## Model Driven Development
|
|
223
|
-
[Model Driven Development](
|
|
223
|
+
[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.
|
|
224
224
|
|
|
225
225
|
## Data Mapping
|
|
226
|
-
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](
|
|
226
|
+
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).
|
|
227
227
|
|
|
228
228
|
## Composition
|
|
229
|
-
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](
|
|
229
|
+
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).
|
|
230
230
|
|
|
231
231
|
## Architectural First Principles
|
|
232
|
-
For a deep dive into HotMesh's distributed orchestration philosophy, refer to the [Architectural First Principles Overview](
|
|
232
|
+
For a deep dive into HotMesh's distributed orchestration philosophy, refer to the [Architectural First Principles Overview](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/architecture.md).
|
|
233
233
|
|
|
234
234
|
## Distributed Orchestration
|
|
235
|
-
HotMesh is a distributed orchestration engine. Refer to the [Distributed Orchestration Guide](
|
|
235
|
+
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.
|
|
236
236
|
|
|
237
237
|
## System Lifecycle
|
|
238
|
-
Gain insight into the HotMesh's monitoring, exception handling, and alarm configurations via the [System Lifecycle Guide](
|
|
238
|
+
Gain insight into the 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).
|
|
239
239
|
|
|
240
240
|
## Alpha Release
|
|
241
|
-
So what exacty is an [alpha release](
|
|
241
|
+
So what exacty is an [alpha release](https://github.com/hotmeshio/sdk-typescript/blob/main/docs/alpha.md)?!
|
package/build/package.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hotmeshio/hotmesh",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.4",
|
|
4
4
|
"description": "Durable Workflows",
|
|
5
5
|
"main": "build/index.js",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/hotmeshio/
|
|
9
|
+
"url": "https://github.com/hotmeshio/sdk-typescript.git"
|
|
10
10
|
},
|
|
11
|
-
"homepage": "https://github.com/hotmeshio/
|
|
11
|
+
"homepage": "https://github.com/hotmeshio/sdk-typescript#readme",
|
|
12
12
|
"publishConfig": {
|
|
13
13
|
"access": "public"
|
|
14
14
|
},
|
|
@@ -53,6 +53,8 @@ declare class Activity {
|
|
|
53
53
|
initSelf(context: StringAnyType): JobState;
|
|
54
54
|
initPolicies(context: JobState): void;
|
|
55
55
|
bindActivityData(type: 'output' | 'hook'): void;
|
|
56
|
+
resolveDad(): string;
|
|
57
|
+
resolveAdjacentDad(): string;
|
|
56
58
|
filterAdjacent(): Promise<StreamData[]>;
|
|
57
59
|
transition(adjacencyList: StreamData[], jobStatus: JobStatus): Promise<string[]>;
|
|
58
60
|
}
|
|
@@ -100,7 +100,8 @@ class Activity {
|
|
|
100
100
|
const durationInSeconds = pipe_1.Pipe.resolve(this.config.sleep, this.context);
|
|
101
101
|
const jobId = this.context.metadata.jid;
|
|
102
102
|
const activityId = this.metadata.aid;
|
|
103
|
-
|
|
103
|
+
const dId = this.metadata.dad;
|
|
104
|
+
await this.engine.task.registerTimeHook(jobId, `${activityId}${dId || ''}`, 'sleep', durationInSeconds);
|
|
104
105
|
return jobId;
|
|
105
106
|
}
|
|
106
107
|
}
|
|
@@ -158,6 +159,7 @@ class Activity {
|
|
|
158
159
|
return jobStatus;
|
|
159
160
|
}
|
|
160
161
|
catch (error) {
|
|
162
|
+
console.error('this error?', error);
|
|
161
163
|
this.logger.error('engine-process-hook-event-error', error);
|
|
162
164
|
telemetry.setActivityError(error.message);
|
|
163
165
|
throw error;
|
|
@@ -214,11 +216,10 @@ class Activity {
|
|
|
214
216
|
}) ?? [];
|
|
215
217
|
}
|
|
216
218
|
bindDimensionalAddress(state) {
|
|
217
|
-
const
|
|
218
|
-
state[`${aid}/output/metadata/dad`] = dad;
|
|
219
|
+
const dad = this.resolveDad();
|
|
220
|
+
state[`${this.metadata.aid}/output/metadata/dad`] = dad;
|
|
219
221
|
}
|
|
220
222
|
async setState(multi) {
|
|
221
|
-
const { id: appId } = await this.engine.getVID();
|
|
222
223
|
const jobId = this.context.metadata.jid;
|
|
223
224
|
this.bindJobMetadata();
|
|
224
225
|
this.bindActivityMetadata();
|
|
@@ -233,7 +234,8 @@ class Activity {
|
|
|
233
234
|
this.metadata.aid,
|
|
234
235
|
...presets
|
|
235
236
|
];
|
|
236
|
-
|
|
237
|
+
const dIds = collator_1.CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], this.resolveDad());
|
|
238
|
+
return await this.store.setState(state, this.getJobStatus(), jobId, symbolNames, dIds, multi);
|
|
237
239
|
}
|
|
238
240
|
bindJobMetadata() {
|
|
239
241
|
//both legs of the most recently run activity (1 and 2) modify ju (job_updated)
|
|
@@ -247,6 +249,7 @@ class Activity {
|
|
|
247
249
|
if (this.status === stream_1.StreamStatus.ERROR) {
|
|
248
250
|
self.output.metadata.err = JSON.stringify(this.data);
|
|
249
251
|
}
|
|
252
|
+
//todo: verify leg2 never overwrites leg1 `ac`
|
|
250
253
|
self.output.metadata.ac =
|
|
251
254
|
self.output.metadata.au = (0, utils_1.formatISODate)(new Date());
|
|
252
255
|
self.output.metadata.atp = this.config.type;
|
|
@@ -315,10 +318,11 @@ class Activity {
|
|
|
315
318
|
}
|
|
316
319
|
}
|
|
317
320
|
telemetry_1.TelemetryService.addTargetTelemetryPaths(consumes, this.config, this.metadata, this.leg);
|
|
318
|
-
|
|
321
|
+
let { dad, jid } = this.context.metadata;
|
|
319
322
|
jobId = jobId || jid;
|
|
320
323
|
//`state` is a flat hash
|
|
321
|
-
const
|
|
324
|
+
const dIds = collator_1.CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], dad);
|
|
325
|
+
const [state, status] = await this.store.getState(jobId, consumes, dIds);
|
|
322
326
|
//`context` is a tree
|
|
323
327
|
this.context = (0, utils_1.restoreHierarchy)(state);
|
|
324
328
|
this.initDimensionalAddress(dad);
|
|
@@ -353,11 +357,25 @@ class Activity {
|
|
|
353
357
|
bindActivityData(type) {
|
|
354
358
|
this.context[this.metadata.aid][type].data = this.data;
|
|
355
359
|
}
|
|
360
|
+
resolveDad() {
|
|
361
|
+
let dad = this.metadata.dad;
|
|
362
|
+
if (this.adjacentIndex > 0) {
|
|
363
|
+
//if adjacent index > 0 the activity is cycling; replace last index with cycle index
|
|
364
|
+
dad = `${dad.substring(0, dad.lastIndexOf(','))},${this.adjacentIndex}`;
|
|
365
|
+
}
|
|
366
|
+
return dad;
|
|
367
|
+
}
|
|
368
|
+
resolveAdjacentDad() {
|
|
369
|
+
//concat self and child dimension (all children (leg 1) begin life at 0)
|
|
370
|
+
return `${this.resolveDad()}${dimension_1.DimensionService.getSeed(0)}`;
|
|
371
|
+
}
|
|
372
|
+
;
|
|
356
373
|
async filterAdjacent() {
|
|
357
374
|
const adjacencyList = [];
|
|
358
375
|
const transitions = await this.store.getTransitions(await this.engine.getVID());
|
|
359
376
|
const transition = transitions[`.${this.metadata.aid}`];
|
|
360
|
-
|
|
377
|
+
//resolve the dimensional address for adjacent children
|
|
378
|
+
const adjacentDad = this.resolveAdjacentDad();
|
|
361
379
|
if (transition) {
|
|
362
380
|
for (const toActivityId in transition) {
|
|
363
381
|
const transitionRule = transition[toActivityId];
|
|
@@ -365,7 +383,7 @@ class Activity {
|
|
|
365
383
|
adjacencyList.push({
|
|
366
384
|
metadata: {
|
|
367
385
|
jid: this.context.metadata.jid,
|
|
368
|
-
dad:
|
|
386
|
+
dad: adjacentDad,
|
|
369
387
|
aid: toActivityId,
|
|
370
388
|
spn: this.context['$self'].output.metadata?.l2s,
|
|
371
389
|
trc: this.context.metadata.trc,
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { RedisMulti } from
|
|
2
|
-
import { CollationStage } from
|
|
3
|
-
import { ActivityDuplex } from
|
|
4
|
-
import { HotMeshGraph } from
|
|
5
|
-
import { Activity } from
|
|
1
|
+
import { RedisMulti } from '../../types/redis';
|
|
2
|
+
import { CollationStage } from '../../types/collator';
|
|
3
|
+
import { ActivityDuplex } from '../../types/activity';
|
|
4
|
+
import { HotMeshGraph } from '../../types/hotmesh';
|
|
5
|
+
import { Activity } from '../activities/activity';
|
|
6
6
|
declare class CollatorService {
|
|
7
7
|
static targetLength: number;
|
|
8
|
+
static getDimensionalAddress(activity: Activity): Record<string, string>;
|
|
8
9
|
static notarizeEntry(activity: Activity, multi?: RedisMulti): Promise<number>;
|
|
9
10
|
static authorizeReentry(activity: Activity, multi?: RedisMulti): Promise<number>;
|
|
10
11
|
static notarizeEarlyCompletion(activity: Activity, multi?: RedisMulti): Promise<number>;
|
|
@@ -17,6 +18,7 @@ declare class CollatorService {
|
|
|
17
18
|
static isInactive(num: number): boolean;
|
|
18
19
|
static isPrimed(amount: number, leg: ActivityDuplex): boolean;
|
|
19
20
|
static verifyInteger(amount: number, leg: ActivityDuplex, stage: CollationStage): void;
|
|
21
|
+
static getDimensionsById(ancestors: string[], dad: string): Record<string, string>;
|
|
20
22
|
/**
|
|
21
23
|
* All non-trigger activities are assigned a status seed by their parent
|
|
22
24
|
*/
|
|
@@ -4,9 +4,18 @@ exports.CollatorService = void 0;
|
|
|
4
4
|
const errors_1 = require("../../modules/errors");
|
|
5
5
|
const collator_1 = require("../../types/collator");
|
|
6
6
|
class CollatorService {
|
|
7
|
+
static getDimensionalAddress(activity) {
|
|
8
|
+
let dad = activity.context.metadata.dad || activity.metadata.dad;
|
|
9
|
+
//todo: unsure about this reset
|
|
10
|
+
// if (dad && activity.leg === 2) {
|
|
11
|
+
// console.log('setting dad index back to 0=>', dad);
|
|
12
|
+
// dad = `${dad.substring(0, dad.lastIndexOf(','))},0`;
|
|
13
|
+
// }
|
|
14
|
+
return CollatorService.getDimensionsById([...activity.config.ancestors, activity.metadata.aid], dad);
|
|
15
|
+
}
|
|
7
16
|
static async notarizeEntry(activity, multi) {
|
|
8
17
|
//decrement by -100_000_000_000_000
|
|
9
|
-
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, -100000000000000, multi);
|
|
18
|
+
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, -100000000000000, this.getDimensionalAddress(activity), multi);
|
|
10
19
|
this.verifyInteger(amount, 1, 'enter');
|
|
11
20
|
return amount;
|
|
12
21
|
}
|
|
@@ -14,30 +23,30 @@ class CollatorService {
|
|
|
14
23
|
static async authorizeReentry(activity, multi) {
|
|
15
24
|
//set second digit to 8, allowing for re-entry
|
|
16
25
|
//decrement by -10_000_000_000_000
|
|
17
|
-
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, -10000000000000, multi);
|
|
26
|
+
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, -10000000000000, this.getDimensionalAddress(activity), multi);
|
|
18
27
|
//this.verifyInteger(amount, 1, 'exit');
|
|
19
28
|
return amount;
|
|
20
29
|
}
|
|
21
30
|
static async notarizeEarlyCompletion(activity, multi) {
|
|
22
31
|
//initialize both `possible` (1m) and `actualized` (1) zero dimension, while decrementing the 2nd and 3rd digits to deactivate the activity
|
|
23
|
-
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1000001 - 11000000000000, multi);
|
|
32
|
+
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1000001 - 11000000000000, this.getDimensionalAddress(activity), multi);
|
|
24
33
|
}
|
|
25
34
|
;
|
|
26
35
|
static async notarizeReentry(activity, multi) {
|
|
27
36
|
//increment by 1_000_000 (indicates re-entry and is used to drive the 'dimensional address' for adjacent activities (minus 1))
|
|
28
|
-
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1000000, multi);
|
|
37
|
+
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1000000, this.getDimensionalAddress(activity), multi);
|
|
29
38
|
this.verifyInteger(amount, 2, 'enter');
|
|
30
39
|
return amount;
|
|
31
40
|
}
|
|
32
41
|
;
|
|
33
42
|
static async notarizeContinuation(activity, multi) {
|
|
34
43
|
//keep open; actualize the leg2 dimension (+1)
|
|
35
|
-
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1, multi);
|
|
44
|
+
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1, this.getDimensionalAddress(activity), multi);
|
|
36
45
|
}
|
|
37
46
|
;
|
|
38
47
|
static async notarizeCompletion(activity, multi) {
|
|
39
48
|
//close out; actualize leg2 dimension (+1) and decrement the 3rd digit (-1_000_000_000_000)
|
|
40
|
-
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1 - 1000000000000, multi);
|
|
49
|
+
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1 - 1000000000000, this.getDimensionalAddress(activity), multi);
|
|
41
50
|
}
|
|
42
51
|
;
|
|
43
52
|
static getDigitAtIndex(num, targetDigitIndex) {
|
|
@@ -106,6 +115,20 @@ class CollatorService {
|
|
|
106
115
|
throw new errors_1.CollationError(amount, leg, stage, faultType);
|
|
107
116
|
}
|
|
108
117
|
}
|
|
118
|
+
static getDimensionsById(ancestors, dad) {
|
|
119
|
+
//ancestors is an ordered list of all ancestors, starting with the trigger (['t1', 'a1', 'a2'])
|
|
120
|
+
//dad is the dimensional address of the ancestors list (',0,5,3')
|
|
121
|
+
//loop through the ancestors list and create a map of the ancestor to the dimensional address.
|
|
122
|
+
//return { 't1': ',0', 'a1': ',0,5', 'a1': ',0,5,3', $ADJACENT: ',0,5,3,0' };
|
|
123
|
+
// `adjacent` is a special key that is used to track the dimensional address of adjacent activities
|
|
124
|
+
const map = { '$ADJACENT': `${dad},0` };
|
|
125
|
+
let dadStr = dad;
|
|
126
|
+
ancestors.reverse().forEach((ancestor) => {
|
|
127
|
+
map[ancestor] = dadStr;
|
|
128
|
+
dadStr = dadStr.substring(0, dadStr.lastIndexOf(','));
|
|
129
|
+
});
|
|
130
|
+
return map;
|
|
131
|
+
}
|
|
109
132
|
/**
|
|
110
133
|
* All non-trigger activities are assigned a status seed by their parent
|
|
111
134
|
*/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { StoreService } from '../store';
|
|
2
|
-
import { HotMeshGraph, HotMeshManifest } from
|
|
3
|
-
import { RedisClient, RedisMulti } from
|
|
4
|
-
import { Symbols } from
|
|
2
|
+
import { HotMeshGraph, HotMeshManifest } from '../../types/hotmesh';
|
|
3
|
+
import { RedisClient, RedisMulti } from '../../types/redis';
|
|
4
|
+
import { Symbols } from '../../types/serializer';
|
|
5
5
|
declare class Deployer {
|
|
6
6
|
manifest: HotMeshManifest | null;
|
|
7
7
|
store: StoreService<RedisClient, RedisMulti> | null;
|
|
@@ -12,6 +12,7 @@ declare class Deployer {
|
|
|
12
12
|
version: string;
|
|
13
13
|
};
|
|
14
14
|
generateSymKeys(): Promise<void>;
|
|
15
|
+
bindSelf(consumes: Record<string, string[]>, produces: string[], activityId: string): void;
|
|
15
16
|
bindSymbols(startIndex: number, maxIndex: number, existingSymbols: Symbols, prefix: string, produces: string[]): Symbols;
|
|
16
17
|
copyJobSchemas(): void;
|
|
17
18
|
bindBackRefs(): void;
|
|
@@ -53,6 +53,7 @@ class Deployer {
|
|
|
53
53
|
for (const [activityId, activity] of Object.entries(graph.activities)) {
|
|
54
54
|
const [lower, upper, symbols] = await this.store.reserveSymbolRange(activityId, DEFAULT_RANGE_SIZE, 'ACTIVITY');
|
|
55
55
|
const prefix = `${activityId}/`; //activity meta/data is namespaced
|
|
56
|
+
this.bindSelf(activity.consumes, activity.produces, activityId);
|
|
56
57
|
const newSymbols = this.bindSymbols(lower, upper, symbols, prefix, activity.produces);
|
|
57
58
|
if (Object.keys(newSymbols).length) {
|
|
58
59
|
await this.store.addSymbols(activityId, newSymbols);
|
|
@@ -60,6 +61,19 @@ class Deployer {
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
}
|
|
64
|
+
bindSelf(consumes, produces, activityId) {
|
|
65
|
+
//bind self-referential mappings
|
|
66
|
+
for (const selfId of [activityId, '$self']) {
|
|
67
|
+
const selfConsumes = consumes[selfId];
|
|
68
|
+
if (selfConsumes) {
|
|
69
|
+
for (const path of selfConsumes) {
|
|
70
|
+
if (!produces.includes(path)) {
|
|
71
|
+
produces.push(path);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
63
77
|
bindSymbols(startIndex, maxIndex, existingSymbols, prefix, produces) {
|
|
64
78
|
let newSymbols = {};
|
|
65
79
|
let currentSymbols = { ...existingSymbols };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { WorkflowHandleService } from
|
|
2
|
-
import { HotMeshService as HotMesh } from
|
|
3
|
-
import { ClientConfig, Connection, WorkflowOptions } from
|
|
1
|
+
import { WorkflowHandleService } from './handle';
|
|
2
|
+
import { HotMeshService as HotMesh } from '../hotmesh';
|
|
3
|
+
import { ClientConfig, Connection, WorkflowOptions } from '../../types/durable';
|
|
4
4
|
export declare class ClientService {
|
|
5
5
|
connection: Connection;
|
|
6
6
|
options: WorkflowOptions;
|
|
@@ -92,7 +92,16 @@ class ClientService {
|
|
|
92
92
|
await hotMesh.activate(version);
|
|
93
93
|
}
|
|
94
94
|
catch (err) {
|
|
95
|
-
hotMesh.engine.logger.error('durable-client-
|
|
95
|
+
hotMesh.engine.logger.error('durable-client-deploy-activate-err', err);
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
else if (app && !app.active) {
|
|
100
|
+
try {
|
|
101
|
+
await hotMesh.activate(version);
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
hotMesh.engine.logger.error('durable-client-activate-err', err);
|
|
96
105
|
throw err;
|
|
97
106
|
}
|
|
98
107
|
}
|
|
@@ -73,7 +73,16 @@ class WorkerService {
|
|
|
73
73
|
await hotMesh.activate(version);
|
|
74
74
|
}
|
|
75
75
|
catch (err) {
|
|
76
|
-
hotMesh.engine.logger.error('durable-worker-
|
|
76
|
+
hotMesh.engine.logger.error('durable-worker-deploy-activate-err', err);
|
|
77
|
+
throw err;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else if (app && !app.active) {
|
|
81
|
+
try {
|
|
82
|
+
await hotMesh.activate(version);
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
hotMesh.engine.logger.error('durable-worker-activate-err', err);
|
|
77
86
|
throw err;
|
|
78
87
|
}
|
|
79
88
|
}
|
|
@@ -176,7 +185,16 @@ class WorkerService {
|
|
|
176
185
|
await hotMesh.activate(version);
|
|
177
186
|
}
|
|
178
187
|
catch (err) {
|
|
179
|
-
console.log('durable-worker-activity-
|
|
188
|
+
console.log('durable-worker-activity-deploy-activate-error', err);
|
|
189
|
+
throw err;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else if (app && !app.active) {
|
|
193
|
+
try {
|
|
194
|
+
await hotMesh.activate(version);
|
|
195
|
+
}
|
|
196
|
+
catch (err) {
|
|
197
|
+
hotMesh.engine.logger.error('durable-worker-activity-activate-err', err);
|
|
180
198
|
throw err;
|
|
181
199
|
}
|
|
182
200
|
}
|
|
@@ -58,7 +58,7 @@ declare class EngineService {
|
|
|
58
58
|
hasParentJob(context: JobState): boolean;
|
|
59
59
|
resolveError(metadata: JobMetadata): StreamError | undefined;
|
|
60
60
|
scrub(jobId: string): Promise<void>;
|
|
61
|
-
hook(topic: string, data: JobData): Promise<JobStatus | void>;
|
|
61
|
+
hook(topic: string, data: JobData, dad?: string): Promise<JobStatus | void>;
|
|
62
62
|
hookTime(jobId: string, activityId: string): Promise<JobStatus | void>;
|
|
63
63
|
hookAll(hookTopic: string, data: JobData, query: JobStatsInput, queryFacets?: string[]): Promise<string[]>;
|
|
64
64
|
pub(topic: string, data: JobData, context?: JobState): Promise<string>;
|
|
@@ -317,21 +317,37 @@ class EngineService {
|
|
|
317
317
|
await this.store.scrub(jobId);
|
|
318
318
|
}
|
|
319
319
|
// ****************** `HOOK` ACTIVITY RE-ENTRY POINT *****************
|
|
320
|
-
async hook(topic, data) {
|
|
320
|
+
async hook(topic, data, dad) {
|
|
321
321
|
const hookRule = await this.storeSignaler.getHookRule(topic);
|
|
322
|
+
const [aid, schema] = await this.getSchema(`.${hookRule.to}`);
|
|
323
|
+
if (!dad) {
|
|
324
|
+
//assume dimensional address is singular (0)
|
|
325
|
+
// for ancestors and self if not provided
|
|
326
|
+
// todo: register
|
|
327
|
+
dad = ',0'.repeat(schema.ancestors.length + 1);
|
|
328
|
+
}
|
|
322
329
|
const streamData = {
|
|
323
330
|
type: stream_2.StreamDataType.WEBHOOK,
|
|
324
|
-
metadata: {
|
|
331
|
+
metadata: {
|
|
332
|
+
//jid is unknown at this point; will be resolved using the data
|
|
333
|
+
aid,
|
|
334
|
+
dad,
|
|
335
|
+
topic
|
|
336
|
+
},
|
|
325
337
|
data,
|
|
326
338
|
};
|
|
327
339
|
await this.streamSignaler.publishMessage(null, streamData);
|
|
328
340
|
}
|
|
329
341
|
async hookTime(jobId, activityId) {
|
|
342
|
+
//the activityid is concatenated with its dimensional address (dad); split to resolve
|
|
343
|
+
const [aid, ...dimensions] = activityId.split(',');
|
|
344
|
+
const dad = `,${dimensions.join(',')}`;
|
|
330
345
|
const streamData = {
|
|
331
346
|
type: stream_2.StreamDataType.TIMEHOOK,
|
|
332
347
|
metadata: {
|
|
333
348
|
jid: jobId,
|
|
334
|
-
aid
|
|
349
|
+
aid,
|
|
350
|
+
dad,
|
|
335
351
|
},
|
|
336
352
|
data: { timestamp: Date.now() },
|
|
337
353
|
};
|
|
@@ -484,12 +500,13 @@ class EngineService {
|
|
|
484
500
|
// (e.g, if {dimensions:true}, use hscan to deliver
|
|
485
501
|
// the full set of dimensional job data)
|
|
486
502
|
async getState(topic, jobId) {
|
|
487
|
-
const { id: appId } = await this.getVID();
|
|
488
503
|
const jobSymbols = await this.store.getSymbols(`$${topic}`);
|
|
489
504
|
const consumes = {
|
|
490
505
|
[`$${topic}`]: Object.keys(jobSymbols)
|
|
491
506
|
};
|
|
492
|
-
|
|
507
|
+
//job data exists at the 'zero' dimension; pass an empty object
|
|
508
|
+
const dIds = {};
|
|
509
|
+
const output = await this.store.getState(jobId, consumes, dIds);
|
|
493
510
|
if (!output) {
|
|
494
511
|
throw new Error(`not found ${jobId}`);
|
|
495
512
|
}
|
|
@@ -37,7 +37,7 @@ declare class HotMeshService {
|
|
|
37
37
|
getIds(topic: string, query: JobStatsInput, queryFacets?: any[]): Promise<IdsResponse>;
|
|
38
38
|
resolveQuery(topic: string, query: JobStatsInput): Promise<GetStatsOptions>;
|
|
39
39
|
scrub(jobId: string): Promise<void>;
|
|
40
|
-
hook(topic: string, data: JobData): Promise<JobStatus | void>;
|
|
40
|
+
hook(topic: string, data: JobData, dad?: string): Promise<JobStatus | void>;
|
|
41
41
|
hookAll(hookTopic: string, data: JobData, query: JobStatsInput, queryFacets?: string[]): Promise<string[]>;
|
|
42
42
|
stop(): Promise<void>;
|
|
43
43
|
compress(terms: string[]): Promise<boolean>;
|
|
@@ -116,9 +116,9 @@ class HotMeshService {
|
|
|
116
116
|
await this.engine?.scrub(jobId);
|
|
117
117
|
}
|
|
118
118
|
// ****** `HOOK` ACTIVITY RE-ENTRY POINT ******
|
|
119
|
-
async hook(topic, data) {
|
|
119
|
+
async hook(topic, data, dad) {
|
|
120
120
|
//return collation int
|
|
121
|
-
return await this.engine?.hook(topic, data);
|
|
121
|
+
return await this.engine?.hook(topic, data, dad);
|
|
122
122
|
}
|
|
123
123
|
async hookAll(hookTopic, data, query, queryFacets = []) {
|
|
124
124
|
return await this.engine?.hookAll(hookTopic, data, query, queryFacets);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Consumes } from '../../types/activity';
|
|
1
2
|
import { StringStringType, StringAnyType, SymbolMap, SymbolMaps, SymbolSets, Symbols } from '../../types/serializer';
|
|
2
3
|
export declare const MDATA_SYMBOLS: {
|
|
3
4
|
SLOTS: number;
|
|
@@ -15,12 +16,16 @@ export declare const MDATA_SYMBOLS: {
|
|
|
15
16
|
};
|
|
16
17
|
};
|
|
17
18
|
export declare class SerializerService {
|
|
19
|
+
dIds: StringStringType;
|
|
18
20
|
symKeys: SymbolMaps;
|
|
19
21
|
symReverseKeys: SymbolMaps;
|
|
20
22
|
symValMaps: SymbolMap;
|
|
21
23
|
symValReverseMaps: SymbolMap;
|
|
22
24
|
constructor();
|
|
23
|
-
|
|
25
|
+
abbreviate(consumes: Consumes, symbolNames: string[], fields?: string[]): string[];
|
|
26
|
+
resolveDimensionalIndex(path: string): string;
|
|
27
|
+
isJobPath(path: string): boolean;
|
|
28
|
+
resetSymbols(symKeys: SymbolSets, symVals: Symbols, dIds: StringStringType): void;
|
|
24
29
|
getReverseKeyMap(keyMap: SymbolMap, id?: string): SymbolMap;
|
|
25
30
|
getReverseValueMap(valueMap: SymbolMap): SymbolMap;
|
|
26
31
|
static filterSymVals(startIndex: number, maxIndex: number, existingSymbolValues: Symbols, proposedValues: Set<string>): Symbols;
|