@hotmeshio/hotmesh 0.0.52 → 0.0.54

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.
Files changed (134) hide show
  1. package/README.md +22 -18
  2. package/build/index.d.ts +1 -2
  3. package/build/index.js +1 -3
  4. package/build/modules/enums.d.ts +8 -3
  5. package/build/modules/enums.js +16 -8
  6. package/build/modules/errors.d.ts +58 -20
  7. package/build/modules/errors.js +90 -33
  8. package/build/package.json +7 -2
  9. package/build/services/activities/activity.d.ts +8 -0
  10. package/build/services/activities/activity.js +63 -14
  11. package/build/services/activities/await.js +6 -6
  12. package/build/services/activities/cycle.d.ts +2 -2
  13. package/build/services/activities/cycle.js +5 -5
  14. package/build/services/activities/hook.js +9 -5
  15. package/build/services/activities/interrupt.d.ts +3 -3
  16. package/build/services/activities/interrupt.js +15 -6
  17. package/build/services/activities/signal.d.ts +2 -2
  18. package/build/services/activities/signal.js +4 -4
  19. package/build/services/activities/trigger.d.ts +5 -2
  20. package/build/services/activities/trigger.js +34 -4
  21. package/build/services/activities/worker.js +6 -6
  22. package/build/services/compiler/deployer.js +33 -5
  23. package/build/services/compiler/validator.d.ts +2 -0
  24. package/build/services/compiler/validator.js +5 -1
  25. package/build/services/durable/client.d.ts +7 -1
  26. package/build/services/durable/client.js +57 -38
  27. package/build/services/durable/exporter.d.ts +27 -81
  28. package/build/services/durable/exporter.js +153 -325
  29. package/build/services/durable/handle.d.ts +13 -8
  30. package/build/services/durable/handle.js +61 -48
  31. package/build/services/durable/index.d.ts +0 -2
  32. package/build/services/durable/index.js +0 -2
  33. package/build/services/durable/schemas/factory.d.ts +33 -0
  34. package/build/services/durable/schemas/factory.js +2356 -0
  35. package/build/services/durable/search.js +8 -8
  36. package/build/services/durable/worker.js +117 -25
  37. package/build/services/durable/workflow.d.ts +67 -52
  38. package/build/services/durable/workflow.js +322 -306
  39. package/build/services/engine/index.d.ts +2 -2
  40. package/build/services/engine/index.js +5 -2
  41. package/build/services/exporter/index.d.ts +2 -4
  42. package/build/services/exporter/index.js +4 -5
  43. package/build/services/hotmesh/index.d.ts +2 -2
  44. package/build/services/hotmesh/index.js +2 -2
  45. package/build/services/mapper/index.d.ts +6 -2
  46. package/build/services/mapper/index.js +6 -2
  47. package/build/services/pipe/functions/array.d.ts +2 -10
  48. package/build/services/pipe/functions/array.js +30 -28
  49. package/build/services/pipe/functions/conditional.d.ts +1 -0
  50. package/build/services/pipe/functions/conditional.js +3 -0
  51. package/build/services/pipe/functions/date.d.ts +1 -0
  52. package/build/services/pipe/functions/date.js +4 -0
  53. package/build/services/pipe/functions/index.d.ts +2 -0
  54. package/build/services/pipe/functions/index.js +2 -0
  55. package/build/services/pipe/functions/logical.d.ts +5 -0
  56. package/build/services/pipe/functions/logical.js +12 -0
  57. package/build/services/pipe/functions/object.d.ts +3 -0
  58. package/build/services/pipe/functions/object.js +25 -7
  59. package/build/services/pipe/index.d.ts +20 -3
  60. package/build/services/pipe/index.js +82 -16
  61. package/build/services/router/index.js +14 -3
  62. package/build/services/serializer/index.d.ts +3 -2
  63. package/build/services/serializer/index.js +11 -4
  64. package/build/services/store/clients/ioredis.js +6 -6
  65. package/build/services/store/clients/redis.js +7 -7
  66. package/build/services/store/index.d.ts +2 -0
  67. package/build/services/store/index.js +4 -1
  68. package/build/services/stream/clients/ioredis.js +8 -8
  69. package/build/services/stream/clients/redis.js +1 -1
  70. package/build/types/activity.d.ts +60 -5
  71. package/build/types/durable.d.ts +183 -36
  72. package/build/types/error.d.ts +48 -0
  73. package/build/types/error.js +2 -0
  74. package/build/types/exporter.d.ts +35 -7
  75. package/build/types/index.d.ts +4 -3
  76. package/build/types/job.d.ts +93 -6
  77. package/build/types/pipe.d.ts +81 -3
  78. package/build/types/stream.d.ts +61 -1
  79. package/build/types/stream.js +4 -0
  80. package/index.ts +1 -2
  81. package/modules/enums.ts +16 -8
  82. package/modules/errors.ts +139 -34
  83. package/package.json +7 -2
  84. package/services/activities/activity.ts +63 -14
  85. package/services/activities/await.ts +6 -6
  86. package/services/activities/cycle.ts +7 -6
  87. package/services/activities/hook.ts +12 -5
  88. package/services/activities/interrupt.ts +19 -9
  89. package/services/activities/signal.ts +6 -5
  90. package/services/activities/trigger.ts +43 -6
  91. package/services/activities/worker.ts +7 -7
  92. package/services/compiler/deployer.ts +33 -6
  93. package/services/compiler/validator.ts +7 -3
  94. package/services/durable/client.ts +49 -22
  95. package/services/durable/exporter.ts +162 -349
  96. package/services/durable/handle.ts +66 -53
  97. package/services/durable/index.ts +0 -2
  98. package/services/durable/schemas/factory.ts +2358 -0
  99. package/services/durable/search.ts +8 -8
  100. package/services/durable/worker.ts +128 -29
  101. package/services/durable/workflow.ts +371 -322
  102. package/services/engine/index.ts +8 -3
  103. package/services/exporter/index.ts +10 -12
  104. package/services/hotmesh/index.ts +4 -3
  105. package/services/mapper/index.ts +6 -2
  106. package/services/pipe/functions/array.ts +24 -37
  107. package/services/pipe/functions/conditional.ts +4 -0
  108. package/services/pipe/functions/date.ts +6 -0
  109. package/services/pipe/functions/index.ts +7 -5
  110. package/services/pipe/functions/logical.ts +11 -0
  111. package/services/pipe/functions/object.ts +26 -7
  112. package/services/pipe/index.ts +99 -21
  113. package/services/quorum/index.ts +1 -3
  114. package/services/router/index.ts +14 -3
  115. package/services/serializer/index.ts +12 -5
  116. package/services/store/clients/ioredis.ts +6 -6
  117. package/services/store/clients/redis.ts +7 -7
  118. package/services/store/index.ts +4 -1
  119. package/services/stream/clients/ioredis.ts +8 -8
  120. package/services/stream/clients/redis.ts +1 -1
  121. package/types/activity.ts +87 -15
  122. package/types/durable.ts +263 -75
  123. package/types/error.ts +52 -0
  124. package/types/exporter.ts +43 -9
  125. package/types/index.ts +14 -8
  126. package/types/job.ts +157 -36
  127. package/types/pipe.ts +84 -3
  128. package/types/stream.ts +82 -23
  129. package/build/services/durable/factory.d.ts +0 -17
  130. package/build/services/durable/factory.js +0 -817
  131. package/build/services/durable/meshos.d.ts +0 -127
  132. package/build/services/durable/meshos.js +0 -380
  133. package/services/durable/factory.ts +0 -818
  134. package/services/durable/meshos.ts +0 -441
