@hotmeshio/hotmesh 0.0.15 → 0.0.16
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 +1 -1
- package/build/package.json +1 -1
- package/build/services/activities/activity.js +5 -2
- package/build/services/durable/client.d.ts +1 -0
- package/build/services/durable/client.js +5 -0
- package/build/services/durable/factory.d.ts +5 -0
- package/build/services/durable/factory.js +5 -0
- package/build/services/durable/workflow.js +18 -9
- package/build/services/store/index.js +3 -0
- package/package.json +1 -1
- package/services/activities/activity.ts +5 -2
- package/services/durable/client.ts +6 -0
- package/services/durable/factory.ts +5 -0
- package/services/durable/workflow.ts +23 -10
- package/services/store/index.ts +3 -0
package/README.md
CHANGED
|
@@ -22,7 +22,7 @@ The HotMesh SDK is designed to keep your code front-and-center. Write functions
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export async function saludar(nombre: string): Promise<string> {
|
|
25
|
-
Math.random() > 0.5
|
|
25
|
+
if (Math.random() > 0.5) throw new Error('Random error');
|
|
26
26
|
return `¡Hola, ${nombre}!`;
|
|
27
27
|
}
|
|
28
28
|
```
|
package/build/package.json
CHANGED
|
@@ -159,8 +159,7 @@ class Activity {
|
|
|
159
159
|
return jobStatus;
|
|
160
160
|
}
|
|
161
161
|
catch (error) {
|
|
162
|
-
if (error instanceof errors_1.CollationError) {
|
|
163
|
-
//caused by external over-signaling; the job is complete
|
|
162
|
+
if (error instanceof errors_1.CollationError && error.fault === 'inactive') {
|
|
164
163
|
this.logger.info('process-hook-event-inactive-error', { error });
|
|
165
164
|
return;
|
|
166
165
|
}
|
|
@@ -206,6 +205,10 @@ class Activity {
|
|
|
206
205
|
this.transitionAdjacent(multiResponse, telemetry);
|
|
207
206
|
}
|
|
208
207
|
catch (error) {
|
|
208
|
+
if (error instanceof errors_1.CollationError && error.fault === 'inactive') {
|
|
209
|
+
this.logger.info('process-event-inactive-error', { error });
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
209
212
|
this.logger.error('activity-process-event-error', { error });
|
|
210
213
|
telemetry && telemetry.setActivityError(error.message);
|
|
211
214
|
throw error;
|
|
@@ -10,6 +10,7 @@ export declare class ClientService {
|
|
|
10
10
|
workflow: {
|
|
11
11
|
start: (options: WorkflowOptions) => Promise<WorkflowHandleService>;
|
|
12
12
|
signal: (signalId: string, data: Record<any, any>) => Promise<string>;
|
|
13
|
+
getHandle: (taskQueue: string, workflowName: string, workflowId: string) => Promise<WorkflowHandleService>;
|
|
13
14
|
};
|
|
14
15
|
activateWorkflow(hotMesh: HotMesh, appId?: string, version?: string): Promise<void>;
|
|
15
16
|
static shutdown(): Promise<void>;
|
|
@@ -97,6 +97,11 @@ class ClientService {
|
|
|
97
97
|
},
|
|
98
98
|
signal: async (signalId, data) => {
|
|
99
99
|
return await (await this.getHotMeshClient('durable.wfs.signal')).hook('durable.wfs.signal', { id: signalId, data });
|
|
100
|
+
},
|
|
101
|
+
getHandle: async (taskQueue, workflowName, workflowId) => {
|
|
102
|
+
const workflowTopic = `${taskQueue}-${workflowName}`;
|
|
103
|
+
const hotMeshClient = await this.getHotMeshClient(workflowTopic);
|
|
104
|
+
return new handle_1.WorkflowHandleService(hotMeshClient, workflowTopic, workflowId);
|
|
100
105
|
}
|
|
101
106
|
};
|
|
102
107
|
this.connection = config.connection;
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
* NOTE: Using `maxSystemRetries = 3` and `backoffCoefficient = 10`, errant
|
|
3
3
|
* workflows will be retried on the following schedule (8 times in 27 hours):
|
|
4
4
|
* => 10ms, 100ms, 1000ms, 10s, 100s, 1_000s, 10_000s, 100_000s
|
|
5
|
+
* 593:
|
|
6
|
+
* 594: waitforsignal
|
|
7
|
+
* 595: sleep
|
|
8
|
+
* 596, 597, 598: fatal
|
|
9
|
+
* 599: retry
|
|
5
10
|
*/
|
|
6
11
|
declare const getWorkflowYAML: (app: string, version: string) => string;
|
|
7
12
|
declare const APP_VERSION = "1";
|
|
@@ -3,6 +3,11 @@
|
|
|
3
3
|
* NOTE: Using `maxSystemRetries = 3` and `backoffCoefficient = 10`, errant
|
|
4
4
|
* workflows will be retried on the following schedule (8 times in 27 hours):
|
|
5
5
|
* => 10ms, 100ms, 1000ms, 10s, 100s, 1_000s, 10_000s, 100_000s
|
|
6
|
+
* 593:
|
|
7
|
+
* 594: waitforsignal
|
|
8
|
+
* 595: sleep
|
|
9
|
+
* 596, 597, 598: fatal
|
|
10
|
+
* 599: retry
|
|
6
11
|
*/
|
|
7
12
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
13
|
exports.WFS_HOOK_ID = exports.WFSC_PUBLISHES_TOPIC = exports.WFSC_SUBSCRIBES_TOPIC = exports.WFS_PUBLISHES_TOPIC = exports.WFS_SUBSCRIBES_TOPIC = exports.DEFAULT_COEFFICIENT = exports.SLEEP_HOOK_ID = exports.ACTIVITY_HOOK_ID = exports.HOOK_ID = exports.PUBLISHES_TOPIC = exports.SUBSCRIBES_TOPIC = exports.SLEEP_PUBLISHES_TOPIC = exports.SLEEP_SUBSCRIBES_TOPIC = exports.ACTIVITY_PUBLISHES_TOPIC = exports.ACTIVITY_SUBSCRIBES_TOPIC = exports.APP_ID = exports.APP_VERSION = exports.getWorkflowYAML = void 0;
|
|
@@ -51,17 +51,26 @@ class WorkflowService {
|
|
|
51
51
|
const workflowId = store.get('workflowId');
|
|
52
52
|
const workflowTrace = store.get('workflowTrace');
|
|
53
53
|
const workflowSpan = store.get('workflowSpan');
|
|
54
|
+
const COUNTER = store.get('counter');
|
|
55
|
+
const execIndex = COUNTER.counter = COUNTER.counter + 1;
|
|
56
|
+
const childJobId = `${workflowId}-$${options.workflowName}-${execIndex}`;
|
|
54
57
|
const client = new client_1.ClientService({
|
|
55
58
|
connection: await connection_1.ConnectionService.connect(worker_1.WorkerService.connection),
|
|
56
59
|
});
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
60
|
+
let handle = await client.workflow.getHandle(options.taskQueue, options.workflowName, childJobId);
|
|
61
|
+
try {
|
|
62
|
+
return await handle.result();
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
handle = await client.workflow.start({
|
|
66
|
+
...options,
|
|
67
|
+
workflowId: childJobId,
|
|
68
|
+
workflowTrace,
|
|
69
|
+
workflowSpan,
|
|
70
|
+
});
|
|
71
|
+
const result = await handle.result();
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
65
74
|
}
|
|
66
75
|
static proxyActivities(options) {
|
|
67
76
|
if (options.activities) {
|
|
@@ -168,7 +177,7 @@ class WorkflowService {
|
|
|
168
177
|
const trc = store.get('workflowTrace');
|
|
169
178
|
const spn = store.get('workflowSpan');
|
|
170
179
|
const activityTopic = `${workflowTopic}-activity`;
|
|
171
|
-
const activityJobId = `${workflowId}
|
|
180
|
+
const activityJobId = `${workflowId}-$${activityName}-${execIndex}`;
|
|
172
181
|
let activityState;
|
|
173
182
|
try {
|
|
174
183
|
const hotMeshClient = await worker_1.WorkerService.getHotMesh(activityTopic);
|
|
@@ -373,6 +373,9 @@ class StoreService {
|
|
|
373
373
|
async getStatus(jobId, appId) {
|
|
374
374
|
const jobKey = this.mintKey(key_1.KeyType.JOB_STATE, { appId, jobId });
|
|
375
375
|
const status = await this.redisClient[this.commands.hget](jobKey, ':');
|
|
376
|
+
if (status === null) {
|
|
377
|
+
throw new Error(`Job ${jobId} not found`);
|
|
378
|
+
}
|
|
376
379
|
return Number(status);
|
|
377
380
|
}
|
|
378
381
|
async setState({ ...state }, status, jobId, symbolNames, dIds, multi) {
|
package/package.json
CHANGED
|
@@ -209,8 +209,7 @@ class Activity {
|
|
|
209
209
|
telemetry.setActivityAttributes(attrs);
|
|
210
210
|
return jobStatus as number;
|
|
211
211
|
} catch (error) {
|
|
212
|
-
if (error instanceof CollationError) {
|
|
213
|
-
//caused by external over-signaling; the job is complete
|
|
212
|
+
if (error instanceof CollationError && error.fault === 'inactive') {
|
|
214
213
|
this.logger.info('process-hook-event-inactive-error', { error });
|
|
215
214
|
return;
|
|
216
215
|
}
|
|
@@ -256,6 +255,10 @@ class Activity {
|
|
|
256
255
|
}
|
|
257
256
|
this.transitionAdjacent(multiResponse, telemetry);
|
|
258
257
|
} catch (error) {
|
|
258
|
+
if (error instanceof CollationError && error.fault === 'inactive') {
|
|
259
|
+
this.logger.info('process-event-inactive-error', { error });
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
259
262
|
this.logger.error('activity-process-event-error', { error });
|
|
260
263
|
telemetry && telemetry.setActivityError(error.message);
|
|
261
264
|
throw error;
|
|
@@ -115,6 +115,12 @@ export class ClientService {
|
|
|
115
115
|
|
|
116
116
|
signal: async (signalId: string, data: Record<any, any>): Promise<string> => {
|
|
117
117
|
return await (await this.getHotMeshClient('durable.wfs.signal')).hook('durable.wfs.signal', { id: signalId, data });
|
|
118
|
+
},
|
|
119
|
+
|
|
120
|
+
getHandle: async (taskQueue: string, workflowName: string, workflowId: string): Promise<WorkflowHandleService> => {
|
|
121
|
+
const workflowTopic = `${taskQueue}-${workflowName}`;
|
|
122
|
+
const hotMeshClient = await this.getHotMeshClient(workflowTopic);
|
|
123
|
+
return new WorkflowHandleService(hotMeshClient, workflowTopic, workflowId);
|
|
118
124
|
}
|
|
119
125
|
}
|
|
120
126
|
|
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
* NOTE: Using `maxSystemRetries = 3` and `backoffCoefficient = 10`, errant
|
|
3
3
|
* workflows will be retried on the following schedule (8 times in 27 hours):
|
|
4
4
|
* => 10ms, 100ms, 1000ms, 10s, 100s, 1_000s, 10_000s, 100_000s
|
|
5
|
+
* 593:
|
|
6
|
+
* 594: waitforsignal
|
|
7
|
+
* 595: sleep
|
|
8
|
+
* 596, 597, 598: fatal
|
|
9
|
+
* 599: retry
|
|
5
10
|
*/
|
|
6
11
|
|
|
7
12
|
//todo: getChildWorkflowYAML (includes key, so flow will cleanup)
|
|
@@ -8,7 +8,6 @@ import { ActivityConfig, ProxyType, WorkflowOptions } from "../../types/durable"
|
|
|
8
8
|
import { JobOutput, JobState } from '../../types';
|
|
9
9
|
import { ACTIVITY_PUBLISHES_TOPIC, ACTIVITY_SUBSCRIBES_TOPIC, SLEEP_SUBSCRIBES_TOPIC, WFS_SUBSCRIBES_TOPIC } from './factory';
|
|
10
10
|
import { DurableIncompleteSignalError, DurableSleepError, DurableWaitForSignalError } from '../../modules/errors';
|
|
11
|
-
import { stringify } from 'querystring';
|
|
12
11
|
|
|
13
12
|
/*
|
|
14
13
|
`proxyActivities` returns a wrapped instance of the
|
|
@@ -52,18 +51,32 @@ export class WorkflowService {
|
|
|
52
51
|
const workflowId = store.get('workflowId');
|
|
53
52
|
const workflowTrace = store.get('workflowTrace');
|
|
54
53
|
const workflowSpan = store.get('workflowSpan');
|
|
54
|
+
const COUNTER = store.get('counter');
|
|
55
|
+
const execIndex = COUNTER.counter = COUNTER.counter + 1;
|
|
56
|
+
const childJobId = `${workflowId}-$${options.workflowName}-${execIndex}`;
|
|
55
57
|
|
|
56
58
|
const client = new Client({
|
|
57
59
|
connection: await Connection.connect(WorkerService.connection),
|
|
58
60
|
});
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
61
|
+
|
|
62
|
+
let handle = await client.workflow.getHandle(
|
|
63
|
+
options.taskQueue,
|
|
64
|
+
options.workflowName,
|
|
65
|
+
childJobId
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
return await handle.result() as T;
|
|
70
|
+
} catch (error) {
|
|
71
|
+
handle = await client.workflow.start({
|
|
72
|
+
...options,
|
|
73
|
+
workflowId: childJobId,
|
|
74
|
+
workflowTrace,
|
|
75
|
+
workflowSpan,
|
|
76
|
+
});
|
|
77
|
+
const result = await handle.result();
|
|
78
|
+
return result as T;
|
|
79
|
+
}
|
|
67
80
|
}
|
|
68
81
|
|
|
69
82
|
static proxyActivities<ACT>(options?: ActivityConfig): ProxyType<ACT> {
|
|
@@ -172,7 +185,7 @@ export class WorkflowService {
|
|
|
172
185
|
const trc = store.get('workflowTrace');
|
|
173
186
|
const spn = store.get('workflowSpan');
|
|
174
187
|
const activityTopic = `${workflowTopic}-activity`;
|
|
175
|
-
const activityJobId = `${workflowId}
|
|
188
|
+
const activityJobId = `${workflowId}-$${activityName}-${execIndex}`;
|
|
176
189
|
|
|
177
190
|
let activityState: JobOutput
|
|
178
191
|
try {
|
package/services/store/index.ts
CHANGED
|
@@ -452,6 +452,9 @@ abstract class StoreService<T, U extends AbstractRedisClient> {
|
|
|
452
452
|
async getStatus(jobId: string, appId: string): Promise<number> {
|
|
453
453
|
const jobKey = this.mintKey(KeyType.JOB_STATE, { appId, jobId });
|
|
454
454
|
const status = await this.redisClient[this.commands.hget](jobKey, ':');
|
|
455
|
+
if (status === null) {
|
|
456
|
+
throw new Error(`Job ${jobId} not found`);
|
|
457
|
+
}
|
|
455
458
|
return Number(status);
|
|
456
459
|
}
|
|
457
460
|
|