qdone 2.0.8-alpha → 2.0.9-alpha

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.
@@ -83,19 +83,20 @@ var DoNotProcess = /** @class */ (function (_super) {
83
83
  return DoNotProcess;
84
84
  }(Error));
85
85
  exports.DoNotProcess = DoNotProcess;
86
- var debug = (0, debug_1.default)('qdone:worker');
86
+ var debug = (0, debug_1.default)('qdone:consumer');
87
87
  // Global flag for shutdown request
88
88
  var shutdownRequested = false;
89
89
  var shutdownCallbacks = [];
90
90
  function requestShutdown() {
91
+ debug('requestShutdown');
91
92
  shutdownRequested = true;
92
93
  for (var _i = 0, shutdownCallbacks_1 = shutdownCallbacks; _i < shutdownCallbacks_1.length; _i++) {
93
94
  var callback = shutdownCallbacks_1[_i];
94
- try {
95
- callback();
96
- }
97
- catch (e) { }
95
+ debug('callback', callback);
96
+ callback();
97
+ // try { callback() } catch (e) { }
98
98
  }
99
+ debug('requestShutdown done');
99
100
  }
100
101
  exports.requestShutdown = requestShutdown;
101
102
  function processMessage(message, callback, qname, qrl, opt) {
@@ -167,7 +168,7 @@ function processMessage(message, callback, qname, qrl, opt) {
167
168
  switch (_a.label) {
168
169
  case 0:
169
170
  debug('processMessage', message, qname, qrl);
170
- payload = JSON.parse(message.Body);
171
+ payload = opt.json ? JSON.parse(message.Body) : message.Body;
171
172
  if (opt.verbose) {
172
173
  console.error(chalk_1.default.blue(' Processing payload:'), payload);
173
174
  }
@@ -366,16 +367,19 @@ function processMessages(queues, callback, options) {
366
367
  // Callback to help facilitate better UX at shutdown
367
368
  function shutdownCallback() {
368
369
  if (opt.verbose) {
369
- debug({ activeLoops: activeLoops });
370
- var activeQueues = Object.keys(activeLoops).filter(function (q) { return activeLoops[q]; }).map(function (q) { return q.slice(opt.prefix.length); });
371
- if (activeQueues.length) {
372
- console.error(chalk_1.default.blue('Waiting for work to finish on the following queues: ') + activeQueues.join(chalk_1.default.blue(', ')));
370
+ debug({ activeLoops: activeLoops, completedLoops: completedLoops });
371
+ if (activeLoops.length) {
372
+ console.error(chalk_1.default.blue('Waiting for work to finish on the following queues: '));
373
+ for (var _i = 0, activeLoops_2 = activeLoops; _i < activeLoops_2.length; _i++) {
374
+ var _a = activeLoops_2[_i], id = _a[0], _b = _a[1], qname = _b.qname, qrl = _b.qrl, promise = _b.promise;
375
+ console.error(' ' + qname + "job ".concat(id));
376
+ }
373
377
  }
374
- clearTimeout(delayTimeout);
378
+ // clearTimeout(delayTimeout)
375
379
  }
376
380
  }
377
381
  // Listen to a queue until it is out of messages
378
- function listenLoop(qname, qrl) {
382
+ function listenLoop(qname, qrl, loopId) {
379
383
  return __awaiter(this, void 0, void 0, function () {
380
384
  var _a, noJobs, jobsSucceeded, jobsFailed, err_3;
381
385
  return __generator(this, function (_b) {
@@ -392,11 +396,14 @@ function processMessages(queues, callback, options) {
392
396
  return [4 /*yield*/, pollSingleQueue(qname, qrl, callback, opt)];
393
397
  case 1:
394
398
  _a = _b.sent(), noJobs = _a.noJobs, jobsSucceeded = _a.jobsSucceeded, jobsFailed = _a.jobsFailed;
399
+ debug('pollSingleQueue return');
395
400
  stats.noJobs += noJobs;
396
401
  stats.jobsFailed += jobsFailed;
397
402
  stats.jobsSucceeded += jobsSucceeded;
398
- // No work? return to outer loop
399
- if (noJobs)
403
+ debug({ stats: stats, noJobs: noJobs });
404
+ // No work? Shutdown requested? Return to outer loop
405
+ debug({ noJobs: noJobs, shutdownRequested: shutdownRequested });
406
+ if (noJobs || shutdownRequested)
400
407
  return [2 /*return*/];
401
408
  // Otherwise keep going
402
409
  return [2 /*return*/, listenLoop(qname, qrl)];
@@ -407,34 +414,38 @@ function processMessages(queues, callback, options) {
407
414
  console.error(chalk_1.default.blue(' error : ') + err_3);
408
415
  return [3 /*break*/, 4];
409
416
  case 3:
410
- delete activeLoops[qname];
417
+ completedLoops.set(loopId, activeLoops.get(loopId));
411
418
  return [7 /*endfinally*/];
412
419
  case 4: return [2 /*return*/];
413
420
  }
414
421
  });
415
422
  });
416
423
  }
417
- var opt, stats, activeLoops, delayTimeout, delay, start, selectedPairs, _i, selectedPairs_1, _a, qname, qrl, msSoFar, msUntilNextResolve;
418
- return __generator(this, function (_b) {
419
- switch (_b.label) {
424
+ var opt, loopCounter, stats, maxActiveLoops, activeLoops, completedLoops, delayTimeout, delay, start, selectedPairs, _i, selectedPairs_1, _a, qname, qrl, loopId, msSoFar, msUntilNextResolve, _b, completedLoops_1, _c, id, _d, qname, qrl, promise, _e, activeLoops_1, _f, id, _g, qname, qrl, promise;
425
+ return __generator(this, function (_h) {
426
+ switch (_h.label) {
420
427
  case 0:
421
428
  opt = (0, defaults_js_1.getOptionsWithDefaults)(options);
422
429
  debug('processMessages', { queues: queues, callback: callback, options: options, opt: opt });
430
+ loopCounter = 0;
423
431
  stats = { noJobs: 0, jobsSucceeded: 0, jobsFailed: 0 };
424
- activeLoops = {};
432
+ maxActiveLoops = 10;
433
+ activeLoops = new Map();
434
+ completedLoops = new Map();
425
435
  delay = function (ms) { return new Promise(function (resolve) {
426
436
  delayTimeout = setTimeout(resolve, ms);
427
437
  }); };
428
438
  shutdownCallbacks.push(shutdownCallback);
429
- _b.label = 1;
439
+ _h.label = 1;
430
440
  case 1:
431
- if (!!shutdownRequested) return [3 /*break*/, 5];
441
+ if (!!shutdownRequested) return [3 /*break*/, 9];
432
442
  start = new Date();
433
443
  return [4 /*yield*/, resolveQueues(queues, opt)];
434
444
  case 2:
435
- selectedPairs = _b.sent();
445
+ selectedPairs = _h.sent();
446
+ debug({ selectedPairs: selectedPairs });
436
447
  if (shutdownRequested)
437
- return [3 /*break*/, 5];
448
+ return [3 /*break*/, 9];
438
449
  // But only if we have queues to listen on
439
450
  if (selectedPairs.length) {
440
451
  // Randomize order
@@ -450,30 +461,60 @@ function processMessages(queues, callback, options) {
450
461
  // Launch listen loop for each queue
451
462
  for (_i = 0, selectedPairs_1 = selectedPairs; _i < selectedPairs_1.length; _i++) {
452
463
  _a = selectedPairs_1[_i], qname = _a.qname, qrl = _a.qrl;
453
- if (!activeLoops[qname])
454
- activeLoops[qname] = listenLoop(qname, qrl);
464
+ // Bail if we already have too many
465
+ if (activeLoops.size >= maxActiveLoops) {
466
+ if (opt.verbose)
467
+ console.error(chalk_1.default.yellow('Hit active worker limit of ') + maxActiveLoops);
468
+ break;
469
+ }
470
+ loopId = loopCounter++;
471
+ activeLoops.set(loopId, { qname: qname, qrl: qrl, promise: listenLoop(qname, qrl, loopId) });
455
472
  }
456
473
  }
457
474
  if (!!shutdownRequested) return [3 /*break*/, 4];
458
475
  msSoFar = Math.max(0, new Date() - start);
459
- msUntilNextResolve = Math.max(0, opt.waitTime * 1000 - msSoFar);
476
+ msUntilNextResolve = Math.max(0, /*opt.waitTime **/ 1000 - msSoFar);
460
477
  debug({ msSoFar: msSoFar, msUntilNextResolve: msUntilNextResolve });
461
478
  if (!msUntilNextResolve) return [3 /*break*/, 4];
462
479
  if (opt.verbose)
463
480
  console.error(chalk_1.default.blue('Will resolve queues again in ' + Math.round(msUntilNextResolve / 1000) + ' seconds'));
464
481
  return [4 /*yield*/, delay(msUntilNextResolve)];
465
482
  case 3:
466
- _b.sent();
467
- _b.label = 4;
468
- case 4: return [3 /*break*/, 1];
469
- case 5:
470
- // Wait on all work to finish
471
- // shutdownCallback()
472
- return [4 /*yield*/, Promise.all(Object.values(activeLoops))];
483
+ _h.sent();
484
+ _h.label = 4;
485
+ case 4:
486
+ _b = 0, completedLoops_1 = completedLoops;
487
+ _h.label = 5;
488
+ case 5:
489
+ if (!(_b < completedLoops_1.length)) return [3 /*break*/, 8];
490
+ _c = completedLoops_1[_b], id = _c[0], _d = _c[1], qname = _d.qname, qrl = _d.qrl, promise = _d.promise;
491
+ return [4 /*yield*/, promise]; // make sure the promise resolves
473
492
  case 6:
474
- // Wait on all work to finish
475
- // shutdownCallback()
476
- _b.sent();
493
+ _h.sent(); // make sure the promise resolves
494
+ debug('Cleaning up', { id: id, qname: qname, qrl: qrl, promise: promise });
495
+ activeLoops.delete(id);
496
+ _h.label = 7;
497
+ case 7:
498
+ _b++;
499
+ return [3 /*break*/, 5];
500
+ case 8: return [3 /*break*/, 1];
501
+ case 9:
502
+ debug('out here', { activeLoops: activeLoops });
503
+ _e = 0, activeLoops_1 = activeLoops;
504
+ _h.label = 10;
505
+ case 10:
506
+ if (!(_e < activeLoops_1.length)) return [3 /*break*/, 13];
507
+ _f = activeLoops_1[_e], id = _f[0], _g = _f[1], qname = _g.qname, qrl = _g.qrl, promise = _g.promise;
508
+ debug('Waiting on active loop', id);
509
+ return [4 /*yield*/, promise]; // make sure the promise resolves
510
+ case 11:
511
+ _h.sent(); // make sure the promise resolves
512
+ _h.label = 12;
513
+ case 12:
514
+ _e++;
515
+ return [3 /*break*/, 10];
516
+ case 13:
517
+ debug('after all');
477
518
  return [2 /*return*/];
478
519
  }
479
520
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qdone",
3
- "version": "2.0.8-alpha",
3
+ "version": "2.0.9-alpha",
4
4
  "description": "Language agnostic job queue for SQS",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/src/consumer.js CHANGED
@@ -27,22 +27,26 @@ import { getSQSClient } from './sqs.js'
27
27
  //
28
28
  export class DoNotProcess extends Error {}
29
29
 
30
- const debug = Debug('qdone:worker')
30
+ const debug = Debug('qdone:consumer')
31
31
 
32
32
  // Global flag for shutdown request
33
33
  let shutdownRequested = false
34
34
  const shutdownCallbacks = []
35
35
 
36
36
  export function requestShutdown () {
37
+ debug('requestShutdown')
37
38
  shutdownRequested = true
38
39
  for (const callback of shutdownCallbacks) {
39
- try { callback() } catch (e) { }
40
+ debug('callback', callback)
41
+ callback()
42
+ // try { callback() } catch (e) { }
40
43
  }
44
+ debug('requestShutdown done')
41
45
  }
42
46
 
43
47
  export async function processMessage (message, callback, qname, qrl, opt) {
44
48
  debug('processMessage', message, qname, qrl)
45
- const payload = JSON.parse(message.Body)
49
+ const payload = opt.json ? JSON.parse(message.Body) : message.Body
46
50
  if (opt.verbose) {
47
51
  console.error(chalk.blue(' Processing payload:'), payload)
48
52
  } else if (!opt.disableLog) {
@@ -248,8 +252,11 @@ export async function processMessages (queues, callback, options) {
248
252
  const opt = getOptionsWithDefaults(options)
249
253
  debug('processMessages', { queues, callback, options, opt })
250
254
 
255
+ let loopCounter = 0
251
256
  const stats = { noJobs: 0, jobsSucceeded: 0, jobsFailed: 0 }
252
- const activeLoops = {}
257
+ const maxActiveLoops = 10
258
+ const activeLoops = new Map()
259
+ const completedLoops = new Map()
253
260
 
254
261
  // This delay function keeps a timeout reference around so it can be
255
262
  // cancelled at shutdown
@@ -261,18 +268,20 @@ export async function processMessages (queues, callback, options) {
261
268
  // Callback to help facilitate better UX at shutdown
262
269
  function shutdownCallback () {
263
270
  if (opt.verbose) {
264
- debug({ activeLoops })
265
- const activeQueues = Object.keys(activeLoops).filter(q => activeLoops[q]).map(q => q.slice(opt.prefix.length))
266
- if (activeQueues.length) {
267
- console.error(chalk.blue('Waiting for work to finish on the following queues: ') + activeQueues.join(chalk.blue(', ')))
271
+ debug({ activeLoops, completedLoops })
272
+ if (activeLoops.length) {
273
+ console.error(chalk.blue('Waiting for work to finish on the following queues: '))
274
+ for (const [id, { qname, qrl, promise }] of activeLoops) {
275
+ console.error(' ' + qname + `job ${id}`)
276
+ }
268
277
  }
269
- clearTimeout(delayTimeout)
278
+ // clearTimeout(delayTimeout)
270
279
  }
271
280
  }
272
281
  shutdownCallbacks.push(shutdownCallback)
273
282
 
274
283
  // Listen to a queue until it is out of messages
275
- async function listenLoop (qname, qrl) {
284
+ async function listenLoop (qname, qrl, loopId) {
276
285
  try {
277
286
  if (shutdownRequested) return
278
287
  if (opt.verbose) {
@@ -284,12 +293,15 @@ export async function processMessages (queues, callback, options) {
284
293
  }
285
294
  // Aggregate the results
286
295
  const { noJobs, jobsSucceeded, jobsFailed } = await pollSingleQueue(qname, qrl, callback, opt)
296
+ debug('pollSingleQueue return')
287
297
  stats.noJobs += noJobs
288
298
  stats.jobsFailed += jobsFailed
289
299
  stats.jobsSucceeded += jobsSucceeded
300
+ debug({ stats, noJobs })
290
301
 
291
- // No work? return to outer loop
292
- if (noJobs) return
302
+ // No work? Shutdown requested? Return to outer loop
303
+ debug({ noJobs, shutdownRequested })
304
+ if (noJobs || shutdownRequested) return
293
305
 
294
306
  // Otherwise keep going
295
307
  return listenLoop(qname, qrl)
@@ -298,7 +310,7 @@ export async function processMessages (queues, callback, options) {
298
310
  console.error(chalk.red(' ERROR in listenLoop'))
299
311
  console.error(chalk.blue(' error : ') + err)
300
312
  } finally {
301
- delete activeLoops[qname]
313
+ completedLoops.set(loopId, activeLoops.get(loopId))
302
314
  }
303
315
  }
304
316
 
@@ -306,6 +318,7 @@ export async function processMessages (queues, callback, options) {
306
318
  while (!shutdownRequested) { // eslint-disable-line
307
319
  const start = new Date()
308
320
  const selectedPairs = await resolveQueues(queues, opt)
321
+ debug({ selectedPairs })
309
322
  if (shutdownRequested) break
310
323
 
311
324
  // But only if we have queues to listen on
@@ -323,24 +336,43 @@ export async function processMessages (queues, callback, options) {
323
336
 
324
337
  // Launch listen loop for each queue
325
338
  for (const { qname, qrl } of selectedPairs) {
326
- if (!activeLoops[qname]) activeLoops[qname] = listenLoop(qname, qrl)
339
+ // Bail if we already have too many
340
+ if (activeLoops.size >= maxActiveLoops) {
341
+ if (opt.verbose) console.error(chalk.yellow('Hit active worker limit of ') + maxActiveLoops)
342
+ break
343
+ }
344
+ const loopId = loopCounter++
345
+ activeLoops.set(loopId, { qname, qrl, promise: listenLoop(qname, qrl, loopId) })
327
346
  }
328
347
  }
348
+
329
349
  // Wait until the next time we need to resolve
330
350
  if (!shutdownRequested) {
331
351
  const msSoFar = Math.max(0, new Date() - start)
332
- const msUntilNextResolve = Math.max(0, opt.waitTime * 1000 - msSoFar)
352
+ const msUntilNextResolve = Math.max(0, /*opt.waitTime **/ 1000 - msSoFar)
333
353
  debug({ msSoFar, msUntilNextResolve })
334
354
  if (msUntilNextResolve) {
335
355
  if (opt.verbose) console.error(chalk.blue('Will resolve queues again in ' + Math.round(msUntilNextResolve / 1000) + ' seconds'))
336
356
  await delay(msUntilNextResolve)
337
357
  }
338
358
  }
359
+
360
+ // Cleanup completed loops
361
+ for (const [id, { qname, qrl, promise }] of completedLoops) {
362
+ await promise // make sure the promise resolves
363
+ debug('Cleaning up', { id, qname, qrl, promise })
364
+ activeLoops.delete(id)
365
+ }
339
366
  }
367
+ debug('out here', { activeLoops })
340
368
 
341
369
  // Wait on all work to finish
342
370
  // shutdownCallback()
343
- await Promise.all(Object.values(activeLoops))
371
+ for (const [id, { qname, qrl, promise }] of activeLoops) {
372
+ debug('Waiting on active loop', id)
373
+ await promise // make sure the promise resolves
374
+ }
375
+ debug('after all')
344
376
  }
345
377
 
346
378
  debug('loaded')