@@ -54,8 +54,8 @@ export class Search {
54
54
  const hotMeshPrefix = KeyService.mintKey(hotMeshClient.namespace, KeyType.JOB_STATE, keyParams);
55
55
  const prefixes = search.prefix.map((prefix) => `${hotMeshPrefix}${prefix}`);
56
56
  await store.exec('FT.CREATE', `${search.index}`, 'ON', 'HASH', 'PREFIX', prefixes.length.toString(), ...prefixes, 'SCHEMA', ...schema);
57
- } catch (err) {
58
- hotMeshClient.engine.logger.info('durable-client-search-err', { err });
57
+ } catch (error) {
58
+ hotMeshClient.engine.logger.info('durable-client-search-err', { ...error });
59
59
  }
60
60
  }
61
61
  }
@@ -71,8 +71,8 @@ export class Search {
71
71
  const store = hotMeshClient.engine.store;
72
72
  const searchIndexes = await store.exec('FT._LIST');
73
73
  return searchIndexes as string[];
74
- } catch (err) {
75
- hotMeshClient.engine.logger.info('durable-client-search-list-err', { err });
74
+ } catch (error) {
75
+ hotMeshClient.engine.logger.info('durable-client-search-list-err', { ...error });
76
76
  return [];
77
77
  }
78
78
  }
