@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
|
@@ -20,9 +20,45 @@ exports.MDATA_SYMBOLS = {
|
|
|
20
20
|
};
|
|
21
21
|
class SerializerService {
|
|
22
22
|
constructor() {
|
|
23
|
-
this.resetSymbols({}, {});
|
|
23
|
+
this.resetSymbols({}, {}, {});
|
|
24
24
|
}
|
|
25
|
-
|
|
25
|
+
abbreviate(consumes, symbolNames, fields = []) {
|
|
26
|
+
for (const symbolName of symbolNames) {
|
|
27
|
+
const symbolSet = this.symKeys.get(symbolName);
|
|
28
|
+
const symbolPaths = consumes[symbolName];
|
|
29
|
+
for (const symbolPath of symbolPaths) {
|
|
30
|
+
const abbreviation = symbolSet.get(symbolPath);
|
|
31
|
+
if (abbreviation) {
|
|
32
|
+
const dimensionalIndex = this.resolveDimensionalIndex(symbolPath);
|
|
33
|
+
fields.push(`${abbreviation}${dimensionalIndex}`);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
fields.push(symbolPath);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return fields;
|
|
41
|
+
}
|
|
42
|
+
resolveDimensionalIndex(path) {
|
|
43
|
+
if (this.isJobPath(path)) {
|
|
44
|
+
return '';
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
const [activityId] = path.split('/');
|
|
48
|
+
if (activityId in this.dIds) {
|
|
49
|
+
return this.dIds[activityId];
|
|
50
|
+
}
|
|
51
|
+
else if ('$ADJACENT' in this.dIds) {
|
|
52
|
+
//else=> pre-authorizing adjacent activity entry
|
|
53
|
+
return this.dIds['$ADJACENT'];
|
|
54
|
+
}
|
|
55
|
+
return ',0';
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
isJobPath(path) {
|
|
59
|
+
return path.startsWith('data/') || path.startsWith('metadata/');
|
|
60
|
+
}
|
|
61
|
+
resetSymbols(symKeys, symVals, dIds) {
|
|
26
62
|
this.symKeys = new Map();
|
|
27
63
|
this.symReverseKeys = new Map();
|
|
28
64
|
for (const id in symKeys) {
|
|
@@ -30,6 +66,7 @@ class SerializerService {
|
|
|
30
66
|
}
|
|
31
67
|
this.symValMaps = new Map(Object.entries(symVals));
|
|
32
68
|
this.symValReverseMaps = this.getReverseValueMap(this.symValMaps);
|
|
69
|
+
this.dIds = dIds;
|
|
33
70
|
}
|
|
34
71
|
getReverseKeyMap(keyMap, id) {
|
|
35
72
|
let map = this.symReverseKeys.get(id);
|
|
@@ -70,24 +107,22 @@ class SerializerService {
|
|
|
70
107
|
if (this.symKeys.size === 0) {
|
|
71
108
|
return document;
|
|
72
109
|
}
|
|
73
|
-
let
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
delete result[key];
|
|
83
|
-
}
|
|
110
|
+
let source = { ...document };
|
|
111
|
+
let result = {};
|
|
112
|
+
const compressWithMap = (abbreviationMap, id) => {
|
|
113
|
+
for (let key in source) {
|
|
114
|
+
if (key.startsWith(`${id}/`) || (id.startsWith('$') && ['data', 'metadata'].includes(key.split('/')[0]))) {
|
|
115
|
+
const dimensionalIndex = this.resolveDimensionalIndex(key);
|
|
116
|
+
let shortKey = abbreviationMap.get(key) || key;
|
|
117
|
+
const shortDimensionalKey = `${shortKey}${dimensionalIndex}`;
|
|
118
|
+
result[shortDimensionalKey] = source[key];
|
|
84
119
|
}
|
|
85
120
|
}
|
|
86
121
|
};
|
|
87
122
|
for (let id of ids) {
|
|
88
123
|
const abbreviationMap = this.symKeys.get(id);
|
|
89
124
|
if (abbreviationMap) {
|
|
90
|
-
compressWithMap(abbreviationMap);
|
|
125
|
+
compressWithMap(abbreviationMap, id);
|
|
91
126
|
}
|
|
92
127
|
}
|
|
93
128
|
return result;
|
|
@@ -100,14 +135,12 @@ class SerializerService {
|
|
|
100
135
|
const inflateWithMap = (abbreviationMap, id) => {
|
|
101
136
|
const reversedAbbreviationMap = this.getReverseKeyMap(abbreviationMap, id);
|
|
102
137
|
for (let key in result) {
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
let
|
|
106
|
-
if (
|
|
107
|
-
result[
|
|
108
|
-
|
|
109
|
-
delete result[key];
|
|
110
|
-
}
|
|
138
|
+
//strip dimensional index from key
|
|
139
|
+
const shortKey = key.split(',')[0];
|
|
140
|
+
let longKey = reversedAbbreviationMap.get(shortKey);
|
|
141
|
+
if (longKey) {
|
|
142
|
+
result[longKey] = result[key];
|
|
143
|
+
delete result[key];
|
|
111
144
|
}
|
|
112
145
|
}
|
|
113
146
|
};
|
|
@@ -59,9 +59,9 @@ declare abstract class StoreService<T, U extends AbstractRedisClient> {
|
|
|
59
59
|
getJobIds(indexKeys: string[], idRange: [number, number]): Promise<IdsData>;
|
|
60
60
|
setStatus(collationKeyStatus: number, jobId: string, appId: string, multi?: U): Promise<any>;
|
|
61
61
|
getStatus(jobId: string, appId: string): Promise<number>;
|
|
62
|
-
setState({ ...state }: StringAnyType, status: number | null, jobId: string,
|
|
63
|
-
getState(jobId: string, consumes: Consumes): Promise<[StringAnyType, number] | undefined>;
|
|
64
|
-
collate(jobId: string, activityId: string, amount: number, multi?: U): Promise<number>;
|
|
62
|
+
setState({ ...state }: StringAnyType, status: number | null, jobId: string, symbolNames: string[], dIds: StringStringType, multi?: U): Promise<string>;
|
|
63
|
+
getState(jobId: string, consumes: Consumes, dIds: StringStringType): Promise<[StringAnyType, number] | undefined>;
|
|
64
|
+
collate(jobId: string, activityId: string, amount: number, dIds: StringStringType, multi?: U): Promise<number>;
|
|
65
65
|
setStateNX(jobId: string, appId: string): Promise<boolean>;
|
|
66
66
|
getSchema(activityId: string, appVersion: AppVID): Promise<ActivityType>;
|
|
67
67
|
getSchemas(appVersion: AppVID): Promise<Record<string, ActivityType>>;
|
|
@@ -238,7 +238,12 @@ class StoreService {
|
|
|
238
238
|
app = {};
|
|
239
239
|
for (const field in sApp) {
|
|
240
240
|
try {
|
|
241
|
-
|
|
241
|
+
if (field === 'active') {
|
|
242
|
+
app[field] = sApp[field] === 'true';
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
app[field] = sApp[field];
|
|
246
|
+
}
|
|
242
247
|
}
|
|
243
248
|
catch (e) {
|
|
244
249
|
app[field] = sApp[field];
|
|
@@ -369,12 +374,12 @@ class StoreService {
|
|
|
369
374
|
const status = await this.redisClient[this.commands.hget](jobKey, ':');
|
|
370
375
|
return Number(status);
|
|
371
376
|
}
|
|
372
|
-
async setState({ ...state }, status, jobId,
|
|
377
|
+
async setState({ ...state }, status, jobId, symbolNames, dIds, multi) {
|
|
373
378
|
delete state['metadata/js'];
|
|
374
|
-
const hashKey = this.mintKey(key_1.KeyType.JOB_STATE, { appId, jobId });
|
|
379
|
+
const hashKey = this.mintKey(key_1.KeyType.JOB_STATE, { appId: this.appId, jobId });
|
|
375
380
|
const symKeys = await this.getSymbolKeys(symbolNames);
|
|
376
381
|
const symVals = await this.getSymbolValues();
|
|
377
|
-
this.serializer.resetSymbols(symKeys, symVals);
|
|
382
|
+
this.serializer.resetSymbols(symKeys, symVals, dIds);
|
|
378
383
|
const hashData = this.serializer.package(state, symbolNames);
|
|
379
384
|
if (status !== null) {
|
|
380
385
|
hashData[':'] = status.toString();
|
|
@@ -385,26 +390,13 @@ class StoreService {
|
|
|
385
390
|
await (multi || this.redisClient)[this.commands.hset](hashKey, hashData);
|
|
386
391
|
return jobId;
|
|
387
392
|
}
|
|
388
|
-
async getState(jobId, consumes) {
|
|
389
|
-
//
|
|
393
|
+
async getState(jobId, consumes, dIds) {
|
|
394
|
+
//get abbreviated field list (the symbols for the paths)
|
|
390
395
|
const key = this.mintKey(key_1.KeyType.JOB_STATE, { appId: this.appId, jobId });
|
|
391
396
|
const symbolNames = Object.keys(consumes);
|
|
392
397
|
const symKeys = await this.getSymbolKeys(symbolNames);
|
|
393
|
-
|
|
394
|
-
const fields = [':'];
|
|
395
|
-
for (const symbolName of symbolNames) {
|
|
396
|
-
const symbolSet = symKeys[symbolName];
|
|
397
|
-
const symbolPaths = consumes[symbolName];
|
|
398
|
-
for (const symbolPath of symbolPaths) {
|
|
399
|
-
const abbreviation = symbolSet[symbolPath];
|
|
400
|
-
if (abbreviation) {
|
|
401
|
-
fields.push(abbreviation);
|
|
402
|
-
}
|
|
403
|
-
else {
|
|
404
|
-
fields.push(symbolPath);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
}
|
|
398
|
+
this.serializer.resetSymbols(symKeys, {}, dIds);
|
|
399
|
+
const fields = this.serializer.abbreviate(consumes, symbolNames, [':']);
|
|
408
400
|
const jobDataArray = await this.redisClient[this.commands.hmget](key, fields);
|
|
409
401
|
const jobData = {};
|
|
410
402
|
let atLeast1 = false; //if status field (':') isn't present assume 404
|
|
@@ -416,7 +408,7 @@ class StoreService {
|
|
|
416
408
|
});
|
|
417
409
|
if (atLeast1) {
|
|
418
410
|
const symVals = await this.getSymbolValues();
|
|
419
|
-
this.serializer.resetSymbols(symKeys, symVals);
|
|
411
|
+
this.serializer.resetSymbols(symKeys, symVals, dIds);
|
|
420
412
|
const state = this.serializer.unpackage(jobData, symbolNames);
|
|
421
413
|
let status = 0;
|
|
422
414
|
if (state[':']) {
|
|
@@ -427,13 +419,13 @@ class StoreService {
|
|
|
427
419
|
return [state, status];
|
|
428
420
|
}
|
|
429
421
|
}
|
|
430
|
-
async collate(jobId, activityId, amount, multi) {
|
|
422
|
+
async collate(jobId, activityId, amount, dIds, multi) {
|
|
431
423
|
const jobKey = this.mintKey(key_1.KeyType.JOB_STATE, { appId: this.appId, jobId });
|
|
432
424
|
const collationKey = `${activityId}/output/metadata/as`; //activity state
|
|
433
425
|
const symbolNames = [activityId];
|
|
434
426
|
const symKeys = await this.getSymbolKeys(symbolNames);
|
|
435
427
|
const symVals = await this.getSymbolValues();
|
|
436
|
-
this.serializer.resetSymbols(symKeys, symVals);
|
|
428
|
+
this.serializer.resetSymbols(symKeys, symVals, dIds);
|
|
437
429
|
const payload = { [collationKey]: amount.toString() };
|
|
438
430
|
const hashData = this.serializer.package(payload, symbolNames);
|
|
439
431
|
const targetId = Object.keys(hashData)[0];
|
package/build/types/hotmesh.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ILogger } from
|
|
2
|
-
import { HotMeshService } from
|
|
3
|
-
import { HookRules } from
|
|
4
|
-
import { RedisClass, RedisClient, RedisOptions } from
|
|
5
|
-
import { StreamData, StreamDataResponse } from
|
|
1
|
+
import { ILogger } from '../services/logger';
|
|
2
|
+
import { HotMeshService } from '../services/hotmesh';
|
|
3
|
+
import { HookRules } from './hook';
|
|
4
|
+
import { RedisClass, RedisClient, RedisOptions } from './redis';
|
|
5
|
+
import { StreamData, StreamDataResponse } from './stream';
|
|
6
6
|
type HotMesh = typeof HotMeshService;
|
|
7
7
|
type RedisConfig = {
|
|
8
8
|
class: RedisClass;
|
package/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
|
},
|
|
@@ -142,7 +142,8 @@ class Activity {
|
|
|
142
142
|
const durationInSeconds = Pipe.resolve(this.config.sleep, this.context);
|
|
143
143
|
const jobId = this.context.metadata.jid;
|
|
144
144
|
const activityId = this.metadata.aid;
|
|
145
|
-
|
|
145
|
+
const dId = this.metadata.dad;
|
|
146
|
+
await this.engine.task.registerTimeHook(jobId, `${activityId}${dId||''}`, 'sleep', durationInSeconds);
|
|
146
147
|
return jobId;
|
|
147
148
|
}
|
|
148
149
|
}
|
|
@@ -206,6 +207,7 @@ class Activity {
|
|
|
206
207
|
telemetry.setActivityAttributes(attrs);
|
|
207
208
|
return jobStatus as number;
|
|
208
209
|
} catch (error) {
|
|
210
|
+
console.error('this error?', error);
|
|
209
211
|
this.logger.error('engine-process-hook-event-error', error);
|
|
210
212
|
telemetry.setActivityError(error.message);
|
|
211
213
|
throw error;
|
|
@@ -278,12 +280,11 @@ class Activity {
|
|
|
278
280
|
}
|
|
279
281
|
|
|
280
282
|
bindDimensionalAddress(state: StringAnyType) {
|
|
281
|
-
const
|
|
282
|
-
state[`${aid}/output/metadata/dad`] = dad;
|
|
283
|
+
const dad = this.resolveDad();
|
|
284
|
+
state[`${this.metadata.aid}/output/metadata/dad`] = dad;
|
|
283
285
|
}
|
|
284
286
|
|
|
285
287
|
async setState(multi?: RedisMulti): Promise<string> {
|
|
286
|
-
const { id: appId } = await this.engine.getVID();
|
|
287
288
|
const jobId = this.context.metadata.jid;
|
|
288
289
|
this.bindJobMetadata();
|
|
289
290
|
this.bindActivityMetadata();
|
|
@@ -298,7 +299,8 @@ class Activity {
|
|
|
298
299
|
this.metadata.aid,
|
|
299
300
|
...presets
|
|
300
301
|
];
|
|
301
|
-
|
|
302
|
+
const dIds = CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], this.resolveDad());
|
|
303
|
+
return await this.store.setState(state, this.getJobStatus(), jobId, symbolNames, dIds, multi);
|
|
302
304
|
}
|
|
303
305
|
|
|
304
306
|
bindJobMetadata(): void {
|
|
@@ -314,6 +316,7 @@ class Activity {
|
|
|
314
316
|
if (this.status === StreamStatus.ERROR) {
|
|
315
317
|
self.output.metadata.err = JSON.stringify(this.data);
|
|
316
318
|
}
|
|
319
|
+
//todo: verify leg2 never overwrites leg1 `ac`
|
|
317
320
|
self.output.metadata.ac =
|
|
318
321
|
self.output.metadata.au = formatISODate(new Date());
|
|
319
322
|
self.output.metadata.atp = this.config.type;
|
|
@@ -386,10 +389,11 @@ class Activity {
|
|
|
386
389
|
}
|
|
387
390
|
}
|
|
388
391
|
TelemetryService.addTargetTelemetryPaths(consumes, this.config, this.metadata, this.leg);
|
|
389
|
-
|
|
392
|
+
let { dad, jid } = this.context.metadata;
|
|
390
393
|
jobId = jobId || jid;
|
|
391
394
|
//`state` is a flat hash
|
|
392
|
-
const
|
|
395
|
+
const dIds = CollatorService.getDimensionsById([...this.config.ancestors, this.metadata.aid], dad);
|
|
396
|
+
const [state, status] = await this.store.getState(jobId, consumes, dIds);
|
|
393
397
|
//`context` is a tree
|
|
394
398
|
this.context = restoreHierarchy(state) as JobState;
|
|
395
399
|
this.initDimensionalAddress(dad);
|
|
@@ -429,11 +433,26 @@ class Activity {
|
|
|
429
433
|
this.context[this.metadata.aid][type].data = this.data;
|
|
430
434
|
}
|
|
431
435
|
|
|
436
|
+
resolveDad(): string {
|
|
437
|
+
let dad = this.metadata.dad;
|
|
438
|
+
if (this.adjacentIndex > 0) {
|
|
439
|
+
//if adjacent index > 0 the activity is cycling; replace last index with cycle index
|
|
440
|
+
dad = `${dad.substring(0, dad.lastIndexOf(','))},${this.adjacentIndex}`
|
|
441
|
+
}
|
|
442
|
+
return dad;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
resolveAdjacentDad(): string {
|
|
446
|
+
//concat self and child dimension (all children (leg 1) begin life at 0)
|
|
447
|
+
return `${this.resolveDad()}${DimensionService.getSeed(0)}`;
|
|
448
|
+
};
|
|
449
|
+
|
|
432
450
|
async filterAdjacent(): Promise<StreamData[]> {
|
|
433
451
|
const adjacencyList: StreamData[] = [];
|
|
434
452
|
const transitions = await this.store.getTransitions(await this.engine.getVID());
|
|
435
453
|
const transition = transitions[`.${this.metadata.aid}`];
|
|
436
|
-
|
|
454
|
+
//resolve the dimensional address for adjacent children
|
|
455
|
+
const adjacentDad = this.resolveAdjacentDad();
|
|
437
456
|
if (transition) {
|
|
438
457
|
for (const toActivityId in transition) {
|
|
439
458
|
const transitionRule: boolean | TransitionRule = transition[toActivityId];
|
|
@@ -441,7 +460,7 @@ class Activity {
|
|
|
441
460
|
adjacencyList.push({
|
|
442
461
|
metadata: {
|
|
443
462
|
jid: this.context.metadata.jid,
|
|
444
|
-
dad:
|
|
463
|
+
dad: adjacentDad,
|
|
445
464
|
aid: toActivityId,
|
|
446
465
|
spn: this.context['$self'].output.metadata?.l2s,
|
|
447
466
|
trc: this.context.metadata.trc,
|
|
@@ -62,6 +62,7 @@ class Worker extends Activity {
|
|
|
62
62
|
if (error instanceof GetStateError) {
|
|
63
63
|
this.logger.error('worker-get-state-error', error);
|
|
64
64
|
} else {
|
|
65
|
+
console.error(error);
|
|
65
66
|
this.logger.error('worker-process-error', error);
|
|
66
67
|
}
|
|
67
68
|
telemetry.setActivityError(error.message);
|
|
@@ -1,18 +1,28 @@
|
|
|
1
|
-
import { CollationError } from
|
|
2
|
-
import { RedisMulti } from
|
|
3
|
-
import { CollationFaultType, CollationStage } from
|
|
4
|
-
import { ActivityDuplex } from
|
|
5
|
-
import { HotMeshGraph } from
|
|
6
|
-
import { Activity } from
|
|
1
|
+
import { CollationError } from '../../modules/errors';
|
|
2
|
+
import { RedisMulti } from '../../types/redis';
|
|
3
|
+
import { CollationFaultType, CollationStage } from '../../types/collator';
|
|
4
|
+
import { ActivityDuplex } from '../../types/activity';
|
|
5
|
+
import { HotMeshGraph } from '../../types/hotmesh';
|
|
6
|
+
import { Activity } from '../activities/activity';
|
|
7
7
|
|
|
8
8
|
class CollatorService {
|
|
9
9
|
|
|
10
10
|
//max int digit count that supports `hincrby`
|
|
11
|
-
static targetLength = 15;
|
|
11
|
+
static targetLength = 15;
|
|
12
|
+
|
|
13
|
+
static getDimensionalAddress(activity: Activity): Record<string, string> {
|
|
14
|
+
let dad = activity.context.metadata.dad || activity.metadata.dad;
|
|
15
|
+
//todo: unsure about this reset
|
|
16
|
+
// if (dad && activity.leg === 2) {
|
|
17
|
+
// console.log('setting dad index back to 0=>', dad);
|
|
18
|
+
// dad = `${dad.substring(0, dad.lastIndexOf(','))},0`;
|
|
19
|
+
// }
|
|
20
|
+
return CollatorService.getDimensionsById([...activity.config.ancestors, activity.metadata.aid], dad);
|
|
21
|
+
}
|
|
12
22
|
|
|
13
23
|
static async notarizeEntry(activity: Activity, multi?: RedisMulti): Promise<number> {
|
|
14
24
|
//decrement by -100_000_000_000_000
|
|
15
|
-
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, -100_000_000_000_000, multi);
|
|
25
|
+
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, -100_000_000_000_000, this.getDimensionalAddress(activity), multi);
|
|
16
26
|
this.verifyInteger(amount, 1, 'enter');
|
|
17
27
|
return amount;
|
|
18
28
|
};
|
|
@@ -20,31 +30,31 @@ class CollatorService {
|
|
|
20
30
|
static async authorizeReentry(activity: Activity, multi?: RedisMulti): Promise<number> {
|
|
21
31
|
//set second digit to 8, allowing for re-entry
|
|
22
32
|
//decrement by -10_000_000_000_000
|
|
23
|
-
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, -10_000_000_000_000, multi);
|
|
33
|
+
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, -10_000_000_000_000, this.getDimensionalAddress(activity), multi);
|
|
24
34
|
//this.verifyInteger(amount, 1, 'exit');
|
|
25
35
|
return amount;
|
|
26
36
|
}
|
|
27
37
|
|
|
28
38
|
static async notarizeEarlyCompletion(activity: Activity, multi?: RedisMulti): Promise<number> {
|
|
29
39
|
//initialize both `possible` (1m) and `actualized` (1) zero dimension, while decrementing the 2nd and 3rd digits to deactivate the activity
|
|
30
|
-
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1_000_001 - 11_000_000_000_000, multi);
|
|
40
|
+
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1_000_001 - 11_000_000_000_000, this.getDimensionalAddress(activity), multi);
|
|
31
41
|
};
|
|
32
42
|
|
|
33
43
|
static async notarizeReentry(activity: Activity, multi?: RedisMulti): Promise<number> {
|
|
34
44
|
//increment by 1_000_000 (indicates re-entry and is used to drive the 'dimensional address' for adjacent activities (minus 1))
|
|
35
|
-
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1_000_000, multi);
|
|
45
|
+
const amount = await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1_000_000, this.getDimensionalAddress(activity), multi);
|
|
36
46
|
this.verifyInteger(amount, 2, 'enter');
|
|
37
47
|
return amount;
|
|
38
48
|
};
|
|
39
49
|
|
|
40
50
|
static async notarizeContinuation(activity: Activity, multi?: RedisMulti): Promise<number> {
|
|
41
51
|
//keep open; actualize the leg2 dimension (+1)
|
|
42
|
-
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1, multi);
|
|
52
|
+
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1, this.getDimensionalAddress(activity), multi);
|
|
43
53
|
};
|
|
44
54
|
|
|
45
55
|
static async notarizeCompletion(activity: Activity, multi?: RedisMulti): Promise<number> {
|
|
46
56
|
//close out; actualize leg2 dimension (+1) and decrement the 3rd digit (-1_000_000_000_000)
|
|
47
|
-
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1 - 1_000_000_000_000, multi);
|
|
57
|
+
return await activity.store.collate(activity.context.metadata.jid, activity.metadata.aid, 1 - 1_000_000_000_000, this.getDimensionalAddress(activity), multi);
|
|
48
58
|
};
|
|
49
59
|
|
|
50
60
|
static getDigitAtIndex(num: number, targetDigitIndex: number): number | null {
|
|
@@ -112,6 +122,21 @@ class CollatorService {
|
|
|
112
122
|
}
|
|
113
123
|
}
|
|
114
124
|
|
|
125
|
+
static getDimensionsById(ancestors: string[], dad: string): Record<string, string> {
|
|
126
|
+
//ancestors is an ordered list of all ancestors, starting with the trigger (['t1', 'a1', 'a2'])
|
|
127
|
+
//dad is the dimensional address of the ancestors list (',0,5,3')
|
|
128
|
+
//loop through the ancestors list and create a map of the ancestor to the dimensional address.
|
|
129
|
+
//return { 't1': ',0', 'a1': ',0,5', 'a1': ',0,5,3', $ADJACENT: ',0,5,3,0' };
|
|
130
|
+
// `adjacent` is a special key that is used to track the dimensional address of adjacent activities
|
|
131
|
+
const map: Record<string, string> = { '$ADJACENT': `${dad},0` };
|
|
132
|
+
let dadStr = dad;
|
|
133
|
+
ancestors.reverse().forEach((ancestor) => {
|
|
134
|
+
map[ancestor] = dadStr;
|
|
135
|
+
dadStr = dadStr.substring(0, dadStr.lastIndexOf(','));
|
|
136
|
+
});
|
|
137
|
+
return map;
|
|
138
|
+
}
|
|
139
|
+
|
|
115
140
|
/**
|
|
116
141
|
* All non-trigger activities are assigned a status seed by their parent
|
|
117
142
|
*/
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import { KeyStoreParams, KeyType } from
|
|
2
|
-
import { getSymKey } from
|
|
3
|
-
import { CollatorService } from
|
|
4
|
-
import { SerializerService } from
|
|
1
|
+
import { KeyStoreParams, KeyType } from '../../modules/key';
|
|
2
|
+
import { getSymKey } from '../../modules/utils';
|
|
3
|
+
import { CollatorService } from '../collator';
|
|
4
|
+
import { SerializerService } from '../serializer';
|
|
5
5
|
import { StoreService } from '../store';
|
|
6
|
-
import { ActivityType } from
|
|
7
|
-
import { HookRule } from
|
|
8
|
-
import { HotMeshGraph, HotMeshManifest } from
|
|
9
|
-
import { RedisClient, RedisMulti } from
|
|
10
|
-
import { StringAnyType, Symbols } from
|
|
6
|
+
import { ActivityType } from '../../types/activity';
|
|
7
|
+
import { HookRule } from '../../types/hook';
|
|
8
|
+
import { HotMeshGraph, HotMeshManifest } from '../../types/hotmesh';
|
|
9
|
+
import { RedisClient, RedisMulti } from '../../types/redis';
|
|
10
|
+
import { StringAnyType, Symbols } from '../../types/serializer';
|
|
11
11
|
|
|
12
12
|
const DEFAULT_METADATA_RANGE_SIZE = 26; //metadata is 26 slots ([a-z] * 1)
|
|
13
13
|
const DEFAULT_DATA_RANGE_SIZE = 260; //data is 260 slots ([a-zA-Z] * 5)
|
|
@@ -63,6 +63,7 @@ class Deployer {
|
|
|
63
63
|
for (const [activityId, activity] of Object.entries(graph.activities)) {
|
|
64
64
|
const [lower, upper, symbols] = await this.store.reserveSymbolRange(activityId, DEFAULT_RANGE_SIZE, 'ACTIVITY');
|
|
65
65
|
const prefix = `${activityId}/`; //activity meta/data is namespaced
|
|
66
|
+
this.bindSelf(activity.consumes, activity.produces, activityId);
|
|
66
67
|
const newSymbols = this.bindSymbols(lower, upper, symbols, prefix, activity.produces);
|
|
67
68
|
if (Object.keys(newSymbols).length) {
|
|
68
69
|
await this.store.addSymbols(activityId, newSymbols);
|
|
@@ -71,6 +72,20 @@ class Deployer {
|
|
|
71
72
|
}
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
bindSelf(consumes: Record<string, string[]>, produces: string[], activityId: string) {
|
|
76
|
+
//bind self-referential mappings
|
|
77
|
+
for (const selfId of [activityId, '$self']) {
|
|
78
|
+
const selfConsumes = consumes[selfId];
|
|
79
|
+
if (selfConsumes) {
|
|
80
|
+
for (const path of selfConsumes) {
|
|
81
|
+
if (!produces.includes(path)) {
|
|
82
|
+
produces.push(path);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
74
89
|
bindSymbols(startIndex: number, maxIndex: number, existingSymbols: Symbols, prefix: string, produces: string[]): Symbols {
|
|
75
90
|
let newSymbols: Symbols = {};
|
|
76
91
|
let currentSymbols: Symbols = {...existingSymbols};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { WorkflowHandleService } from
|
|
2
|
-
import { HotMeshService as HotMesh } from
|
|
3
|
-
import { ClientConfig, Connection, WorkflowOptions } from
|
|
4
|
-
import { getWorkflowYAML } from
|
|
5
|
-
import { JobState } from
|
|
1
|
+
import { WorkflowHandleService } from './handle';
|
|
2
|
+
import { HotMeshService as HotMesh } from '../hotmesh';
|
|
3
|
+
import { ClientConfig, Connection, WorkflowOptions } from '../../types/durable';
|
|
4
|
+
import { getWorkflowYAML } from './factory';
|
|
5
|
+
import { JobState } from '../../types/job';
|
|
6
6
|
|
|
7
7
|
/*
|
|
8
8
|
Here is an example of how the methods in this file are used:
|
|
@@ -101,7 +101,14 @@ export class ClientService {
|
|
|
101
101
|
await hotMesh.deploy(getWorkflowYAML(workflowTopic, version));
|
|
102
102
|
await hotMesh.activate(version);
|
|
103
103
|
} catch (err) {
|
|
104
|
-
hotMesh.engine.logger.error('durable-client-
|
|
104
|
+
hotMesh.engine.logger.error('durable-client-deploy-activate-err', err);
|
|
105
|
+
throw err;
|
|
106
|
+
}
|
|
107
|
+
} else if(app && !app.active) {
|
|
108
|
+
try {
|
|
109
|
+
await hotMesh.activate(version);
|
|
110
|
+
} catch (err) {
|
|
111
|
+
hotMesh.engine.logger.error('durable-client-activate-err', err);
|
|
105
112
|
throw err;
|
|
106
113
|
}
|
|
107
114
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { JobOutput } from
|
|
2
|
-
import { HotMeshService as HotMesh } from
|
|
1
|
+
import { JobOutput } from '../../types/job';
|
|
2
|
+
import { HotMeshService as HotMesh } from '../hotmesh';
|
|
3
3
|
|
|
4
4
|
export class WorkflowHandleService {
|
|
5
5
|
hotMesh: HotMesh;
|
|
@@ -67,7 +67,14 @@ export class WorkerService {
|
|
|
67
67
|
await hotMesh.deploy(factory(topic, version));
|
|
68
68
|
await hotMesh.activate(version);
|
|
69
69
|
} catch (err) {
|
|
70
|
-
hotMesh.engine.logger.error('durable-worker-
|
|
70
|
+
hotMesh.engine.logger.error('durable-worker-deploy-activate-err', err);
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
} else if(app && !app.active) {
|
|
74
|
+
try {
|
|
75
|
+
await hotMesh.activate(version);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
hotMesh.engine.logger.error('durable-worker-activate-err', err);
|
|
71
78
|
throw err;
|
|
72
79
|
}
|
|
73
80
|
}
|
|
@@ -176,7 +183,14 @@ export class WorkerService {
|
|
|
176
183
|
await hotMesh.deploy(getActivityYAML(activityTopic, version));
|
|
177
184
|
await hotMesh.activate(version);
|
|
178
185
|
} catch (err) {
|
|
179
|
-
console.log('durable-worker-activity-
|
|
186
|
+
console.log('durable-worker-activity-deploy-activate-error', err);
|
|
187
|
+
throw err;
|
|
188
|
+
}
|
|
189
|
+
} else if(app && !app.active) {
|
|
190
|
+
try {
|
|
191
|
+
await hotMesh.activate(version);
|
|
192
|
+
} catch (err) {
|
|
193
|
+
hotMesh.engine.logger.error('durable-worker-activity-activate-err', err);
|
|
180
194
|
throw err;
|
|
181
195
|
}
|
|
182
196
|
}
|