@hotmeshio/hotmesh 0.0.36 → 0.0.38

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 (89) hide show
  1. package/README.md +11 -11
  2. package/build/modules/enums.d.ts +1 -0
  3. package/build/modules/enums.js +3 -1
  4. package/build/modules/errors.d.ts +9 -1
  5. package/build/modules/errors.js +12 -1
  6. package/build/modules/key.d.ts +20 -19
  7. package/build/modules/key.js +20 -20
  8. package/build/package.json +1 -1
  9. package/build/services/activities/activity.d.ts +10 -0
  10. package/build/services/activities/activity.js +28 -3
  11. package/build/services/activities/await.js +10 -9
  12. package/build/services/activities/cycle.js +10 -9
  13. package/build/services/activities/hook.d.ts +7 -1
  14. package/build/services/activities/hook.js +61 -44
  15. package/build/services/activities/interrupt.js +10 -9
  16. package/build/services/activities/signal.js +7 -7
  17. package/build/services/activities/trigger.js +4 -2
  18. package/build/services/activities/worker.js +9 -8
  19. package/build/services/durable/meshos.js +2 -2
  20. package/build/services/durable/worker.js +2 -2
  21. package/build/services/durable/workflow.js +17 -17
  22. package/build/services/engine/index.d.ts +5 -7
  23. package/build/services/engine/index.js +53 -47
  24. package/build/services/hotmesh/index.d.ts +2 -2
  25. package/build/services/hotmesh/index.js +6 -7
  26. package/build/services/quorum/index.d.ts +6 -6
  27. package/build/services/quorum/index.js +47 -11
  28. package/build/services/{signaler/stream.d.ts → router/index.d.ts} +3 -3
  29. package/build/services/{signaler/stream.js → router/index.js} +6 -6
  30. package/build/services/serializer/index.js +1 -1
  31. package/build/services/store/clients/ioredis.d.ts +1 -0
  32. package/build/services/store/clients/ioredis.js +9 -0
  33. package/build/services/store/clients/redis.d.ts +1 -0
  34. package/build/services/store/clients/redis.js +16 -0
  35. package/build/services/store/index.d.ts +10 -4
  36. package/build/services/store/index.js +21 -10
  37. package/build/services/stream/clients/ioredis.d.ts +1 -0
  38. package/build/services/stream/clients/ioredis.js +33 -24
  39. package/build/services/stream/clients/redis.d.ts +1 -0
  40. package/build/services/stream/clients/redis.js +15 -0
  41. package/build/services/stream/index.d.ts +1 -0
  42. package/build/services/task/index.d.ts +13 -4
  43. package/build/services/task/index.js +115 -17
  44. package/build/services/telemetry/index.js +6 -6
  45. package/build/services/worker/index.d.ts +4 -3
  46. package/build/services/worker/index.js +32 -8
  47. package/build/types/job.d.ts +2 -0
  48. package/build/types/quorum.d.ts +11 -1
  49. package/build/types/redisclient.d.ts +1 -0
  50. package/build/types/stream.d.ts +1 -0
  51. package/modules/enums.ts +3 -0
  52. package/modules/errors.ts +18 -0
  53. package/modules/key.ts +21 -20
  54. package/package.json +1 -1
  55. package/services/activities/activity.ts +44 -4
  56. package/services/activities/await.ts +14 -10
  57. package/services/activities/cycle.ts +14 -10
  58. package/services/activities/hook.ts +70 -47
  59. package/services/activities/interrupt.ts +13 -10
  60. package/services/activities/signal.ts +11 -8
  61. package/services/activities/trigger.ts +5 -1
  62. package/services/activities/worker.ts +13 -9
  63. package/services/durable/meshos.ts +1 -1
  64. package/services/durable/worker.ts +1 -1
  65. package/services/durable/workflow.ts +1 -1
  66. package/services/engine/index.ts +82 -44
  67. package/services/hotmesh/index.ts +7 -8
  68. package/services/quorum/index.ts +48 -12
  69. package/services/{signaler/stream.ts → router/index.ts} +5 -5
  70. package/services/serializer/index.ts +1 -1
  71. package/services/store/clients/ioredis.ts +9 -0
  72. package/services/store/clients/redis.ts +16 -0
  73. package/services/store/index.ts +27 -12
  74. package/services/stream/clients/ioredis.ts +33 -24
  75. package/services/stream/clients/redis.ts +14 -0
  76. package/services/stream/index.ts +1 -0
  77. package/services/task/index.ts +120 -21
  78. package/services/telemetry/index.ts +6 -6
  79. package/services/worker/index.ts +37 -7
  80. package/types/job.ts +2 -0
  81. package/types/quorum.ts +15 -4
  82. package/types/redisclient.ts +1 -0
  83. package/types/stream.ts +6 -5
  84. package/build/services/signaler/store.d.ts +0 -15
  85. package/build/services/signaler/store.js +0 -68
  86. package/services/signaler/store.ts +0 -76
  87. /package/build/{services/durable/asyncLocalStorage.d.ts → modules/storage.d.ts} +0 -0
  88. /package/build/{services/durable/asyncLocalStorage.js → modules/storage.js} +0 -0
  89. /package/{services/durable/asyncLocalStorage.ts → modules/storage.ts} +0 -0
