@hotmeshio/hotmesh 0.0.19 → 0.0.20

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 (58) hide show
  1. package/README.md +4 -4
  2. package/build/modules/errors.d.ts +2 -1
  3. package/build/modules/errors.js +2 -1
  4. package/build/package.json +2 -1
  5. package/build/services/activities/activity.d.ts +2 -2
  6. package/build/services/activities/activity.js +10 -8
  7. package/build/services/activities/hook.d.ts +2 -1
  8. package/build/services/activities/hook.js +12 -9
  9. package/build/services/activities/signal.d.ts +4 -0
  10. package/build/services/activities/signal.js +16 -2
  11. package/build/services/durable/client.d.ts +15 -5
  12. package/build/services/durable/client.js +37 -14
  13. package/build/services/durable/factory.d.ts +2 -16
  14. package/build/services/durable/factory.js +276 -46
  15. package/build/services/durable/handle.d.ts +1 -1
  16. package/build/services/durable/handle.js +18 -5
  17. package/build/services/durable/search.d.ts +8 -1
  18. package/build/services/durable/search.js +34 -7
  19. package/build/services/durable/worker.d.ts +7 -9
  20. package/build/services/durable/worker.js +29 -23
  21. package/build/services/durable/workflow.d.ts +18 -1
  22. package/build/services/durable/workflow.js +99 -35
  23. package/build/services/engine/index.d.ts +2 -2
  24. package/build/services/engine/index.js +7 -12
  25. package/build/services/hotmesh/index.d.ts +2 -2
  26. package/build/services/hotmesh/index.js +2 -2
  27. package/build/services/signaler/store.d.ts +2 -2
  28. package/build/services/signaler/store.js +17 -7
  29. package/build/services/signaler/stream.js +1 -0
  30. package/build/services/store/clients/redis.js +1 -1
  31. package/build/services/store/index.js +3 -0
  32. package/build/services/telemetry/index.js +7 -1
  33. package/build/types/activity.d.ts +5 -3
  34. package/build/types/durable.d.ts +12 -1
  35. package/build/types/hook.d.ts +0 -1
  36. package/build/types/index.d.ts +1 -1
  37. package/modules/errors.ts +4 -2
  38. package/package.json +2 -1
  39. package/services/activities/activity.ts +10 -8
  40. package/services/activities/hook.ts +13 -10
  41. package/services/activities/signal.ts +17 -3
  42. package/services/durable/client.ts +40 -15
  43. package/services/durable/factory.ts +274 -46
  44. package/services/durable/handle.ts +18 -5
  45. package/services/durable/search.ts +36 -7
  46. package/services/durable/worker.ts +30 -24
  47. package/services/durable/workflow.ts +111 -38
  48. package/services/engine/index.ts +8 -12
  49. package/services/hotmesh/index.ts +3 -3
  50. package/services/signaler/store.ts +18 -8
  51. package/services/signaler/stream.ts +1 -0
  52. package/services/store/clients/redis.ts +1 -1
  53. package/services/store/index.ts +2 -0
  54. package/services/telemetry/index.ts +6 -1
  55. package/types/activity.ts +10 -8
  56. package/types/durable.ts +13 -0
  57. package/types/hook.ts +0 -1
  58. package/types/index.ts +1 -0
@@ -2,16 +2,12 @@
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:
5
+ *
6
6
  * 594: waitforsignal
7
7
  * 595: sleep
8
8
  * 596, 597, 598: fatal
9
9
  * 599: retry
10
10
  */