@@ -113,8 +113,8 @@ export class Search {
113
113
  async get(key: string): Promise<string> {
114
114
  try {
115
115
  return await this.store.exec('HGET',this.jobId, this.safeKey(key)) as string;
116
- } catch (err) {
117
- this.hotMeshClient.logger.error('durable-search-get-error', { err });
116
+ } catch (error) {
117
+ this.hotMeshClient.logger.error('durable-search-get-error', { ...error });
118
118
  return '';
119
119
  }
120
120
  }
@@ -126,8 +126,8 @@ export class Search {
126
126
  }
127
127
  try {
128
128
  return await this.store.exec('HMGET', this.jobId, ...safeArgs) as string[];
129
- } catch (err) {
130
- this.hotMeshClient.logger.error('durable-search-mget-error', { err });
129
+ } catch (error) {
130
+ this.hotMeshClient.logger.error('durable-search-mget-error', { ...error });
131
131
  return [];
132
132
  }
133
133
  }
@@ -1,13 +1,22 @@
1
+ import ms from 'ms';
1
2
  import {
3
+ HMSH_CODE_DURABLE_ALL,
4
+ HMSH_CODE_DURABLE_RETRYABLE,
5
+ HMSH_DURABLE_EXP_BACKOFF,
6
+ HMSH_DURABLE_MAX_INTERVAL,
7
+ HMSH_DURABLE_MAX_ATTEMPTS,
8
+ HMSH_LOGLEVEL } from '../../modules/enums';
9
+ import {
10
+ DurableChildError,
2
11
  DurableFatalError,
3
- DurableIncompleteSignalError,
4
12
  DurableMaxedError,
13
+ DurableProxyError,
5
14
  DurableRetryError,
6
- DurableSleepForError,
15
+ DurableSleepError,
7
16
  DurableTimeoutError,
8
- DurableWaitForSignalError} from '../../modules/errors';
17
+ DurableWaitForError } from '../../modules/errors';
9
18
  import { asyncLocalStorage } from '../../modules/storage';
10
- import { APP_ID, APP_VERSION, getWorkflowYAML } from './factory';
19
+ import { APP_ID, APP_VERSION, getWorkflowYAML } from './schemas/factory';
11
20
  import { HotMeshService as HotMesh } from '../hotmesh';
