@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 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 && throw new Error('Random error');
25
+ if (Math.random() > 0.5) throw new Error('Random error');
26
26
  return `¡Hola, ${nombre}!`;
27
27
  }
28
28
  ```
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "description": "Unbreakable Workflows",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -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
- const handle = await client.workflow.start({
58
- ...options,
59
- workflowId: `${workflowId}${options.workflowId}`,
60
- workflowTrace,
61
- workflowSpan,
62
- });
63
- const result = await handle.result();
64
- return result;
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}-${activityName}-${execIndex}`;
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hotmeshio/hotmesh",
3
- "version": "0.0.15",
3
+ "version": "0.0.16",
4
4
  "description": "Unbreakable Workflows",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -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
- const handle = await client.workflow.start({
60
- ...options,
61
- workflowId: `${workflowId}${options.workflowId}`, //concat (caller MUST PROVIDE)
62
- workflowTrace,
63
- workflowSpan,
64
- });
65
- const result = await handle.result();
66
- return result as T;
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}-${activityName}-${execIndex}`;
188
+ const activityJobId = `${workflowId}-$${activityName}-${execIndex}`;
176
189
 
177
190
  let activityState: JobOutput
178
191
  try {
@@ -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