@@ -10,9 +10,8 @@ const utils_1 = require("../../modules/utils");
10
10
  const activities_1 = __importDefault(require("../activities"));
11
11
  const compiler_1 = require("../compiler");
12
12
  const reporter_1 = require("../reporter");
13
+ const router_1 = require("../router");
13
14
  const serializer_1 = require("../serializer");
14
- const store_1 = require("../signaler/store");
15
- const stream_1 = require("../signaler/stream");
16
15
  const redis_1 = require("../store/clients/redis");
17
16
  const ioredis_1 = require("../store/clients/ioredis");
18
17
  const redis_2 = require("../stream/clients/redis");
@@ -20,7 +19,7 @@ const ioredis_2 = require("../stream/clients/ioredis");
20
19
  const ioredis_3 = require("../sub/clients/ioredis");
21
20
  const redis_3 = require("../sub/clients/redis");
22
21
  const task_1 = require("../task");
23
- const stream_2 = require("../../types/stream");
22
+ const stream_1 = require("../../types/stream");
24
23
  class EngineService {
25
24
  constructor() {
26
25
  this.cacheMode = 'cache';
@@ -40,13 +39,10 @@ class EngineService {
40
39
  await instance.initStoreChannel(config.engine.store);
41
40
  await instance.initSubChannel(config.engine.sub);
42
41
  await instance.initStreamChannel(config.engine.stream);
43
- instance.streamSignaler = instance.initStreamSignaler(config);
44
- instance.streamSignaler.consumeMessages(instance.stream.mintKey(key_1.KeyType.STREAMS, { appId: instance.appId }), 'ENGINE', instance.guid, instance.processStreamMessage.bind(instance));
45
- //the storeSignaler service is used by the engine to create `webhooks`
46
- //todo: unify/move to the task service (it manages all `signal` types)
47
- instance.storeSignaler = new store_1.StoreSignaler(instance.store, logger);
42
+ instance.router = instance.initRouter(config);
43
+ instance.router.consumeMessages(instance.stream.mintKey(key_1.KeyType.STREAMS, { appId: instance.appId }), 'ENGINE', instance.guid, instance.processStreamMessage.bind(instance));
48
44
  //the task service is used by the engine to process `webhooks` and `timehooks`
49
- instance.task = new task_1.TaskService(instance.store, logger);
45
+ instance.taskService = new task_1.TaskService(instance.store, logger);
50
46
  return instance;
51
47
  }
52
48
  }
@@ -84,12 +80,12 @@ class EngineService {
84
80
  }
85
81
  await this.stream.init(this.namespace, this.appId, this.logger);
86
82
  }