12
21
  import {
13
22
  ActivityWorkflowDataType,
@@ -22,7 +31,7 @@ import {
22
31
  StreamData,
23
32
  StreamDataResponse,
24
33
  StreamStatus } from '../../types/stream';
25
- import { HMSH_LOGLEVEL } from '../../modules/enums';
34
+ import { formatISODate } from '../../modules/utils';
26
35
 
27
36
  export class WorkerService {
28
37
  static activityRegistry: Registry = {}; //user's activities
@@ -135,6 +144,7 @@ export class WorkerService {
135
144
  return hotMeshWorker;
136
145
  }
137
146
 
147
+ //this is the linked worker function in the reentrant workflow test
138
148
  wrapActivityFunctions(): Function {
139
149
  return async (data: StreamData): Promise<StreamDataResponse> => {
140
150
  try {
@@ -150,17 +160,42 @@ export class WorkerService {
150
160
  data: { response: pojoResponse }
151
161
  };
152
162
  } catch (err) {
153
- this.activityRunner.engine.logger.error('durable-worker-activity-err', err);
163
+ this.activityRunner.engine.logger.error('durable-worker-activity-err', { name: err.name, message: err.message, stack: err.stack });
154
164
  if (!(err instanceof DurableTimeoutError) &&
155
165
  !(err instanceof DurableMaxedError) &&
156
166
  !(err instanceof DurableFatalError)) {
157
- err = new DurableRetryError(err.message);
167
+
168
+ //use code 599 as a proxy for all retryable errors
169
+ // (basically anything not 596, 597, 598)
170
+ return {
171
+ status: StreamStatus.SUCCESS,
172
+ code: HMSH_CODE_DURABLE_RETRYABLE,
173
+ metadata: { ...data.metadata },
174
+ data: {
175
+ $error: {
176
+ message: err.message,
177
+ stack: err.stack,
178
+ timestamp: formatISODate(new Date()),
179
+ }
180
+ },
181
+ } as StreamDataResponse;
158
182
  }
183
+
159
184
  return {
160
- status: StreamStatus.ERROR,
185
+ //always returrn success (the Durable module is just fine);
186
+ // it's the user's function that has failed
187
+ status: StreamStatus.SUCCESS,
161
188
  code: err.code,
189
+ stack: err.stack,
162
190
  metadata: { ...data.metadata },
163
- data: { message: err.message }
191
+ data: {
192
+ $error: {
193
+ message: err.message,
194
+ stack: err.stack,
195
+ timestamp: formatISODate(new Date()),
196
+ code: err.code,
197
+ }
198
+ },
164
199
  } as StreamDataResponse;
165
200
  }
166
201
  }
@@ -196,15 +231,17 @@ export class WorkerService {
196
231
 
197
232
  wrapWorkflowFunction(workflowFunction: Function, workflowTopic: string, config: WorkerConfig): Function {
198
233
  return async (data: StreamData): Promise<StreamDataResponse> => {
199
- const counter = { counter: 0 };
234
+ const counter = { counter: 0 };
235
+ const interruptionRegistry: any[] = [];
200
236
  try {
201
237
  //incoming data payload has arguments and workflowId
202
238
  const workflowInput = data.data as unknown as WorkflowDataType;
203
239
  const context = new Map();
204
- context.set('raw', data);
205
- context.set('namespace', config.namespace ?? APP_ID);
240
+ context.set('canRetry', workflowInput.canRetry);
206
241
  context.set('counter', counter);
207
- context.set('workflowId', workflowInput.workflowId);
242
+ context.set('interruptionRegistry', interruptionRegistry);
243
+ context.set('namespace', config.namespace ?? APP_ID);
244
+ context.set('raw', data);
208
245
  context.set('workflowId', workflowInput.workflowId);
209
246
  if (workflowInput.originJobId) {
210
247
  //if present there is an origin job to which this job is subordinated;
@@ -219,8 +256,8 @@ export class WorkerService {
219
256
  context.set('workflowDimension', workflowInput.workflowDimension);
220
257
  replayQuery = `-*${workflowInput.workflowDimension}-*`;
221
258
  } else {
222
- //last letter of words like 'hook', 'sleep', 'wait', 'signal', 'search', 'start'
223
- replayQuery = '-*[ehklpt]-*';
259
+ //last letter of words like 'hook', 'sleep', 'wait', 'signal', 'search', 'start', 'proxy', 'child', 'collator'
260
+ replayQuery = '-*[ehklptydr]-*';
224
261
  }
225
262
  context.set('workflowTopic', workflowTopic);
226
263
  context.set('workflowName', workflowTopic.split('-').pop());
@@ -245,50 +282,112 @@ export class WorkerService {
245
282
  data: { response: workflowResponse, done: true }
246
283
  };
247
284
  } catch (err) {
248
- //not an error...just a trigger to sleep
249
- if (err instanceof DurableSleepForError) {
285
+ if (err instanceof DurableWaitForError || interruptionRegistry.length > 1) {
286
+
287
+ //NOTE: this type is spawned when `Promise.all` is used OR if the interruption is a `waitFor`
288
+ const workflowInput = data.data as unknown as WorkflowDataType;
289
+ const execIndex = counter.counter - interruptionRegistry.length + 1;
290
+ const { workflowId, workflowTopic, workflowDimension, originJobId } = workflowInput;
291
+ const collatorFlowId = `-${workflowId}-$${workflowDimension || ''}-$${execIndex}`;
292
+ return {
293
+ status: StreamStatus.SUCCESS,
294
+ code: HMSH_CODE_DURABLE_ALL,
295
+ metadata: { ...data.metadata },
296
+ data: {
297
+ code: HMSH_CODE_DURABLE_ALL,
298
+ items: [...interruptionRegistry],
299
+ size: interruptionRegistry.length,
300
+ workflowDimension: workflowDimension || '',
301
+ index: execIndex,
302
+ originJobId: originJobId || workflowId,
303
+ parentWorkflowId: workflowId,
304
+ workflowId: collatorFlowId,
305
+ workflowTopic: workflowTopic,
306
+ },
307
+ } as StreamDataResponse;
308
+
309
+ } else if (err instanceof DurableSleepError) {
310
+ //return the sleep interruption
250
311
  return {
251
312
  status: StreamStatus.SUCCESS,
252
313
  code: err.code,
253
314
  metadata: { ...data.metadata },
254
315
  data: {
255
316
  code: err.code,
256
- message: JSON.stringify({ duration: err.duration, index: err.index, dimension: err.dimension }),
317
+ message: JSON.stringify({ duration: err.duration, index: err.index, workflowDimension: err.workflowDimension }),
257
318
  duration: err.duration,
258
319
  index: err.index,
259
- dimension: err.dimension
320
+ workflowDimension: err.workflowDimension,
260
321
  }
261
322
  } as StreamDataResponse;
262
323
 
263
- //not an error...just a trigger to wait for a signal
264
- } else if (err instanceof DurableWaitForSignalError) {
324
+ } else if (err instanceof DurableProxyError) {
325
+ //return the proxyActivity interruption
265
326
  return {
266
327
  status: StreamStatus.SUCCESS,
267
328
  code: err.code,
268
329
  metadata: { ...data.metadata },
269
330
  data: {
270
331
  code: err.code,
271
- signals: err.signals,
272
- index: err.signals[0].index
332
+ message: JSON.stringify({ message: err.message, workflowId: err.workflowId, activityName: err.activityName, dimension: err.workflowDimension }),
333
+ arguments: err.arguments,
334
+ workflowDimension: err.workflowDimension,
335
+ index: err.index,
336
+ originJobId: err.originJobId,
337
+ parentWorkflowId: err.parentWorkflowId,
338
+ workflowId: err.workflowId,
339
+ workflowTopic: err.workflowTopic,
340
+ activityName: err.activityName,
341
+ backoffCoefficient: err.backoffCoefficient,
342
+ maximumAttempts: err.maximumAttempts,
343
+ maximumInterval: err.maximumInterval,
273
344
  }
274
345
  } as StreamDataResponse;
275
346
 
276
- //not an error...still waiting for all the signals to arrive
277
- } else if (err instanceof DurableIncompleteSignalError) {
347
+ } else if (err instanceof DurableChildError) {
348
+ //return the child interruption
349
+ const msg = {
350
+ message: err.message,
351
+ workflowId: err.workflowId,
352
+ dimension: err.workflowDimension
353
+ };
278
354
  return {
279
355
  status: StreamStatus.SUCCESS,
280
356
  code: err.code,
281
357
  metadata: { ...data.metadata },
282
- data: { code: err.code }
358
+ data: {
359
+ arguments: err.arguments,
360
+ await: err.await,
361
+ backoffCoefficient: err.backoffCoefficient || HMSH_DURABLE_EXP_BACKOFF ,
362
+ code: err.code,
363
+ index: err.index,
364
+ message: JSON.stringify(msg),
365
+ maximumAttempts: err.maximumAttempts || HMSH_DURABLE_MAX_ATTEMPTS,
366
+ maximumInterval: err.maximumInterval || ms(HMSH_DURABLE_MAX_INTERVAL) / 1000,
367
+ originJobId: err.originJobId,
368
+ parentWorkflowId: err.parentWorkflowId,
369
+ workflowDimension: err.workflowDimension,
370
+ workflowId: err.workflowId,
371
+ workflowTopic: err.workflowTopic,
372
+ }
283
373
  } as StreamDataResponse;
284
374
  }
285
375
 
286
- // all other errors are fatal (598, 597, 596) or will be retried (599)
376
+ // ALL other errors are actual fatal errors (598, 597, 596)
377
+ // OR will be retried (599)
287
378
  return {
288
- status: StreamStatus.ERROR,
379
+ status: StreamStatus.SUCCESS,
289
380
  code: err.code || new DurableRetryError(err.message).code,
290
381
  metadata: { ...data.metadata },
291
- data: { message: err.message, type: err.name }
382
+ data: {
383
+ $error: {
384
+ message: err.message,
385
+ type: err.name,
386
+ name: err.name,
387
+ stack: err.stack,
388
+ code: err.code || new DurableRetryError(err.message).code,
389
+ }
390
+ }
292
391
  } as StreamDataResponse;
293
392
  }
294
393
  }