@hotmeshio/hotmesh 0.0.38 → 0.0.39
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 +1 -1
- 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 +1 -1
- 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/build/types/index.d.ts
CHANGED
|
@@ -10,12 +10,13 @@ export { ILogger } from './logger';
|
|
|
10
10
|
export { JobData, JobsData, JobMetadata, JobOutput, JobState, JobStatus, PartialJobState } from './job';
|
|
11
11
|
export { MappingStatements } from './map';
|
|
12
12
|
export { Pipe, PipeItem, PipeItems } from './pipe';
|
|
13
|
-
export { HotMesh, HotMeshApp, HotMeshApps, HotMeshConfig, HotMeshEngine, RedisConfig, HotMeshGraph, HotMeshManifest, HotMeshSettings, HotMeshWorker } from './hotmesh';
|
|
14
|
-
export { ActivateMessage, JobMessage, JobMessageCallback, PingMessage, PongMessage, QuorumMessage, SubscriptionCallback, ThrottleMessage, WorkMessage } from './quorum';
|
|
13
|
+
export { HotMesh, HotMeshApp, HotMeshApps, HotMeshConfig, HotMeshEngine, RedisConfig, HotMeshGraph, HotMeshManifest, HotMeshSettings, HotMeshWorker, KeyStoreParams, KeyType } from './hotmesh';
|
|
14
|
+
export { ActivateMessage, JobMessage, JobMessageCallback, PingMessage, PongMessage, QuorumMessage, QuorumMessageCallback, QuorumProfile, SubscriptionCallback, ThrottleMessage, WorkMessage } from './quorum';
|
|
15
15
|
export { MultiResponseFlags, RedisClient, RedisMulti } from './redis';
|
|
16
16
|
export { RedisClientType, RedisMultiType } from './redisclient';
|
|
17
17
|
export { JSONSchema, StringAnyType, StringScalarType, StringStringType, SymbolMap, SymbolMaps, SymbolRanges, Symbols, SymbolSets } from './serializer';
|
|
18
18
|
export { AggregatedData, CountByFacet, GetStatsOptions, IdsData, Measure, MeasureIds, MetricTypes, StatType, StatsType, IdsResponse, JobStats, JobStatsInput, JobStatsRange, StatsResponse, Segment, TimeSegment } from './stats';
|
|
19
19
|
export { ReclaimedMessageType, StreamCode, StreamConfig, StreamData, StreamDataType, StreamError, StreamDataResponse, StreamRetryPolicy, StreamRole, StreamStatus } from './stream';
|
|
20
20
|
export { context, Context, Counter, Meter, metrics, propagation, SpanContext, Span, SpanStatus, SpanStatusCode, SpanKind, trace, Tracer, ValueType } from './telemetry';
|
|
21
|
+
export { WorkListTaskType } from './task';
|
|
21
22
|
export { TransitionMatch, TransitionRule, Transitions } from './transition';
|
package/build/types/index.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ValueType = exports.trace = exports.SpanKind = exports.SpanStatusCode = exports.propagation = exports.metrics = exports.context = exports.StreamStatus = exports.StreamRole = exports.StreamDataType = exports.IORedisClientType = exports.HookGate = exports.CollationFaultType = void 0;
|
|
3
|
+
exports.ValueType = exports.trace = exports.SpanKind = exports.SpanStatusCode = exports.propagation = exports.metrics = exports.context = exports.StreamStatus = exports.StreamRole = exports.StreamDataType = exports.KeyType = exports.IORedisClientType = exports.HookGate = exports.CollationFaultType = void 0;
|
|
4
4
|
var collator_1 = require("./collator");
|
|
5
5
|
Object.defineProperty(exports, "CollationFaultType", { enumerable: true, get: function () { return collator_1.CollationFaultType; } });
|
|
6
6
|
var hook_1 = require("./hook");
|
|
7
7
|
Object.defineProperty(exports, "HookGate", { enumerable: true, get: function () { return hook_1.HookGate; } });
|
|
8
8
|
var ioredisclient_1 = require("./ioredisclient");
|
|
9
9
|
Object.defineProperty(exports, "IORedisClientType", { enumerable: true, get: function () { return ioredisclient_1.RedisClientType; } });
|
|
10
|
+
var hotmesh_1 = require("./hotmesh");
|
|
11
|
+
Object.defineProperty(exports, "KeyType", { enumerable: true, get: function () { return hotmesh_1.KeyType; } });
|
|
10
12
|
var stream_1 = require("./stream");
|
|
11
13
|
Object.defineProperty(exports, "StreamDataType", { enumerable: true, get: function () { return stream_1.StreamDataType; } });
|
|
12
14
|
Object.defineProperty(exports, "StreamRole", { enumerable: true, get: function () { return stream_1.StreamRole; } });
|
package/build/types/logger.d.ts
CHANGED
package/build/types/logger.js
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type WorkListTaskType = 'sleep' | 'expire' | 'interrupt' | 'delist';
|
package/modules/enums.ts
CHANGED
|
@@ -1,35 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export const
|
|
10
|
-
export const
|
|
11
|
-
export const
|
|
12
|
-
|
|
13
|
-
export const
|
|
14
|
-
|
|
15
|
-
export const
|
|
16
|
-
|
|
17
|
-
export const
|
|
18
|
-
export const
|
|
19
|
-
export const
|
|
20
|
-
|
|
21
|
-
export const
|
|
22
|
-
|
|
23
|
-
export const
|
|
24
|
-
|
|
25
|
-
//
|
|
26
|
-
export const
|
|
27
|
-
export const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
export const
|
|
33
|
-
|
|
34
|
-
//
|
|
35
|
-
|
|
1
|
+
import { LogLevel } from "../types/logger";
|
|
2
|
+
|
|
3
|
+
// HOTMESH SYSTEM
|
|
4
|
+
export const HMSH_LOGLEVEL = process.env.HMSH_LOGLEVEL as LogLevel || 'info';
|
|
5
|
+
|
|
6
|
+
// STATUS CODES AND MESSAGES
|
|
7
|
+
export const HMSH_CODE_SUCCESS = 200;
|
|
8
|
+
export const HMSH_CODE_PENDING = 202;
|
|
9
|
+
export const HMSH_CODE_NOTFOUND = 404;
|
|
10
|
+
export const HMSH_CODE_INTERRUPT = 410;
|
|
11
|
+
export const HMSH_CODE_UNKNOWN = 500;
|
|
12
|
+
export const HMSH_CODE_TIMEOUT = 504;
|
|
13
|
+
export const HMSH_CODE_UNACKED = 999;
|
|
14
|
+
|
|
15
|
+
export const HMSH_CODE_DURABLE_SLEEPFOR = 592;
|
|
16
|
+
export const HMSH_CODE_DURABLE_INCOMPLETE = 593;
|
|
17
|
+
export const HMSH_CODE_DURABLE_WAITFOR = 594;
|
|
18
|
+
export const HMSH_CODE_DURABLE_TIMEOUT = 596;
|
|
19
|
+
export const HMSH_CODE_DURABLE_MAXED = 597;
|
|
20
|
+
export const HMSH_CODE_DURABLE_FATAL = 598;
|
|
21
|
+
export const HMSH_CODE_DURABLE_RETRYABLE = 599;
|
|
22
|
+
|
|
23
|
+
export const HMSH_STATUS_UNKNOWN = 'unknown';
|
|
24
|
+
|
|
25
|
+
// ENGINE
|
|
26
|
+
export const HMSH_OTT_WAIT_TIME = parseInt(process.env.HMSH_OTT_WAIT_TIME, 10) || 1000;
|
|
27
|
+
export const HMSH_EXPIRE_JOB_SECONDS = parseInt(process.env.HMSH_EXPIRE_JOB_SECONDS, 10) || 1;
|
|
28
|
+
|
|
29
|
+
// STREAM ROUTER
|
|
30
|
+
export const HMSH_MAX_RETRIES = parseInt(process.env.HMSH_MAX_RETRIES, 10) || 3;
|
|
31
|
+
export const HMSH_MAX_TIMEOUT_MS = parseInt(process.env.HMSH_MAX_TIMEOUT_MS, 10) || 60000;
|
|
32
|
+
export const HMSH_GRADUATED_INTERVAL_MS = parseInt(process.env.HMSH_GRADUATED_INTERVAL_MS, 10) || 5000;
|
|
33
|
+
|
|
34
|
+
const BASE_BLOCK_DURATION = 10000; // Modified for clarity
|
|
35
|
+
const TEST_BLOCK_DURATION = 1000; // Modified for clarity
|
|
36
|
+
export const 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);
|
|
37
|
+
|
|
38
|
+
export const HMSH_XCLAIM_DELAY_MS = parseInt(process.env.HMSH_XCLAIM_DELAY_MS, 10) || 1000 * 60;
|
|
39
|
+
export const HMSH_XCLAIM_COUNT = parseInt(process.env.HMSH_XCLAIM_COUNT, 10) || 3;
|
|
40
|
+
export const HMSH_XPENDING_COUNT = parseInt(process.env.HMSH_XPENDING_COUNT, 10) || 10;
|
|
41
|
+
|
|
42
|
+
// TASK WORKER
|
|
43
|
+
export const HMSH_EXPIRE_DURATION = parseInt(process.env.HMSH_EXPIRE_DURATION, 10) || 1;
|
|
44
|
+
|
|
45
|
+
const BASE_FIDELITY_SECONDS = 5;
|
|
46
|
+
const TEST_FIDELITY_SECONDS = 5;
|
|
47
|
+
export const 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);
|
|
48
|
+
|
|
49
|
+
export const HMSH_SCOUT_INTERVAL_SECONDS = parseInt(process.env.HMSH_SCOUT_INTERVAL_SECONDS, 10) || 60;
|
package/modules/errors.ts
CHANGED
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { ActivityDuplex } from "../types/activity";
|
|
2
2
|
import { CollationFaultType, CollationStage } from "../types/collator";
|
|
3
|
+
import {
|
|
4
|
+
HMSH_CODE_DURABLE_MAXED,
|
|
5
|
+
HMSH_CODE_DURABLE_TIMEOUT,
|
|
6
|
+
HMSH_CODE_DURABLE_FATAL,
|
|
7
|
+
HMSH_CODE_DURABLE_INCOMPLETE,
|
|
8
|
+
HMSH_CODE_NOTFOUND,
|
|
9
|
+
HMSH_CODE_DURABLE_RETRYABLE,
|
|
10
|
+
HMSH_CODE_DURABLE_SLEEPFOR,
|
|
11
|
+
HMSH_CODE_DURABLE_WAITFOR } from "./enums";
|
|
3
12
|
|
|
4
13
|
class GetStateError extends Error {
|
|
5
14
|
jobId: string;
|
|
6
|
-
code
|
|
15
|
+
code = HMSH_CODE_NOTFOUND;
|
|
7
16
|
constructor(jobId: string) {
|
|
8
17
|
super(`${jobId} Not Found`);
|
|
9
18
|
this.jobId = jobId;
|
|
@@ -21,7 +30,7 @@ class DurableIncompleteSignalError extends Error {
|
|
|
21
30
|
code: number;
|
|
22
31
|
constructor(message: string) {
|
|
23
32
|
super(message);
|
|
24
|
-
this.code =
|
|
33
|
+
this.code = HMSH_CODE_DURABLE_INCOMPLETE;
|
|
25
34
|
}
|
|
26
35
|
}
|
|
27
36
|
|
|
@@ -32,7 +41,7 @@ class DurableWaitForSignalError extends Error {
|
|
|
32
41
|
constructor(message: string, signals: {signal: string, index: number}[]) {
|
|
33
42
|
super(message);
|
|
34
43
|
this.signals = signals;
|
|
35
|
-
this.code =
|
|
44
|
+
this.code = HMSH_CODE_DURABLE_WAITFOR;
|
|
36
45
|
}
|
|
37
46
|
}
|
|
38
47
|
|
|
@@ -60,35 +69,35 @@ class DurableSleepForError extends Error {
|
|
|
60
69
|
this.duration = duration;
|
|
61
70
|
this.index = index;
|
|
62
71
|
this.dimension = dimension;
|
|
63
|
-
this.code =
|
|
72
|
+
this.code = HMSH_CODE_DURABLE_SLEEPFOR;
|
|
64
73
|
}
|
|
65
74
|
}
|
|
66
75
|
class DurableTimeoutError extends Error {
|
|
67
76
|
code: number;
|
|
68
77
|
constructor(message: string) {
|
|
69
78
|
super(message);
|
|
70
|
-
this.code =
|
|
79
|
+
this.code = HMSH_CODE_DURABLE_TIMEOUT;
|
|
71
80
|
}
|
|
72
81
|
}
|
|
73
82
|
class DurableMaxedError extends Error {
|
|
74
83
|
code: number;
|
|
75
84
|
constructor(message: string) {
|
|
76
85
|
super(message);
|
|
77
|
-
this.code =
|
|
86
|
+
this.code = HMSH_CODE_DURABLE_MAXED;
|
|
78
87
|
}
|
|
79
88
|
}
|
|
80
89
|
class DurableFatalError extends Error {
|
|
81
90
|
code: number;
|
|
82
91
|
constructor(message: string) {
|
|
83
92
|
super(message);
|
|
84
|
-
this.code =
|
|
93
|
+
this.code = HMSH_CODE_DURABLE_FATAL;
|
|
85
94
|
}
|
|
86
95
|
}
|
|
87
96
|
class DurableRetryError extends Error {
|
|
88
97
|
code: number;
|
|
89
98
|
constructor(message: string) {
|
|
90
99
|
super(message);
|
|
91
|
-
this.code =
|
|
100
|
+
this.code = HMSH_CODE_DURABLE_RETRYABLE;
|
|
92
101
|
}
|
|
93
102
|
}
|
|
94
103
|
|
package/modules/key.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { KeyStoreParams, KeyType } from '../types/hotmesh';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Keys
|
|
3
5
|
*
|
|
@@ -26,46 +28,7 @@
|
|
|
26
28
|
* hmsh:<appid>:sym:vals: -> {hash} list of symbols for job values across all app versions
|
|
27
29
|
*/
|
|
28
30
|
|
|
29
|
-
|
|
30
|
-
const HMNS = "hmsh";
|
|
31
|
-
|
|
32
|
-
//these are the entity types that are stored in the key/value store
|
|
33
|
-
enum KeyType {
|
|
34
|
-
APP = 'APP',
|
|
35
|
-
ENGINE_ID = 'ENGINE',
|
|
36
|
-
HOOKS = 'HOOKS',
|
|
37
|
-
JOB_DEPENDENTS = 'JOB_DEPENDENTS',
|
|
38
|
-
JOB_STATE = 'JOB_STATE',
|
|
39
|
-
JOB_STATS_GENERAL = 'JOB_STATS_GENERAL',
|
|
40
|
-
JOB_STATS_MEDIAN = 'JOB_STATS_MEDIAN',
|
|
41
|
-
JOB_STATS_INDEX = 'JOB_STATS_INDEX',
|
|
42
|
-
HOTMESH = 'HOTMESH',
|
|
43
|
-
QUORUM = 'QUORUM',
|
|
44
|
-
SCHEMAS = 'SCHEMAS',
|
|
45
|
-
SIGNALS = 'SIGNALS',
|
|
46
|
-
STREAMS = 'STREAMS',
|
|
47
|
-
SUBSCRIPTIONS = 'SUBSCRIPTIONS',
|
|
48
|
-
SUBSCRIPTION_PATTERNS = 'SUBSCRIPTION_PATTERNS',
|
|
49
|
-
SYMKEYS = 'SYMKEYS',
|
|
50
|
-
SYMVALS = 'SYMVALS',
|
|
51
|
-
TIME_RANGE = 'TIME_RANGE',
|
|
52
|
-
WORK_ITEMS = 'WORK_ITEMS',
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
//when minting a key, the following parameters are used to create a unique key per entity
|
|
56
|
-
type KeyStoreParams = {
|
|
57
|
-
appId?: string; //app id is a uuid for a hotmesh app
|
|
58
|
-
engineId?: string; //unique auto-generated guid for an ephemeral engine instance
|
|
59
|
-
appVersion?: string; //(e.g. "1.0.0", "1", "1.0")
|
|
60
|
-
jobId?: string; //a customer-defined id for job; must be unique for the entire app
|
|
61
|
-
activityId?: string; //activity id is a uuid for a given hotmesh app
|
|
62
|
-
jobKey?: string; //a customer-defined label for a job that serves to categorize events
|
|
63
|
-
dateTime?: string; //UTC date time: YYYY-MM-DDTHH:MM (20203-04-12T00:00); serves as a time-series bucket for the job_key
|
|
64
|
-
facet?: string; //data path starting at root with values separated by colons (e.g. "object/type:bar")
|
|
65
|
-
topic?: string; //topic name (e.g., "foo" or "" for top-level)
|
|
66
|
-
timeValue?: number; //time value (rounded to minute) (for delete range)
|
|
67
|
-
scoutType?: 'signal' | 'time'; //a single member of the quorum serves as the 'scout' for the group, triaging tasks for the collective
|
|
68
|
-
};
|
|
31
|
+
const HMNS = "hmsh"; //default
|
|
69
32
|
|
|
70
33
|
class KeyService {
|
|
71
34
|
|
package/package.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HMSH_EXPIRE_DURATION } from '../../modules/enums';
|
|
2
2
|
import {
|
|
3
3
|
CollationError,
|
|
4
4
|
GenerationalError,
|
|
@@ -435,7 +435,7 @@ class Activity {
|
|
|
435
435
|
|
|
436
436
|
initPolicies(context: JobState) {
|
|
437
437
|
const expire = Pipe.resolve(
|
|
438
|
-
this.config.expire ??
|
|
438
|
+
this.config.expire ?? HMSH_EXPIRE_DURATION,
|
|
439
439
|
context
|
|
440
440
|
);
|
|
441
441
|
context.metadata.expire = expire;
|
|
@@ -131,16 +131,25 @@ class Hook extends Activity {
|
|
|
131
131
|
|
|
132
132
|
async registerHook(multi?: RedisMulti): Promise<string | void> {
|
|
133
133
|
if (this.config.hook?.topic) {
|
|
134
|
-
|
|
135
|
-
|
|
134
|
+
return await this.engine.taskService.registerWebHook(
|
|
135
|
+
this.config.hook.topic,
|
|
136
|
+
this.context,
|
|
137
|
+
this.resolveDad(),
|
|
138
|
+
multi
|
|
139
|
+
);
|
|
136
140
|
} else if (this.config.sleep) {
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
141
|
+
const duration = Pipe.resolve(
|
|
142
|
+
this.config.sleep,
|
|
143
|
+
this.context,
|
|
144
|
+
);
|
|
145
|
+
await this.engine.taskService.registerTimeHook(
|
|
146
|
+
this.context.metadata.jid,
|
|
147
|
+
this.context.metadata.gid,
|
|
148
|
+
`${this.metadata.aid}${this.metadata.dad || ''}`,
|
|
149
|
+
'sleep',
|
|
150
|
+
duration,
|
|
151
|
+
);
|
|
152
|
+
return this.context.metadata.jid;
|
|
144
153
|
}
|
|
145
154
|
}
|
|
146
155
|
|
|
@@ -47,7 +47,7 @@ class Trigger extends Activity {
|
|
|
47
47
|
const multi = this.store.getMulti();
|
|
48
48
|
await this.setState(multi);
|
|
49
49
|
await this.setStats(multi);
|
|
50
|
-
await this.
|
|
50
|
+
await this.registerJobDependency(multi);
|
|
51
51
|
await multi.exec();
|
|
52
52
|
|
|
53
53
|
telemetry.mapActivityAttributes();
|
|
@@ -180,13 +180,17 @@ class Trigger extends Activity {
|
|
|
180
180
|
}
|
|
181
181
|
|
|
182
182
|
/**
|
|
183
|
-
* Registers this job as a dependent of the parent job
|
|
183
|
+
* Registers this job as a dependent of the parent job; when the
|
|
184
|
+
* parent job is interrupted, this job will be interrupted
|
|
184
185
|
*/
|
|
185
|
-
async
|
|
186
|
-
const depKey = this.config.stats?.parent;
|
|
187
|
-
|
|
186
|
+
async registerJobDependency(multi?: RedisMulti): Promise<void> {
|
|
187
|
+
const depKey = this.config.stats?.parent ?? this.context.metadata.pj;
|
|
188
|
+
let resolvedDepKey = depKey ? Pipe.resolve(depKey, this.context) : '';
|
|
189
|
+
if (!resolvedDepKey) {
|
|
190
|
+
resolvedDepKey = this.context.metadata.pj;
|
|
191
|
+
}
|
|
188
192
|
if (resolvedDepKey) {
|
|
189
|
-
await this.store.
|
|
193
|
+
await this.store.registerJobDependency(
|
|
190
194
|
resolvedDepKey,
|
|
191
195
|
this.context.metadata.tpc,
|
|
192
196
|
this.context.metadata.jid,
|
|
@@ -11,11 +11,12 @@ import { JobState } from '../../types/job';
|
|
|
11
11
|
import { KeyService, KeyType } from '../../modules/key';
|
|
12
12
|
import { Search } from './search';
|
|
13
13
|
import { StreamStatus } from '../../types';
|
|
14
|
-
import {
|
|
14
|
+
import { HMSH_LOGLEVEL, HMSH_EXPIRE_JOB_SECONDS } from '../../modules/enums';
|
|
15
15
|
|
|
16
16
|
export class ClientService {
|
|
17
17
|
|
|
18
18
|
connection: Connection;
|
|
19
|
+
topics: string[] = [];
|
|
19
20
|
options: WorkflowOptions;
|
|
20
21
|
static instances = new Map<string, HotMesh | Promise<HotMesh>>();
|
|
21
22
|
|
|
@@ -23,14 +24,22 @@ export class ClientService {
|
|
|
23
24
|
this.connection = config.connection;
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
getHotMeshClient = async (
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
getHotMeshClient = async (workflowTopic: string, namespace?: string) => {
|
|
28
|
+
//use the cached instance
|
|
29
|
+
const instanceId = 'SINGLETON';
|
|
30
|
+
if (ClientService.instances.has(instanceId)) {
|
|
31
|
+
const hotMeshClient = await ClientService.instances.get(instanceId);
|
|
32
|
+
if (!this.topics.includes(workflowTopic)) {
|
|
33
|
+
this.topics.push(workflowTopic);
|
|
34
|
+
await this.createStream(hotMeshClient, workflowTopic, namespace);
|
|
35
|
+
}
|
|
36
|
+
return hotMeshClient;
|
|
30
37
|
}
|
|
31
38
|
|
|
39
|
+
//create and cache an instance
|
|
32
40
|
const hotMeshClient = HotMesh.init({
|
|
33
41
|
appId: namespace ?? APP_ID,
|
|
42
|
+
logLevel: HMSH_LOGLEVEL,
|
|
34
43
|
engine: {
|
|
35
44
|
redis: {
|
|
36
45
|
class: this.connection.class,
|
|
@@ -38,19 +47,27 @@ export class ClientService {
|
|
|
38
47
|
}
|
|
39
48
|
}
|
|
40
49
|
});
|
|
41
|
-
ClientService.instances.set(
|
|
50
|
+
ClientService.instances.set(instanceId, hotMeshClient);
|
|
51
|
+
await this.createStream(await hotMeshClient, workflowTopic, namespace);
|
|
52
|
+
await this.activateWorkflow(await hotMeshClient, namespace ?? APP_ID);
|
|
53
|
+
return hotMeshClient;
|
|
54
|
+
}
|
|
42
55
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Creates a stream (Redis `XGROUP.CREATE`) where events can be published (XADD).
|
|
58
|
+
* It is possible that the worker that will read from this stream channel
|
|
59
|
+
* has not yet been initialized, so this call ensures that the channel
|
|
60
|
+
* exists and is ready to serve as a container for events.
|
|
61
|
+
*/
|
|
62
|
+
createStream = async(hotMeshClient: HotMesh, workflowTopic: string, namespace?: string) => {
|
|
63
|
+
const store = hotMeshClient.engine.store;
|
|
64
|
+
const params = { appId: namespace ?? APP_ID, topic: workflowTopic };
|
|
46
65
|
const streamKey = store.mintKey(KeyType.STREAMS, params);
|
|
47
66
|
try {
|
|
48
67
|
await store.xgroup('CREATE', streamKey, 'WORKER', '$', 'MKSTREAM');
|
|
49
68
|
} catch (err) {
|
|
50
69
|
//ignore if already exists
|
|
51
70
|
}
|
|
52
|
-
await this.activateWorkflow(await hotMeshClient, namespace ?? APP_ID);
|
|
53
|
-
return hotMeshClient;
|
|
54
71
|
}
|
|
55
72
|
|
|
56
73
|
/**
|
|
@@ -105,7 +122,7 @@ export class ClientService {
|
|
|
105
122
|
const payload = {
|
|
106
123
|
arguments: [...options.args],
|
|
107
124
|
originJobId: options.originJobId,
|
|
108
|
-
expire: options.expire ??
|
|
125
|
+
expire: options.expire ?? HMSH_EXPIRE_JOB_SECONDS,
|
|
109
126
|
parentWorkflowId: options.parentWorkflowId,
|
|
110
127
|
workflowId: options.workflowId || HotMesh.guid(),
|
|
111
128
|
workflowTopic: workflowTopic,
|
|
@@ -155,9 +172,8 @@ export class ClientService {
|
|
|
155
172
|
if (options.search?.data) {
|
|
156
173
|
const searchSessionId = `-search-${HotMesh.guid()}-0`;
|
|
157
174
|
const search = new Search(options.workflowId, hotMeshClient, searchSessionId);
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
175
|
+
const entries = Object.entries(options.search.data).flat();
|
|
176
|
+
await search.set(...entries);
|
|
161
177
|
}
|
|
162
178
|
return msgId;
|
|
163
179
|
},
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HMSH_CODE_INTERRUPT } from '../../modules/enums';
|
|
2
2
|
import { HotMeshService as HotMesh } from '../hotmesh';
|
|
3
3
|
import { JobInterruptOptions, JobOutput } from '../../types/job';
|
|
4
4
|
import { StreamError } from '../../types/stream';
|
|
@@ -101,7 +101,7 @@ export class WorkflowHandleService {
|
|
|
101
101
|
const state = await this.hotMesh.getState(`${this.hotMesh.appId}.execute`, this.workflowId);
|
|
102
102
|
if (state.metadata.err) {
|
|
103
103
|
const error = JSON.parse(state.metadata.err) as StreamError;
|
|
104
|
-
if (error.code ===
|
|
104
|
+
if (error.code === HMSH_CODE_INTERRUPT || !state.data) {
|
|
105
105
|
return reject({ ...error, job_id: this.workflowId });
|
|
106
106
|
}
|
|
107
107
|
}
|
|
@@ -117,7 +117,7 @@ export class WorkflowHandleService {
|
|
|
117
117
|
this.hotMesh.sub(topic, async (topic: string, state: JobOutput) => {
|
|
118
118
|
if (state.metadata.err) {
|
|
119
119
|
const error = JSON.parse(state.metadata.err) as StreamError;
|
|
120
|
-
if (error.code ===
|
|
120
|
+
if (error.code === HMSH_CODE_INTERRUPT || !state.data) {
|
|
121
121
|
return await complete(null, state.metadata.err);
|
|
122
122
|
}
|
|
123
123
|
}
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
StreamData,
|
|
24
24
|
StreamDataResponse,
|
|
25
25
|
StreamStatus } from '../../types/stream';
|
|
26
|
+
import { HMSH_LOGLEVEL } from '../../modules/enums';
|
|
26
27
|
|
|
27
28
|
export class WorkerService {
|
|
28
29
|
static activityRegistry: Registry = {}; //user's activities
|
|
@@ -36,7 +37,7 @@ export class WorkerService {
|
|
|
36
37
|
return await WorkerService.instances.get(workflowTopic);
|
|
37
38
|
}
|
|
38
39
|
const hotMeshClient = HotMesh.init({
|
|
39
|
-
logLevel: options?.logLevel
|
|
40
|
+
logLevel: options?.logLevel ?? HMSH_LOGLEVEL,
|
|
40
41
|
appId: config.namespace ?? APP_ID,
|
|
41
42
|
engine: { redis: { ...WorkerService.connection } }
|
|
42
43
|
});
|
|
@@ -121,7 +122,7 @@ export class WorkerService {
|
|
|
121
122
|
options: config.connection.options as RedisOptions
|
|
122
123
|
};
|
|
123
124
|
const hotMeshWorker = await HotMesh.init({
|
|
124
|
-
logLevel: config.options?.logLevel
|
|
125
|
+
logLevel: config.options?.logLevel ?? HMSH_LOGLEVEL,
|
|
125
126
|
appId: config.namespace ?? APP_ID,
|
|
126
127
|
engine: { redis: redisConfig },
|
|
127
128
|
workers: [
|
|
@@ -172,7 +173,7 @@ export class WorkerService {
|
|
|
172
173
|
options: config.connection.options as RedisOptions
|
|
173
174
|
};
|
|
174
175
|
const hotMeshWorker = await HotMesh.init({
|
|
175
|
-
logLevel: config.options?.logLevel
|
|
176
|
+
logLevel: config.options?.logLevel ?? HMSH_LOGLEVEL,
|
|
176
177
|
appId: config.namespace ?? APP_ID,
|
|
177
178
|
engine: { redis: redisConfig },
|
|
178
179
|
workers: [{
|
package/services/engine/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { KeyType } from '../../modules/key';
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
HMSH_OTT_WAIT_TIME,
|
|
4
|
+
HMSH_CODE_SUCCESS,
|
|
5
|
+
HMSH_CODE_PENDING,
|
|
6
|
+
HMSH_CODE_TIMEOUT,
|
|
7
|
+
HMSH_EXPIRE_JOB_SECONDS } from '../../modules/enums';
|
|
8
8
|
import {
|
|
9
9
|
formatISODate,
|
|
10
10
|
getSubscriptionTopic,
|
|
@@ -77,6 +77,7 @@ import {
|
|
|
77
77
|
StreamError,
|
|
78
78
|
StreamRole,
|
|
79
79
|
StreamStatus } from '../../types/stream';
|
|
80
|
+
import { WorkListTaskType } from '../../types/task';
|
|
80
81
|
|
|
81
82
|
class EngineService {
|
|
82
83
|
namespace: string;
|
|
@@ -438,10 +439,10 @@ class EngineService {
|
|
|
438
439
|
streamData.code = error.code;
|
|
439
440
|
} else if (emit) {
|
|
440
441
|
streamData.status = StreamStatus.PENDING;
|
|
441
|
-
streamData.code =
|
|
442
|
+
streamData.code = HMSH_CODE_PENDING;
|
|
442
443
|
} else {
|
|
443
444
|
streamData.status = StreamStatus.SUCCESS;
|
|
444
|
-
streamData.code =
|
|
445
|
+
streamData.code = HMSH_CODE_SUCCESS;
|
|
445
446
|
}
|
|
446
447
|
return (await this.router?.publishMessage(null, streamData)) as string;
|
|
447
448
|
}
|
|
@@ -489,7 +490,7 @@ class EngineService {
|
|
|
489
490
|
};
|
|
490
491
|
return await this.router.publishMessage(null, streamData) as string;
|
|
491
492
|
}
|
|
492
|
-
async hookTime(jobId: string, gId: string, activityId: string, type?:
|
|
493
|
+
async hookTime(jobId: string, gId: string, activityId: string, type?: WorkListTaskType): Promise<string | void> {
|
|
493
494
|
if (type === 'interrupt') {
|
|
494
495
|
return await this.interrupt(
|
|
495
496
|
activityId, //note: 'activityId' is the actually job topic
|
|
@@ -499,7 +500,6 @@ class EngineService {
|
|
|
499
500
|
} else if (type === 'expire') {
|
|
500
501
|
return await this.store.expireJob(jobId, 1);
|
|
501
502
|
}
|
|
502
|
-
//'sleep': parse the activityId into parts
|
|
503
503
|
const [aid, ...dimensions] = activityId.split(',');
|
|
504
504
|
const dad = `,${dimensions.join(',')}`;
|
|
505
505
|
const streamData: StreamData = {
|
|
@@ -575,7 +575,7 @@ class EngineService {
|
|
|
575
575
|
return await this.subscribe.punsubscribe(KeyType.QUORUM, this.appId, wild);
|
|
576
576
|
}
|
|
577
577
|
//publish and await (returns the job and data (if ready)); throws error with jobid if not
|
|
578
|
-
async pubsub(topic: string, data: JobData, context?: JobState | null, timeout =
|
|
578
|
+
async pubsub(topic: string, data: JobData, context?: JobState | null, timeout = HMSH_OTT_WAIT_TIME): Promise<JobOutput> {
|
|
579
579
|
context = {
|
|
580
580
|
metadata: {
|
|
581
581
|
ngn: this.guid,
|
|
@@ -597,9 +597,10 @@ class EngineService {
|
|
|
597
597
|
}
|
|
598
598
|
});
|
|
599
599
|
setTimeout(() => {
|
|
600
|
+
//note: job is still active (the subscriber timed out)
|
|
600
601
|
this.delistJobCallback(jobId);
|
|
601
602
|
reject({
|
|
602
|
-
code:
|
|
603
|
+
code: HMSH_CODE_TIMEOUT,
|
|
603
604
|
message: 'timeout',
|
|
604
605
|
job_id: jobId
|
|
605
606
|
} as StreamError);
|
|
@@ -685,7 +686,7 @@ class EngineService {
|
|
|
685
686
|
* it will be expired immediately.
|
|
686
687
|
*/
|
|
687
688
|
resolveExpires(context: JobState, options: JobCompletionOptions): number {
|
|
688
|
-
return options.expire ?? context.metadata.expire ??
|
|
689
|
+
return options.expire ?? context.metadata.expire ?? HMSH_EXPIRE_JOB_SECONDS;
|
|
689
690
|
}
|
|
690
691
|
|
|
691
692
|
|