87
- initStreamSignaler(config) {
88
- return new stream_1.StreamSignaler({
83
+ initRouter(config) {
84
+ return new router_1.Router({
89
85
  namespace: this.namespace,
90
86
  appId: this.appId,
91
87
  guid: this.guid,
92
- role: stream_2.StreamRole.ENGINE,
88
+ role: stream_1.StreamRole.ENGINE,
93
89
  reclaimDelay: config.engine.reclaimDelay,
94
90
  reclaimCount: config.engine.reclaimCount,
95
91
  }, this.stream, this.store, this.logger);
@@ -128,18 +124,17 @@ class EngineService {
128
124
  }
129
125
  }
130
126
  async processWebHooks() {
131
- this.task.processWebHooks((this.hook).bind(this));
127
+ this.taskService.processWebHooks((this.hook).bind(this));
132
128
  }
133
129
  async processTimeHooks() {
134
- this.task.processTimeHooks((this.hookTime).bind(this));
130
+ this.taskService.processTimeHooks((this.hookTime).bind(this));
135
131
  }
136
132
  async throttle(delayInMillis) {
137
- this.streamSignaler.setThrottle(delayInMillis);
133
+ this.router.setThrottle(delayInMillis);
138
134
  }
139
135
  // ************* METADATA/MODEL METHODS *************
140
136
  async initActivity(topic, data = {}, context) {
141
137
  const [activityId, schema] = await this.getSchema(topic);
142
- utils_1.polyfill;
143
138
  const ActivityHandler = activities_1.default[utils_1.polyfill.resolveActivityType(schema.type)];
144
139
  if (ActivityHandler) {
145
140
  const utc = (0, utils_1.formatISODate)(new Date());
@@ -222,37 +217,43 @@ class EngineService {
222
217
  async processStreamMessage(streamData) {
223
218
  this.logger.debug('engine-process-stream-message', {
224
219
  jid: streamData.metadata.jid,
220
+ gid: streamData.metadata.gid,
225
221
  dad: streamData.metadata.dad,
226
222
  aid: streamData.metadata.aid,
227
- status: streamData.status || stream_2.StreamStatus.SUCCESS,
223
+ status: streamData.status || stream_1.StreamStatus.SUCCESS,
228
224
  code: streamData.code || 200,
229
225
  type: streamData.type,
230
226
  });
231
227
  const context = {
232
228
  metadata: {
233
229
  jid: streamData.metadata.jid,
230
+ gid: streamData.metadata.gid,
234
231
  dad: streamData.metadata.dad,
235
232
  aid: streamData.metadata.aid,
236
233
  },
237
234
  data: streamData.data,
238
235
  };
239
- if (streamData.type === stream_2.StreamDataType.TIMEHOOK || streamData.type === stream_2.StreamDataType.WEBHOOK || streamData.type === stream_2.StreamDataType.TRANSITION) {
236
+ if (streamData.type === stream_1.StreamDataType.TIMEHOOK) {
237
+ //TIMEHOOK AWAKEN
240
238
  const activityHandler = await this.initActivity(`.${streamData.metadata.aid}`, context.data, context);
241
- if (streamData.type === stream_2.StreamDataType.TIMEHOOK) {
242
- await activityHandler.processTimeHookEvent(streamData.metadata.jid);
243
- }
244
- else if (streamData.type === stream_2.StreamDataType.TRANSITION) {
245
- await activityHandler.process();
246
- }
247
- else {
248
- //a 202 code keeps the hook alive (hooks are single-use by default)
249
- await activityHandler.processWebHookEvent(streamData.status, streamData.code);
250
- }
239
+ await activityHandler.processTimeHookEvent(streamData.metadata.jid);
240
+ }
241
+ else if (streamData.type === stream_1.StreamDataType.WEBHOOK) {
242
+ //WEBHOOK AWAKEN (SIGNAL IN)
243
+ const activityHandler = await this.initActivity(`.${streamData.metadata.aid}`, context.data, context);
244
+ await activityHandler.processWebHookEvent(streamData.status, streamData.code);
245
+ }
246
+ else if (streamData.type === stream_1.StreamDataType.TRANSITION) {
247
+ //TRANSITION (ADJACENT ACTIVITY)
248
+ const activityHandler = await this.initActivity(`.${streamData.metadata.aid}`, context.data, context); //todo: `as Activity` (type is more generic)
249
+ await activityHandler.process();
251
250
  }
252
- else if (streamData.type === stream_2.StreamDataType.AWAIT) {
251
+ else if (streamData.type === stream_1.StreamDataType.AWAIT) {
252
+ //TRIGGER JOB
253
253
  context.metadata = {
254
254
  ...context.metadata,
255
255
  pj: streamData.metadata.jid,
256
+ pg: streamData.metadata.gid,
256
257
  pd: streamData.metadata.dad,
257
258
  pa: streamData.metadata.aid,
258
259
  trc: streamData.metadata.trc,
@@ -261,16 +262,19 @@ class EngineService {
261
262
  const activityHandler = await this.initActivity(streamData.metadata.topic, streamData.data, context);
262
263
  await activityHandler.process();
263
264
  }
264
- else if (streamData.type === stream_2.StreamDataType.RESULT) {
265
+ else if (streamData.type === stream_1.StreamDataType.RESULT) {
266
+ //AWAIT RESULT
265
267
  const activityHandler = await this.initActivity(`.${context.metadata.aid}`, streamData.data, context);
266
268
  await activityHandler.processEvent(streamData.status, streamData.code);
267
269
  }
268
270
  else {
271
+ //WORKER RESULT
269
272
  const activityHandler = await this.initActivity(`.${streamData.metadata.aid}`, streamData.data, context);
270
273
  await activityHandler.processEvent(streamData.status, streamData.code, 'output');
271
274
  }
272
275
  this.logger.debug('engine-process-stream-message-end', {
273
276
  jid: streamData.metadata.jid,
277
+ gid: streamData.metadata.gid,
274
278
  aid: streamData.metadata.aid
275
279
  });
276
280
  }
@@ -284,28 +288,29 @@ class EngineService {
284
288
  metadata: {
285
289
  guid: (0, utils_1.guid)(),
286
290
  jid: context.metadata.pj,
291
+ gid: context.metadata.pg,
287
292
  dad: context.metadata.pd,
288
293
  aid: context.metadata.pa,
289
294
  trc: context.metadata.trc,
290
295
  spn,
291
296
  },
292
- type: stream_2.StreamDataType.RESULT,
297
+ type: stream_1.StreamDataType.RESULT,
293
298
  data: jobOutput.data,
294
299
  };
295
300
  if (error && error.code) {
296
- streamData.status = stream_2.StreamStatus.ERROR;
301
+ streamData.status = stream_1.StreamStatus.ERROR;
297
302
  streamData.data = error;
298
303
  streamData.code = error.code;
299
304
  }
300
305
  else if (emit) {
301
- streamData.status = stream_2.StreamStatus.PENDING;
306
+ streamData.status = stream_1.StreamStatus.PENDING;
302
307
  streamData.code = enums_1.STATUS_CODE_PENDING;
303
308
  }
304
309
  else {
305
- streamData.status = stream_2.StreamStatus.SUCCESS;
310
+ streamData.status = stream_1.StreamStatus.SUCCESS;
306
311
  streamData.code = enums_1.STATUS_CODE_SUCCESS;
307
312
  }
308
- return (await this.streamSignaler?.publishMessage(null, streamData));
313
+ return (await this.router?.publishMessage(null, streamData));
309
314
  }
310
315
  }
311
316
  hasParentJob(context) {
@@ -332,11 +337,11 @@ class EngineService {
332
337
  await this.store.scrub(jobId);
333
338
  }
334
339
  // ****************** `HOOK` ACTIVITY RE-ENTRY POINT *****************
335
- async hook(topic, data, status = stream_2.StreamStatus.SUCCESS, code = 200) {
336
- const hookRule = await this.storeSignaler.getHookRule(topic);
340
+ async hook(topic, data, status = stream_1.StreamStatus.SUCCESS, code = 200) {
341
+ const hookRule = await this.taskService.getHookRule(topic);
337
342
  const [aid] = await this.getSchema(`.${hookRule.to}`);
338
343
  const streamData = {
339
- type: stream_2.StreamDataType.WEBHOOK,
344
+ type: stream_1.StreamDataType.WEBHOOK,
340
345
  status,
341
346
  code,
342
347
  metadata: {
@@ -346,9 +351,9 @@ class EngineService {
346
351
  },
347
352
  data,
348
353
  };
349
- return await this.streamSignaler.publishMessage(null, streamData);
354
+ return await this.router.publishMessage(null, streamData);
350
355
  }
351
- async hookTime(jobId, activityId, type) {
356
+ async hookTime(jobId, gId, activityId, type) {
352
357
  if (type === 'interrupt') {
353
358
  return await this.interrupt(activityId, //note: 'activityId' is the actually job topic
354
359
  jobId, { suppress: true, expire: 1 });
@@ -360,20 +365,21 @@ class EngineService {
360
365
  const [aid, ...dimensions] = activityId.split(',');
361
366
  const dad = `,${dimensions.join(',')}`;
362
367
  const streamData = {
363
- type: stream_2.StreamDataType.TIMEHOOK,
368
+ type: stream_1.StreamDataType.TIMEHOOK,
364
369
  metadata: {
365
370
  guid: (0, utils_1.guid)(),
366
371
  jid: jobId,
367
- aid,
372
+ gid: gId,
368
373
  dad,
374
+ aid,
369
375
  },
370
376
  data: { timestamp: Date.now() },
371
377
  };
372
- await this.streamSignaler.publishMessage(null, streamData);
378
+ await this.router.publishMessage(null, streamData);
373
379
  }
374
380
  async hookAll(hookTopic, data, keyResolver, queryFacets = []) {
375
381
  const config = await this.getVID();
376
- const hookRule = await this.storeSignaler.getHookRule(hookTopic);
382
+ const hookRule = await this.taskService.getHookRule(hookTopic);
377
383
  if (hookRule) {
378
384
  const subscriptionTopic = await (0, utils_1.getSubscriptionTopic)(hookRule.to, this.store, config);
379
385
  const resolvedQuery = await this.resolveQuery(subscriptionTopic, keyResolver);
@@ -485,7 +491,7 @@ class EngineService {
485
491
  }
486
492
  }
487
493
  async add(streamData) {
488
- return await this.streamSignaler.publishMessage(null, streamData);
494
+ return await this.router.publishMessage(null, streamData);
489
495
  }
490
496
  registerJobCallback(jobId, jobCallback) {
491
497
  this.jobCallbacks[jobId] = jobCallback;
@@ -510,7 +516,7 @@ class EngineService {
510
516
  this.pubPermSubs(context, jobOutput, options.emit);
511
517
  }
512
518
  if (!options.emit) {
513
- this.task.registerJobForCleanup(context.metadata.jid, this.resolveExpires(context, options), options);
519
+ this.taskService.registerJobForCleanup(context.metadata.jid, this.resolveExpires(context, options), options);
514
520
  }
515
521
  return msgId;
516
522
  }
@@ -520,7 +526,7 @@ class EngineService {
520
526
  * it will be expired immediately.
521
527
  */
522
528
  resolveExpires(context, options) {
523
- return context.metadata.expire ?? options.expire ?? enums_1.DURABLE_EXPIRE_SECONDS;
529
+ return options.expire ?? context.metadata.expire ?? enums_1.DURABLE_EXPIRE_SECONDS;
524
530
  }
525
531
  // ****** GET JOB STATE/COLLATION STATUS BY ID *********
526
532
  async getStatus(jobId) {
@@ -4,7 +4,7 @@ import { QuorumService } from '../quorum';
4
4
  import { WorkerService } from '../worker';
5
5
  import { JobState, JobData, JobOutput, JobStatus, JobInterruptOptions } from '../../types/job';
6
6
  import { HotMeshConfig, HotMeshManifest } from '../../types/hotmesh';
7
- import { JobMessageCallback } from '../../types/quorum';
7
+ import { JobMessageCallback, QuorumProfile } from '../../types/quorum';
8
8
  import { JobStatsInput, GetStatsOptions, IdsResponse, StatsResponse } from '../../types/stats';
9
9
  import { StreamCode, StreamData, StreamDataResponse, StreamStatus } from '../../types/stream';
10
10
  import { StringAnyType } from '../../types/serializer';
@@ -31,10 +31,10 @@ declare class HotMeshService {
31
31
  punsub(wild: string): Promise<void>;
32
32
  pubsub(topic: string, data?: JobData, context?: JobState | null, timeout?: number): Promise<JobOutput>;
33
33
  add(streamData: StreamData | StreamDataResponse): Promise<string>;
34
+ rollCall(delay?: number): Promise<QuorumProfile[]>;
34
35
  plan(path: string): Promise<HotMeshManifest>;
35
36
  deploy(pathOrYAML: string): Promise<HotMeshManifest>;
36
37
  activate(version: string, delay?: number): Promise<boolean>;
37
- inventory(version: string, delay?: number): Promise<number>;
38
38
  getStats(topic: string, query: JobStatsInput): Promise<StatsResponse>;
39
39
  getStatus(jobId: string): Promise<JobStatus>;
40
40
  getState(topic: string, jobId: string): Promise<JobOutput>;
@@ -7,8 +7,8 @@ const redis_1 = require("../connector/clients/redis");
7
7
  const ioredis_1 = require("../connector/clients/ioredis");
8
8
  const engine_1 = require("../engine");
9
9
  const logger_1 = require("../logger");
10
- const stream_1 = require("../signaler/stream");
11
10
  const quorum_1 = require("../quorum");
11
+ const router_1 = require("../router");
12
12
  const worker_1 = require("../worker");
13
13
  const connector_1 = require("../connector");
14
14
  class HotMeshService {
@@ -90,6 +90,9 @@ class HotMeshService {
90
90
  return await this.engine.add(streamData);
91
91
  }
92
92
  // ************* COMPILER METHODS *************
93
+ async rollCall(delay) {
94
+ return await this.quorum?.rollCall(delay);
95
+ }
93
96
  async plan(path) {
94
97
  return await this.engine?.plan(path);
95
98
  }
@@ -100,10 +103,6 @@ class HotMeshService {
100
103
  //activation is a quorum operation
101
104
  return await this.quorum?.activate(version, delay);
102
105
  }
103
- async inventory(version, delay) {
104
- //get count of all peers
105
- return await this.quorum?.inventory(delay);
106
- }
107
106
  // ************* REPORTER METHODS *************
108
107
  async getStats(topic, query) {
109
108
  return await this.engine?.getStats(topic, query);
@@ -141,13 +140,13 @@ class HotMeshService {
141
140
  static async stop() {
142
141
  if (!this.disconnecting) {
143
142
  this.disconnecting = true;
144
- await stream_1.StreamSignaler.stopConsuming();
143
+ await router_1.Router.stopConsuming();
145
144
  await redis_1.RedisConnection.disconnectAll();
146
145
  await ioredis_1.RedisConnection.disconnectAll();
147
146
  }
148
147
  }
149
148
  stop() {
150
- this.engine?.task.cancelCleanup();
149
+ this.engine?.taskService.cancelCleanup();
151
150
  }
152
151
  async compress(terms) {
153
152
  return await this.engine?.compress(terms);
@@ -3,15 +3,15 @@ import { ILogger } from '../logger';
3
3
  import { StoreService } from '../store';
4
4
  import { SubService } from '../sub';
5
5
  import { CacheMode } from '../../types/cache';
6
- import { QuorumMessageCallback, SubscriptionCallback, ThrottleMessage } from '../../types/quorum';
7
- import { HotMeshApps, HotMeshConfig } from '../../types/hotmesh';
6
+ import { QuorumMessageCallback, QuorumProfile, SubscriptionCallback, ThrottleMessage } from '../../types/quorum';
7
+ import { HotMeshConfig } from '../../types/hotmesh';
8
8
  import { RedisClient, RedisMulti } from '../../types/redis';
9
9
  declare class QuorumService {
10
10
  namespace: string;
11
- apps: HotMeshApps | null;
12
11
  appId: string;
13
12
  guid: string;
14
13
  engine: EngineService;
14
+ profiles: QuorumProfile[];
15
15
  store: StoreService<RedisClient, RedisMulti> | null;
16
16
  subscribe: SubService<RedisClient, RedisMulti> | null;
17
17
  logger: ILogger;
@@ -24,12 +24,12 @@ declare class QuorumService {
24
24
  initStoreChannel(store: RedisClient): Promise<void>;
25
25
  initSubChannel(sub: RedisClient): Promise<void>;
26
26
  subscriptionHandler(): SubscriptionCallback;
27
- sayPong(appId: string, guid: string, originator: string): Promise<void>;
28
- requestQuorum(delay?: number): Promise<number>;
27
+ sayPong(appId: string, guid: string, originator: string, details?: boolean): Promise<void>;
28
+ requestQuorum(delay?: number, details?: boolean): Promise<number>;
29
29
  pub(quorumMessage: ThrottleMessage): Promise<boolean>;
30
30
  sub(callback: QuorumMessageCallback): Promise<void>;
31
31
  unsub(callback: QuorumMessageCallback): Promise<void>;
32
- inventory(delay?: number): Promise<number>;
32
+ rollCall(delay?: number): Promise<QuorumProfile[]>;
33
33
  activate(version: string, delay?: number): Promise<boolean>;
34
34
  }
35
35
  export { QuorumService };
@@ -12,6 +12,7 @@ const redis_2 = require("../sub/clients/redis");
12
12
  const QUORUM_DELAY = 250;
13
13
  class QuorumService {
14
14
  constructor() {
15
+ this.profiles = [];
15
16
  this.cacheMode = 'cache';
16
17
  this.untilVersion = null;
17
18
  this.quorum = null;
@@ -68,10 +69,13 @@ class QuorumService {
68
69
  self.engine.setCacheMode(message.cache_mode, message.until_version);
69
70
  }
70
71
  else if (message.type === 'ping') {
71
- this.sayPong(self.appId, self.guid, message.originator);
72
+ self.sayPong(self.appId, self.guid, message.originator, message.details);
72
73
  }
73
74
  else if (message.type === 'pong' && self.guid === message.originator) {
74
75
  self.quorum = self.quorum + 1;
76
+ if (message.profile) {
77
+ self.profiles.push(message.profile);
78
+ }
75
79
  }
76
80
  else if (message.type === 'throttle') {
77
81
  self.engine.throttle(message.throttle);
@@ -91,13 +95,31 @@ class QuorumService {
91
95
  }
92
96
  };
93
97
  }
94
- async sayPong(appId, guid, originator) {
95
- this.store.publish(key_1.KeyType.QUORUM, { type: 'pong', guid, originator }, appId);
98
+ async sayPong(appId, guid, originator, details = false) {
99
+ let profile;
100
+ if (details) {
101
+ profile = {
102
+ engine_id: this.guid,
103
+ namespace: this.namespace,
104
+ app_id: this.appId,
105
+ stream: this.engine.stream.mintKey(key_1.KeyType.STREAMS, { appId: this.appId })
106
+ };
107
+ }
108
+ this.store.publish(key_1.KeyType.QUORUM, {
109
+ type: 'pong',
110
+ guid, originator,
111
+ profile,
112
+ }, appId);
96
113
  }
97
- async requestQuorum(delay = QUORUM_DELAY) {
114
+ async requestQuorum(delay = QUORUM_DELAY, details = false) {
98
115
  const quorum = this.quorum;
99
116
  this.quorum = 0;
100
- await this.store.publish(key_1.KeyType.QUORUM, { type: 'ping', originator: this.guid }, this.appId);
117
+ this.profiles.length = 0;
118
+ await this.store.publish(key_1.KeyType.QUORUM, {
119
+ type: 'ping',
120
+ originator: this.guid,
121
+ details,
122
+ }, this.appId);
101
123
  await (0, utils_1.sleepFor)(delay);
102
124
  return quorum;
103
125
  }
@@ -117,12 +139,26 @@ class QuorumService {
117
139
  this.callbacks = this.callbacks.filter(cb => cb !== callback);
118
140
  }
119
141
  // ************* COMPILER METHODS *************
120
- async inventory(delay = QUORUM_DELAY) {
121
- await this.requestQuorum(delay);
122
- const q1 = await this.requestQuorum(delay);
123
- const q2 = await this.requestQuorum(delay);
124
- const q3 = await this.requestQuorum(delay);
125
- return Math.round((q1 + q2 + q3) / 3);
142
+ async rollCall(delay = QUORUM_DELAY) {
143
+ await this.requestQuorum(delay, true);
144
+ const targetStreams = [];
145
+ const multi = this.store.getMulti();
146
+ this.profiles.forEach((profile) => {
147
+ if (!targetStreams.includes(profile.stream)) {
148
+ targetStreams.push(profile.stream);
149
+ this.store.xlen(profile.stream, multi);
150
+ }
151
+ });
152
+ const stream_depths = await multi.exec();
153
+ this.profiles.forEach(async (profile) => {
154
+ const index = targetStreams.indexOf(profile.stream);
155
+ if (index != -1) {
156
+ profile.stream_depth = Array.isArray(stream_depths[index]) ?
157
+ stream_depths[index][1] :
158
+ stream_depths[index];
159
+ }
160
+ });
161
+ return this.profiles;
126
162
  }
127
163
  async activate(version, delay = QUORUM_DELAY) {
128
164
  version = version.toString();
@@ -4,8 +4,8 @@ import { StoreService } from '../store';
4
4
  import { StreamService } from '../stream';
5
5
  import { RedisClient, RedisMulti } from '../../types/redis';
6
6
  import { ReclaimedMessageType, StreamConfig, StreamData, StreamDataResponse, StreamRole } from '../../types/stream';
7
- declare class StreamSignaler {
8
- static signalers: Set<StreamSignaler>;
7
+ declare class Router {
8
+ static instances: Set<Router>;
9
9
  appId: string;
10
10
  guid: string;
11
11
  role: StreamRole;
@@ -40,4 +40,4 @@ declare class StreamSignaler {
40
40
  expireUnacknowledged(reclaimedMessage: ReclaimedMessageType, stream: string, group: string, consumer: string, id: string, count: number): Promise<void>;
41
41
  parseStreamData(str: string): [undefined, StreamData] | [Error];
42
42
  }
43
- export { StreamSignaler };
43
+ export { Router };
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.StreamSignaler = void 0;
3
+ exports.Router = void 0;
4
4
  const enums_1 = require("../../modules/enums");
5
5
  const key_1 = require("../../modules/key");
6
6
  const utils_1 = require("../../modules/utils");
7
7
  const telemetry_1 = require("../telemetry");
8
8
  const stream_1 = require("../../types/stream");
9
- class StreamSignaler {
9
+ class Router {
10
10
  constructor(config, stream, store, logger) {
11
11
  this.throttle = 0;
12
12
  this.errorCount = 0;
@@ -35,7 +35,7 @@ class StreamSignaler {
35
35
  }
36
36
  async consumeMessages(stream, group, consumer, callback) {
37
37
  this.logger.info(`stream-consumer-starting`, { group, consumer, stream });
38
- StreamSignaler.signalers.add(this);
38
+ Router.instances.add(this);
39
39
  this.shouldConsume = true;
40
40
  await this.createGroup(stream, group);
41
41
  let lastCheckedPendingMessagesAt = Date.now();
@@ -213,7 +213,7 @@ class StreamSignaler {
213
213
  };
214
214
  }
215
215
  static async stopConsuming() {
216
- for (const instance of [...StreamSignaler.signalers]) {
216
+ for (const instance of [...Router.instances]) {
217
217
  instance.stopConsuming();
218
218
  }
219
219
  await (0, utils_1.sleepFor)(enums_1.BLOCK_TIME_MS);
@@ -311,5 +311,5 @@ class StreamSignaler {
311
311
  }
312
312
  }
313
313
  }
314
- exports.StreamSignaler = StreamSignaler;
315
- StreamSignaler.signalers = new Set();
314
+ exports.Router = Router;
315
+ Router.instances = new Set();
@@ -12,7 +12,7 @@ exports.MDATA_SYMBOLS = {
12
12
  KEYS: ['au', 'err', 'l2s']
13
13
  },
14
14
  JOB: {
15
- KEYS: ['ngn', 'tpc', 'pj', 'pd', 'pa', 'key', 'app', 'vrs', 'jid', 'aid', 'ts', 'jc', 'ju', 'js', 'err', 'trc']
15
+ KEYS: ['ngn', 'tpc', 'pj', 'pg', 'pd', 'pa', 'key', 'app', 'vrs', 'jid', 'gid', 'aid', 'ts', 'jc', 'ju', 'js', 'err', 'trc']
16
16
  },
17
17
  JOB_UPDATE: {
18
18
  KEYS: ['ju', 'err']
@@ -24,5 +24,6 @@ declare class IORedisStoreService extends StoreService<RedisClientType, RedisMul
24
24
  xclaim(key: string, group: string, consumer: string, minIdleTime: number, id: string, ...args: string[]): Promise<ReclaimedMessageType>;
25
25
  xack(key: string, group: string, id: string, multi?: RedisMultiType): Promise<number | RedisMultiType>;
26
26
  xdel(key: string, id: string, multi?: RedisMultiType): Promise<number | RedisMultiType>;
27
+ xlen(key: string, multi?: RedisMultiType): Promise<number | RedisMultiType>;
27
28
  }
28
29
  export { IORedisStoreService };
@@ -105,5 +105,14 @@ class IORedisStoreService extends index_1.StoreService {
105
105
  throw error;
106
106
  }
107
107
  }
108
+ async xlen(key, multi) {
109
+ try {
110
+ return await (multi || this.redisClient).xlen(key);
111
+ }
112
+ catch (error) {
113
+ this.logger.error(`Error getting stream depth: ${key}`, { error });
114
+ throw error;
115
+ }
116
+ }
108
117
  }
109
118
  exports.IORedisStoreService = IORedisStoreService;
@@ -26,5 +26,6 @@ declare class RedisStoreService extends StoreService<RedisClientType, RedisMulti
26
26
  xclaim(key: string, group: string, consumer: string, minIdleTime: number, id: string, ...args: string[]): Promise<ReclaimedMessageType>;
27
27
  xack(key: string, group: string, id: string, multi?: RedisMultiType): Promise<number | RedisMultiType>;
28
28
  xdel(key: string, id: string, multi?: RedisMultiType): Promise<number | RedisMultiType>;
29
+ xlen(key: string, multi?: RedisMultiType): Promise<number | RedisMultiType>;
29
30
  }
30
31
  export { RedisStoreService };
@@ -30,6 +30,7 @@ class RedisStoreService extends index_1.StoreService {
30
30
  rpush: 'RPUSH',
31
31
  xack: 'XACK',
32
32
  xdel: 'XDEL',
33
+ xlen: 'XLEN',
33
34
  };
34
35
  }
35
36
  getMulti() {
@@ -141,5 +142,20 @@ class RedisStoreService extends index_1.StoreService {
141
142
  throw error;
142
143
  }
143
144
  }
145
+ async xlen(key, multi) {
146
+ try {
147
+ if (multi) {
148
+ multi.XLEN(key);
149
+ return multi;
150
+ }
151
+ else {
152
+ return await this.redisClient.XLEN(key);
153
+ }
154
+ }
155
+ catch (error) {
156
+ this.logger.error(`Error getting stream depth: ${key}`, { error });
157
+ throw error;
158
+ }
159
+ }
144
160
  }
145
161
  exports.RedisStoreService = RedisStoreService;
@@ -31,6 +31,7 @@ declare abstract class StoreService<T, U extends AbstractRedisClient> {
31
31
  abstract xclaim(key: string, group: string, consumer: string, minIdleTime: number, id: string, ...args: string[]): Promise<ReclaimedMessageType>;
32
32
  abstract xack(key: string, group: string, id: string, multi?: U): Promise<number | U>;
33
33
  abstract xdel(key: string, id: string, multi?: U): Promise<number | U>;
34
+ abstract xlen(key: string, multi?: U): Promise<number | U>;
34
35
  constructor(redisClient: T);
35
36
  init(namespace: string, appId: string, logger: ILogger): Promise<HotMeshApps>;
36
37
  isSuccessful(result: any): boolean;
@@ -39,7 +40,12 @@ declare abstract class StoreService<T, U extends AbstractRedisClient> {
39
40
  zRangeByScore(key: string, score: number | string, value: string | number): Promise<string | null>;
40
41
  mintKey(type: KeyType, params: KeyStoreParams): string;
41
42
  invalidateCache(): void;
42
- reserveEngineId(engineId: string): Promise<boolean>;
43
+ /**
44
+ * At any given time only a single engine will
45
+ * check for and process work items in the
46
+ * time and signal task queues.
47
+ */
48
+ reserveScoutRole(scoutType: 'time' | 'signal', delay?: number): Promise<boolean>;
43
49
  getSettings(bCreate?: boolean): Promise<HotMeshSettings>;
44
50
  setSettings(manifest: HotMeshSettings): Promise<any>;
45
51
  reserveSymbolRange(target: string, size: number, type: 'JOB' | 'ACTIVITY'): Promise<[number, number, Symbols]>;
@@ -61,7 +67,7 @@ declare abstract class StoreService<T, U extends AbstractRedisClient> {
61
67
  * list (added via RPUSH) are LPOPed. If origin was expired, then
62
68
  * LPOPed items from the list are likewise expired;
63
69
  */
64
- setDependency(originJobId: string, topic: string, jobId: string, multi?: U): Promise<any>;
70
+ setDependency(originJobId: string, topic: string, jobId: string, gId: string, multi?: U): Promise<any>;
65
71
  setStats(jobKey: string, jobId: string, dateTime: string, stats: StatsType, appVersion: AppVID, multi?: U): Promise<any>;
66
72
  hGetAllResult(result: any): any;
67
73
  getJobStats(jobKeys: string[]): Promise<JobStatsRange>;
@@ -107,8 +113,8 @@ declare abstract class StoreService<T, U extends AbstractRedisClient> {
107
113
  * for the given sleep group. Sleep groups are
108
114
  * organized into 'n'-second blocks (LISTS))
109
115
  */
110
- registerTimeHook(jobId: string, activityId: string, type: 'sleep' | 'expire' | 'interrupt', deletionTime: number, multi?: U): Promise<void>;
111
- getNextTimeJob(listKey?: string): Promise<[listKey: string, jobId: string, activityId: string, type: 'sleep' | 'expire' | 'interrupt'] | void>;
116
+ registerTimeHook(jobId: string, gId: string, activityId: string, type: 'sleep' | 'expire' | 'interrupt', deletionTime: number, multi?: U): Promise<void>;
117
+ getNextTimeJob(listKey?: string): Promise<[listKey: string, jobId: string, gId: string, activityId: string, type: 'sleep' | 'expire' | 'interrupt'] | boolean>;
112
118
  /**
113
119
  * when processing time jobs, the target LIST ID returned
114
120
  * from the ZSET query can be prefixed to denote what to