11
-
12
- //todo: getChildWorkflowYAML (includes key, so flow will cleanup)
13
- //todo: if an activity throws an error, it should self-clean its index
14
-
15
11
  const getWorkflowYAML = (app: string, version: string) => {
16
12
  return `app:
17
13
  id: ${app}
@@ -45,6 +41,7 @@ const getWorkflowYAML = (app: string, version: string) => {
45
41
 
46
42
  activities:
47
43
  t1:
44
+ title: Main Flow Trigger
48
45
  type: trigger
49
46
  stats:
50
47
  id: '{$self.input.data.workflowId}'
@@ -58,6 +55,7 @@ const getWorkflowYAML = (app: string, version: string) => {
58
55
  done: false
59
56
 
60
57
  a1:
58
+ title: Main Flow Pivot - All Cycling Descendants Point Here
61
59
  type: hook
62
60
  cycle: true
63
61
  output:
@@ -70,6 +68,7 @@ const getWorkflowYAML = (app: string, version: string) => {
70
68
  duration: '{t1.output.data.backoffCoefficient}'
71
69
 
72
70
  w1:
71
+ title: Main Worker - Calls Workflow Functions
73
72
  type: worker
74
73
  topic: '{t1.output.data.workflowTopic}'
75
74
  emit: '{$job.data.done}'
@@ -131,8 +130,8 @@ const getWorkflowYAML = (app: string, version: string) => {
131
130
  done: '{$self.output.data.done}'
132
131
 
133
132
  a2:
134
- type: hook
135
133
  title: Wait for cleanup signal
134
+ type: hook
136
135
  hook:
137
136
  type: object
138
137
  properties:
@@ -142,6 +141,209 @@ const getWorkflowYAML = (app: string, version: string) => {
142
141
  maps:
143
142
  workflowId: '{t1.output.data.workflowId}'
144
143
 
144
+ sig:
145
+ title: Signal In - Receive signals
146
+ type: hook
147
+ hook:
148
+ type: object
149
+ properties:
150
+ id:
151
+ type: string
152
+ arguments:
153
+ type: array
154
+ workflowTopic:
155
+ type: string
156
+ job:
157
+ maps:
158
+ workflowId: '{t1.output.data.workflowId}'
159
+
160
+ siga1:
161
+ title: Signal In Flow Pivot - Cycling Descendants Point Here
162
+ type: hook
163
+ cycle: true
164
+ output:
165
+ schema:
166
+ type: object
167
+ properties:
168
+ duration:
169
+ type: number
170
+ maps:
171
+ duration: '{t1.output.data.backoffCoefficient}'
172
+
173
+ sigw1:
174
+ title: Signal In - Worker
175
+ type: worker
176
+ topic: '{sig.hook.data.workflowTopic}'
177
+ retry:
178
+ '599': [2]
179
+ input:
180
+ schema:
181
+ type: object
182
+ properties:
183
+ workflowId:
184
+ type: string
185
+ workflowDimension:
186
+ type: string
187
+ arguments:
188
+ type: array
189
+ maps:
190
+ workflowId: '{t1.output.data.workflowId}'
191
+ workflowDimension: '{sig.output.metadata.dad}'
192
+ arguments: '{sig.hook.data.arguments}'
193
+ output:
194
+ schema:
195
+ type: object
196
+ 594:
197
+ schema:
198
+ type: object
199
+ properties:
200
+ index:
201
+ type: number
202
+ description: the index of the first signal in the array
203
+ signals:
204
+ type: array
205
+ description: remaining signal ids
206
+ items:
207
+ type: object
208
+ properties:
209
+ signal:
210
+ type: string
211
+ index:
212
+ type: number
213
+ maps:
214
+ index: '{$self.output.data.index}'
215
+ signals: '{$self.output.data.signals}'
216
+ 595:
217
+ schema:
218
+ type: object
219
+ properties:
220
+ duration:
221
+ type: number
222
+ description: sleep duration in seconds
223
+ index:
224
+ type: number
225
+ description: the current index
226
+ maps:
227
+ duration: '{$self.output.data.duration}'
228
+ index: '{$self.output.data.index}'
229
+
230
+ siga594:
231
+ title: Signal In - Wait for signals
232
+ type: await
233
+ topic: ${app}.wfsc.execute
234
+ input:
235
+ schema:
236
+ type: object
237
+ properties:
238
+ index:
239
+ type: number
240
+ signals:
241
+ type: array
242
+ description: signal ids
243
+ items:
244
+ type: object
245
+ properties:
246
+ signal:
247
+ type: string
248
+ index:
249
+ type: number
250
+ parentWorkflowId:
251
+ type: string
252
+ cycleWorkflowId:
253
+ type: string
254
+ baseWorkflowId:
255
+ type: string
256
+ description: index will be appended later
257
+ maps:
258
+ signals: '{sigw1.output.data.signals}'
259
+ parentWorkflowId:
260
+ '@pipe':
261
+ - ['{$job.metadata.jid}', '-w']
262
+ - ['{@string.concat}']
263
+ cycleWorkflowId:
264
+ '@pipe':
265
+ - ['{$job.metadata.jid}', '-$wfc', '{sig.output.metadata.dad}', '-', '{sigw1.output.data.index}']
266
+ - ['{@string.concat}']
267
+ baseWorkflowId:
268
+ '@pipe':
269
+ - ['{$job.metadata.jid}', '-$wfs', '{sig.output.metadata.dad}', '-']
270
+ - ['{@string.concat}']
271
+ output:
272
+ schema:
273
+ type: object
274
+ properties:
275
+ done:
276
+ type: boolean
277
+ maps:
278
+ done: '{sigw1.output.data.done}'
279
+
280
+ sigc594:
281
+ title: Signal In - Goto Activity siga1
282
+ type: cycle
283
+ ancestor: siga1
284
+ input:
285
+ maps:
286
+ duration: '{siga1.output.data.duration}'
287
+
288
+ siga595:
289
+ title: Signal In - Sleep before trying again
290
+ type: await
291
+ topic: ${app}.sleep.execute
292
+ input:
293
+ schema:
294
+ type: object
295
+ properties:
296
+ duration:
297
+ type: number
298
+ index:
299
+ type: number
300
+ workflowId:
301
+ type: string
302
+ parentWorkflowId:
303
+ type: string
304
+ maps:
305
+ duration: '{sigw1.output.data.duration}'
306
+ index: '{sigw1.output.data.index}'
307
+ parentWorkflowId:
308
+ '@pipe':
309
+ - ['{$job.metadata.jid}', '-s']
310
+ - ['{@string.concat}']
311
+ workflowId:
312
+ '@pipe':
313
+ - ['{$job.metadata.jid}', '-$sleep', '{sig.output.metadata.dad}', '-', '{sigw1.output.data.index}']
314
+ - ['{@string.concat}']
315
+ output:
316
+ schema:
317
+ type: object
318
+ properties:
319
+ done:
320
+ type: boolean
321
+ maps:
322
+ done: '{sigw1.output.data.done}'
323
+
324
+ sigc595:
325
+ title: Signal In - Goto Activity siga1
326
+ type: cycle
327
+ ancestor: siga1
328
+ input:
329
+ maps:
330
+ duration: '{siga1.output.data.duration}'
331
+
332
+ siga599:
333
+ title: Signal In - Sleep exponentially longer and retry
334
+ type: hook
335
+ sleep: '{siga1.output.data.duration}'
336
+
337
+ sigc599:
338
+ title: Signal In - Goto Activity siga1
339
+ type: cycle
340
+ ancestor: siga1
341
+ input:
342
+ maps:
343
+ duration:
344
+ '@pipe':
345
+ - ['{siga1.output.data.duration}', '{t1.output.data.backoffCoefficient}']
346
+ - ['{@math.multiply}']
145
347
 
146
348
  a594:
147
349
  title: Wait for signals
@@ -374,7 +576,7 @@ const getWorkflowYAML = (app: string, version: string) => {
374
576
  done: true
375
577
 
376
578
  s4:
377
- title: Awaken child FLOWS so they end and self-clean
579
+ title: Awaken child flows so they end and self-clean
378
580
  type: signal
379
581
  subtype: all
380
582
  key_name: parentWorkflowId
@@ -409,6 +611,19 @@ const getWorkflowYAML = (app: string, version: string) => {
409
611
  type: boolean
410
612
  maps:
411
613
  done: true
614
+ s5:
615
+ title: Close Signal In Channel
616
+ type: signal
617
+ subtype: one
618
+ topic: ${app}.flow.signal
619
+ signal:
620
+ schema:
621
+ type: object
622
+ properties:
623
+ id:
624
+ type: string
625
+ maps:
626
+ id: '{$job.metadata.jid}'
412
627
 
413
628
  transitions:
414
629
  t1:
@@ -421,6 +636,33 @@ const getWorkflowYAML = (app: string, version: string) => {
421
636
  '@pipe':
422
637
  - ['{$job.metadata.key}', true, false]
423
638
  - ['{@conditional.ternary}']
639
+ - to: sig
640
+ sig:
641
+ - to: siga1
642
+ conditions:
643
+ code: 202
644
+ siga1:
645
+ - to: sigw1
646
+ sigw1:
647
+ - to: siga594
648
+ conditions:
649
+ code: 594
650
+ - to: siga595
651
+ conditions:
652
+ code: 595
653
+ - to: siga599
654
+ conditions:
655
+ code: 599
656
+ siga594:
657
+ - to: sigc594
658
+ conditions:
659
+ code: 202
660
+ siga595:
661
+ - to: sigc595
662
+ conditions:
663
+ code: 202
664
+ siga599:
665
+ - to: sigc599
424
666
  a1:
425
667
  - to: w1
426
668
  w1:
@@ -433,16 +675,19 @@ const getWorkflowYAML = (app: string, version: string) => {
433
675
  - to: a599
434
676
  conditions:
435
677
  code: 599
678
+ - to: s3
679
+ conditions:
680
+ code: [200, 598, 597, 596]
436
681
  - to: s1
437
682
  conditions:
438
683
  code: [200, 598, 597, 596]
439
684
  - to: s2
440
685
  conditions:
441
686
  code: [200, 598, 597, 596]
442
- - to: s3
687
+ - to: s4
443
688
  conditions:
444
689
  code: [200, 598, 597, 596]
445
- - to: s4
690
+ - to: s5
446
691
  conditions:
447
692
  code: [200, 598, 597, 596]
448
693
  a594:
@@ -463,7 +708,14 @@ const getWorkflowYAML = (app: string, version: string) => {
463
708
  match:
464
709
  - expected: '{t1.output.data.workflowId}'
465
710
  actual: '{$self.hook.data.id}'
466
-
711
+
712
+ ${app}.flow.signal:
713
+ - to: sig
714
+ conditions:
715
+ match:
716
+ - expected: '{t1.output.data.workflowId}'
717
+ actual: '{$self.hook.data.id}'
718
+
467
719
  - subscribes: ${app}.activity.execute
468
720
  publishes: ${app}.activity.executed
469
721
 
@@ -494,6 +746,7 @@ const getWorkflowYAML = (app: string, version: string) => {
494
746
 
495
747
  activities:
496
748
  t1a:
749
+ title: Activity Flow Trigger
497
750
  type: trigger
498
751
  stats:
499
752
  id: '{$self.input.data.workflowId}'
@@ -504,6 +757,7 @@ const getWorkflowYAML = (app: string, version: string) => {
504
757
  target: '{$self.input.data.parentWorkflowId}'
505
758
 
506
759
  w1a:
760
+ title: Activity Worker - Calls Activity Functions
507
761
  type: worker
508
762
  topic: '{t1a.output.data.workflowTopic}'
509
763
  emit: true
@@ -539,8 +793,8 @@ const getWorkflowYAML = (app: string, version: string) => {
539
793
  done: true
540
794
 
541
795
  s1a:
796
+ title: Awaken activity flows so they end and self-clean
542
797
  type: hook
543
- title: Wait for cleanup signal
544
798
  hook:
545
799
  type: object
546
800
  properties:
@@ -559,7 +813,6 @@ const getWorkflowYAML = (app: string, version: string) => {
559
813
  hooks:
560
814
  ${app}.activity.awaken:
561
815
  - to: s1a
562
- keep_alive: true
563
816
  conditions:
564
817
  match:
565
818
  - expected: '{t1a.output.data.workflowId}'
@@ -596,6 +849,7 @@ const getWorkflowYAML = (app: string, version: string) => {
596
849
 
597
850
  activities:
598
851
  t1s:
852
+ title: Sleep Flow Trigger
599
853
  type: trigger
600
854
  stats:
601
855
  id: '{$self.input.data.workflowId}'
@@ -606,14 +860,14 @@ const getWorkflowYAML = (app: string, version: string) => {
606
860
  target: '{$self.input.data.parentWorkflowId}'
607
861
 
608
862
  a1s:
609
- type: hook
610
863
  title: Sleep for a duration
864
+ type: hook
611
865
  sleep: '{t1s.output.data.duration}'
612
866
  emit: true
613
867
 
614
868
  a2s:
869
+ title: Awaken sleep flows so they end and self-clean
615
870
  type: hook
616
- title: Wait for cleanup signal
617
871
  hook:
618
872
  type: object
619
873
  properties:
@@ -683,7 +937,7 @@ const getWorkflowYAML = (app: string, version: string) => {
683
937
  id: '{$self.input.data.cycleWorkflowId}'
684
938
 
685
939
  a1wc:
686
- title: Split signal data
940
+ title: Pivot - All Cycling Descendants Point Here
687
941
  type: hook
688
942
  cycle: true
689
943
  output:
@@ -722,6 +976,7 @@ const getWorkflowYAML = (app: string, version: string) => {
722
976
  - ['{t1wc.output.data.signals}', 1]
723
977
  - ['{@array.slice}']
724
978
  a2wc:
979
+ title: Precalculate targetLength
725
980
  type: hook
726
981
  output:
727
982
  schema:
@@ -733,7 +988,7 @@ const getWorkflowYAML = (app: string, version: string) => {
733
988
  targetLength: '{a1wc.output.data.targetLength}'
734
989
 
735
990
  c1wc:
736
- title: Goto Activity a1wc
991
+ title: Goto Activity a1wc - Spawn Signal children
737
992
  type: cycle
738
993
  ancestor: a1wc
739
994
  input:
@@ -839,6 +1094,7 @@ const getWorkflowYAML = (app: string, version: string) => {
839
1094
 
840
1095
  activities:
841
1096
  t1ww:
1097
+ title: WFS - Wait For Signal Trigger
842
1098
  type: trigger
843
1099
  stats:
844
1100
  id: '{$self.input.data.workflowId}'
@@ -849,8 +1105,8 @@ const getWorkflowYAML = (app: string, version: string) => {
849
1105
  target: '{$self.input.data.parentWorkflowId}'
850
1106
 
851
1107
  a1ww:
1108
+ title: WFS - signal entry point
852
1109
  type: hook
853
- title: Wait for custom signal
854
1110
  emit: true
855
1111
  hook:
856
1112
  type: object
@@ -864,8 +1120,8 @@ const getWorkflowYAML = (app: string, version: string) => {
864
1120
  signalId: '{t1ww.output.data.signalId}'
865
1121
 
866
1122
  a2ww:
1123
+ title: WFS - cleanup signal entry point
867
1124
  type: hook
868
- title: Wait for cleanup signal
869
1125
  hook:
870
1126
  type: object
871
1127
  properties:
@@ -900,39 +1156,11 @@ const getWorkflowYAML = (app: string, version: string) => {
900
1156
 
901
1157
  const APP_VERSION = '1';
902
1158
  const APP_ID = 'durable';
903
- const ACTIVITY_SUBSCRIBES_TOPIC = 'durable.activity.execute';
904
- const ACTIVITY_PUBLISHES_TOPIC = 'durable.activity.executed';
905
- const SLEEP_SUBSCRIBES_TOPIC = 'durable.sleep.execute';
906
- const SLEEP_PUBLISHES_TOPIC = 'durable.sleep.executed';
907
- const WFS_SUBSCRIBES_TOPIC = 'durable.wfs.execute';
908
- const WFS_PUBLISHES_TOPIC = 'durable.wfs.executed';
909
- const WFSC_SUBSCRIBES_TOPIC = 'durable.wfsc.execute';
910
- const WFSC_PUBLISHES_TOPIC = 'durable.wfsc.executed';
911
- const SUBSCRIBES_TOPIC = 'durable.execute';
912
- const PUBLISHES_TOPIC = 'durable.executed';
913
- const HOOK_ID = 'durable.awaken';
914
- const ACTIVITY_HOOK_ID = 'durable.activity.awaken';
915
- const SLEEP_HOOK_ID = 'durable.sleep.awaken';
916
- const WFS_HOOK_ID = 'durable.wfs.awaken';
917
1159
  const DEFAULT_COEFFICIENT = 10;
918
1160
 
919
1161
  export {
920
1162
  getWorkflowYAML,
921
1163
  APP_VERSION,
922
1164
  APP_ID,
923
- ACTIVITY_SUBSCRIBES_TOPIC,
924
- ACTIVITY_PUBLISHES_TOPIC,
925
- SLEEP_SUBSCRIBES_TOPIC,
926
- SLEEP_PUBLISHES_TOPIC,
927
- SUBSCRIBES_TOPIC,
928
- PUBLISHES_TOPIC,
929
- HOOK_ID,
930
- ACTIVITY_HOOK_ID,
931
- SLEEP_HOOK_ID,
932
1165
  DEFAULT_COEFFICIENT,
933
- WFS_SUBSCRIBES_TOPIC,
934
- WFS_PUBLISHES_TOPIC,
935
- WFSC_SUBSCRIBES_TOPIC,
936
- WFSC_PUBLISHES_TOPIC,
937
- WFS_HOOK_ID,
938
1166
  };
@@ -1,6 +1,5 @@
1
1
  import { JobOutput } from '../../types/job';
2
2
  import { HotMeshService as HotMesh } from '../hotmesh';
3
- import { PUBLISHES_TOPIC } from './factory';
4
3
 
5
4
  export class WorkflowHandleService {
6
5
  hotMesh: HotMesh;
@@ -14,12 +13,26 @@ export class WorkflowHandleService {
14
13
  }
15
14
 
16
15
  async signal(signalId: string, data: Record<any, any>): Promise<void> {
17
- await this.hotMesh.hook('durable.wfs.signal', { id: signalId, data });
16
+ await this.hotMesh.hook(`${this.hotMesh.appId}.wfs.signal`, { id: signalId, data });
18
17
  }
19
18
 
20
- async result(): Promise<any> {
19
+ async result(loadState?: boolean): Promise<any> {
20
+ if (loadState) {
21
+ const state = await this.hotMesh.getState(`${this.hotMesh.appId}.execute`, this.workflowId);
22
+ if (!state.data && state.metadata.err) {
23
+ throw new Error(JSON.parse(state.metadata.err));
24
+ }
25
+ if (state?.data?.done) {
26
+ //child flows are never technically 'done' as they have an open hook
27
+ //that is tied to the parent flow's completion. so, we need to check
28
+ //the 'done' flag on the child flow's payload (not the 'js' metadata field
29
+ //which is typically used); the loadState parameter ensures this
30
+ //check happens early
31
+ return state.data.response;
32
+ }
33
+ }
21
34
  let status = await this.hotMesh.getStatus(this.workflowId);
22
- const topic = `${PUBLISHES_TOPIC}.${this.workflowId}`;
35
+ const topic = `${this.hotMesh.appId}.executed.${this.workflowId}`;
23
36
 
24
37
  return new Promise((resolve, reject) => {
25
38
  let isResolved = false;
@@ -31,7 +44,7 @@ export class WorkflowHandleService {
31
44
  if (err) {
32
45
  return reject(JSON.parse(err));
33
46
  } else if (!response) {
34
- const state = await this.hotMesh.getState(this.workflowTopic, this.workflowId);
47
+ const state = await this.hotMesh.getState(`${this.hotMesh.appId}.execute`, this.workflowId);
35
48
  if (!state.data && state.metadata.err) {
36
49
  return reject(JSON.parse(state.metadata.err));
37
50
  }
@@ -5,6 +5,8 @@ import { KeyService, KeyType } from '../../modules/key';
5
5
 
6
6
  export class Search {
7
7
  jobId: string;
8
+ searchSessionId: string;
9
+ searchSessionIndex: number = 0;
8
10
  hotMeshClient: HotMesh;
9
11
  store: StoreService<RedisClient, RedisMulti> | null;
10
12
 
@@ -14,19 +16,34 @@ export class Search {
14
16
  return `_${key}`;
15
17
  }
16
18
 
17
- constructor(workflowId: string, hotMeshClient: HotMesh) {
19
+ constructor(workflowId: string, hotMeshClient: HotMesh, searchSessionId: string) {
18
20
  const keyParams = {
19
21
  appId: hotMeshClient.appId,
20
22
  jobId: ''
21
23
  }
22
24
  const hotMeshPrefix = KeyService.mintKey(hotMeshClient.namespace, KeyType.JOB_STATE, keyParams);
23
25
  this.jobId = `${hotMeshPrefix}${workflowId}`;
26
+ this.searchSessionId = searchSessionId;
24
27
  this.hotMeshClient = hotMeshClient;
25
28
  this.store = hotMeshClient.engine.store as StoreService<RedisClient, RedisMulti>;
26
29
  }
27
30
 
31
+ /**
32
+ * increments the index to return a unique search session guid when
33
+ * calling any method that produces side effects (changes the value)
34
+ */
35
+ getSearchSessionGuid(): string {
36
+ //return the search session as it would exist in the search session index
37
+ return `${this.searchSessionId}-${this.searchSessionIndex++}-`;
38
+ }
39
+
28
40
  async set(key: string, value: string): Promise<void> {
29
- await this.store.exec('HSET', this.jobId, this.safeKey(key), value.toString());
41
+ const ssGuid = this.getSearchSessionGuid();
42
+ const ssGuidValue = Number(await this.store.exec('HINCRBYFLOAT', this.jobId, ssGuid, '1') as string);
43
+ if (ssGuidValue === 1) {
44
+ //only allowed to set a value the first time
45
+ await this.store.exec('HSET', this.jobId, this.safeKey(key), value.toString());
46
+ }
30
47
  }
31
48
 
32
49
  async get(key: string): Promise<string> {
@@ -39,16 +56,28 @@ export class Search {
39
56
  }
40
57
 
41
58
  async del(key: string): Promise<void> {
42
- await this.store.exec('HDEL', this.jobId, this.safeKey(key));
59
+ const ssGuid = this.getSearchSessionGuid();
60
+ const ssGuidValue = Number(await this.store.exec('HINCRBYFLOAT', this.jobId, ssGuid, '1') as string);
61
+ if (ssGuidValue === 1) {
62
+ await this.store.exec('HDEL', this.jobId, this.safeKey(key));
63
+ }
43
64
  }
44
65
 
45
66
  async incr(key: string, val: number): Promise<number> {
46
- return Number(await this.store.exec('HINCRBYFLOAT', this.jobId, this.safeKey(key), val.toString()) as string);
67
+ const ssGuid = this.getSearchSessionGuid();
68
+ const ssGuidValue = Number(await this.store.exec('HINCRBYFLOAT', this.jobId, ssGuid, '1') as string);
69
+ if (ssGuidValue === 1) {
70
+ return Number(await this.store.exec('HINCRBYFLOAT', this.jobId, this.safeKey(key), val.toString()) as string);
71
+ }
47
72
  }
48
73
 
49
74
  async mult(key: string, val: number): Promise<number> {
50
- const log = Math.log(val);
51
- const logTotal = Number(await this.store.exec('HINCRBYFLOAT', this.jobId, this.safeKey(key), log.toString()) as string);
52
- return Math.exp(logTotal);
75
+ const ssGuid = this.getSearchSessionGuid();
76
+ const ssGuidValue = Number(await this.store.exec('HINCRBYFLOAT', this.jobId, ssGuid, '1') as string);
77
+ if (ssGuidValue === 1) {
78
+ const log = Math.log(val);
79
+ const logTotal = Number(await this.store.exec('HINCRBYFLOAT', this.jobId, this.safeKey(key), log.toString()) as string);
80
+ return Math.exp(logTotal);
81
+ }
53
82
  }
54
83
  }