@hotmeshio/hotmesh 0.0.14 → 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.14",
3
+ "version": "0.0.16",
4
4
  "description": "Unbreakable Workflows",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -136,10 +136,10 @@ class Activity {
136
136
  try {
137
137
  this.setLeg(2);
138
138
  await this.getState(jobId);
139
- const aState = await collator_1.CollatorService.notarizeReentry(this);
140
- this.adjacentIndex = collator_1.CollatorService.getDimensionalIndex(aState);
141
139
  telemetry = new telemetry_1.TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
142
140
  telemetry.startActivitySpan(this.leg);
141
+ const aState = await collator_1.CollatorService.notarizeReentry(this);
142
+ this.adjacentIndex = collator_1.CollatorService.getDimensionalIndex(aState);
143
143
  this.bindActivityData('hook');
144
144
  this.mapJobData();
145
145
  this.adjacencyList = await this.filterAdjacent();
@@ -159,6 +159,10 @@ class Activity {
159
159
  return jobStatus;
160
160
  }
161
161
  catch (error) {
162
+ if (error instanceof errors_1.CollationError && error.fault === 'inactive') {
163
+ this.logger.info('process-hook-event-inactive-error', { error });
164
+ return;
165
+ }
162
166
  this.logger.error('engine-process-hook-event-error', { error });
163
167
  telemetry.setActivityError(error.message);
164
168
  throw error;
@@ -201,6 +205,10 @@ class Activity {
201
205
  this.transitionAdjacent(multiResponse, telemetry);
202
206
  }
203
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
+ }
204
212
  this.logger.error('activity-process-event-error', { error });
205
213
  telemetry && telemetry.setActivityError(error.message);
206
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.14",
3
+ "version": "0.0.16",
4
4
  "description": "Unbreakable Workflows",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -1,4 +1,4 @@
1
- import { GetStateError } from '../../modules/errors';
1
+ import { CollationError, GetStateError } from '../../modules/errors';
2
2
  import {
3
3
  formatISODate,
4
4
  getValueByPath,
@@ -183,11 +183,11 @@ class Activity {
183
183
  try {
184
184
  this.setLeg(2);
185
185
  await this.getState(jobId);
186
+ telemetry = new TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
187
+ telemetry.startActivitySpan(this.leg);
186
188
  const aState = await CollatorService.notarizeReentry(this);
187
189
  this.adjacentIndex = CollatorService.getDimensionalIndex(aState);
188
190
 
189
- telemetry = new TelemetryService(this.engine.appId, this.config, this.metadata, this.context);
190
- telemetry.startActivitySpan(this.leg);
191
191
  this.bindActivityData('hook');
192
192
  this.mapJobData();
193
193
  this.adjacencyList = await this.filterAdjacent();
@@ -209,6 +209,10 @@ class Activity {
209
209
  telemetry.setActivityAttributes(attrs);
210
210
  return jobStatus as number;
211
211
  } catch (error) {
212
+ if (error instanceof CollationError && error.fault === 'inactive') {
213
+ this.logger.info('process-hook-event-inactive-error', { error });
214
+ return;
215
+ }
212
216
  this.logger.error('engine-process-hook-event-error', { error });
213
217
  telemetry.setActivityError(error.message);
214
218
  throw error;
@@ -251,6 +255,10 @@ class Activity {
251
255
  }
252
256
  this.transitionAdjacent(multiResponse, telemetry);
253
257
  } catch (error) {
258
+ if (error instanceof CollationError && error.fault === 'inactive') {
259
+ this.logger.info('process-event-inactive-error', { error });
260
+ return;
261
+ }
254
262
  this.logger.error('activity-process-event-error', { error });
255
263
  telemetry && telemetry.setActivityError(error.message);
256
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