qdone 2.0.7-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
  });
@@ -328,7 +328,7 @@ function sendMessageBatch(qrl, messages, opt) {
328
328
  if (opt.fifo) {
329
329
  params.Entries = params.Entries.map(function (message) { return Object.assign({
330
330
  MessageGroupId: opt.groupIdPerMessage ? uuidFunction() : opt.groupId,
331
- MessageDeduplicationId: uuidFunction()
331
+ MessageDeduplicationId: opt.deduplicationId || uuidFunction()
332
332
  }, message); });
333
333
  }
334
334
  if (opt.delay) {
@@ -400,14 +400,13 @@ function sendMessageBatch(qrl, messages, opt) {
400
400
  });
401
401
  }
402
402
  exports.sendMessageBatch = sendMessageBatch;
403
- var messages = {};
404
403
  var requestCount = 0;
405
404
  //
406
405
  // Flushes the internal message buffer for qrl.
407
406
  // If the message is too large, batch is retried with half the messages.
408
407
  // Returns number of messages flushed.
409
408
  //
410
- function flushMessages(qrl, opt) {
409
+ function flushMessages(qrl, opt, sendBuffer) {
411
410
  return __awaiter(this, void 0, void 0, function () {
412
411
  function whileNotEmpty() {
413
412
  return __awaiter(this, void 0, void 0, function () {
@@ -415,18 +414,18 @@ function flushMessages(qrl, opt) {
415
414
  return __generator(this, function (_a) {
416
415
  switch (_a.label) {
417
416
  case 0:
418
- if (!(messages[qrl] && messages[qrl].length))
417
+ if (!(sendBuffer[qrl] && sendBuffer[qrl].length))
419
418
  return [2 /*return*/, numFlushed
420
419
  // Construct batch until full
421
420
  ];
422
421
  batch = [];
423
- nextSize = JSON.stringify(messages[qrl][0]).length;
422
+ nextSize = JSON.stringify(sendBuffer[qrl][0]).length;
424
423
  totalSize = 0;
425
- while ((totalSize + nextSize) < 262144 && messages[qrl].length && batch.length < 10) {
426
- batch.push(messages[qrl].shift());
424
+ while ((totalSize + nextSize) < 262144 && sendBuffer[qrl].length && batch.length < 10) {
425
+ batch.push(sendBuffer[qrl].shift());
427
426
  totalSize += nextSize;
428
- if (messages[qrl].length)
429
- nextSize = JSON.stringify(messages[qrl][0]).length;
427
+ if (sendBuffer[qrl].length)
428
+ nextSize = JSON.stringify(sendBuffer[qrl][0]).length;
430
429
  else
431
430
  nextSize = 0;
432
431
  }
@@ -468,17 +467,16 @@ exports.flushMessages = flushMessages;
468
467
  // Automaticaly flushes if queue has >= 10 messages.
469
468
  // Returns number of messages flushed.
470
469
  //
471
- var messageIndex = 0;
472
- function addMessage(qrl, command, opt) {
470
+ function addMessage(qrl, command, messageIndex, opt, sendBuffer) {
473
471
  return __awaiter(this, void 0, void 0, function () {
474
472
  var message;
475
473
  return __generator(this, function (_a) {
476
- message = formatMessage(command, messageIndex++);
477
- messages[qrl] = messages[qrl] || [];
478
- messages[qrl].push(message);
479
- debug({ location: 'addMessage', messages: messages });
480
- if (messages[qrl].length >= 10) {
481
- return [2 /*return*/, flushMessages(qrl, opt)];
474
+ message = formatMessage(command, messageIndex);
475
+ sendBuffer[qrl] = sendBuffer[qrl] || [];
476
+ sendBuffer[qrl].push(message);
477
+ debug({ location: 'addMessage', sendBuffer: sendBuffer });
478
+ if (sendBuffer[qrl].length >= 10) {
479
+ return [2 /*return*/, flushMessages(qrl, opt, sendBuffer)];
482
480
  }
483
481
  return [2 /*return*/, 0];
484
482
  });
@@ -512,7 +510,7 @@ exports.enqueue = enqueue;
512
510
  //
513
511
  function enqueueBatch(pairs, options) {
514
512
  return __awaiter(this, void 0, void 0, function () {
515
- var opt, normalizedPairs, uniqueQnames, createPromises, _i, uniqueQnames_1, qname, initialFlushTotal, _a, normalizedPairs_1, _b, qname, command, qrl, _c, extraFlushPromises, qrl, extraFlushCounts, extraFlushTotal, totalFlushed;
513
+ var opt, normalizedPairs, uniqueQnames, createPromises, _i, uniqueQnames_1, qname, sendBuffer, messageIndex, initialFlushTotal, _a, normalizedPairs_1, _b, qname, command, qrl, _c, extraFlushPromises, qrl, extraFlushCounts, extraFlushTotal, totalFlushed;
516
514
  return __generator(this, function (_d) {
517
515
  switch (_d.label) {
518
516
  case 0:
@@ -540,6 +538,8 @@ function enqueueBatch(pairs, options) {
540
538
  // After we've prefetched, all qrls are in cache
541
539
  // so go back through the list of pairs and fire off messages
542
540
  requestCount = 0;
541
+ sendBuffer = {};
542
+ messageIndex = 0;
543
543
  initialFlushTotal = 0;
544
544
  _a = 0, normalizedPairs_1 = normalizedPairs;
545
545
  _d.label = 2;
@@ -550,7 +550,7 @@ function enqueueBatch(pairs, options) {
550
550
  case 3:
551
551
  qrl = _d.sent();
552
552
  _c = initialFlushTotal;
553
- return [4 /*yield*/, addMessage(qrl, command, opt)];
553
+ return [4 /*yield*/, addMessage(qrl, command, messageIndex++, opt, sendBuffer)];
554
554
  case 4:
555
555
  initialFlushTotal = _c + _d.sent();
556
556
  _d.label = 5;
@@ -559,8 +559,8 @@ function enqueueBatch(pairs, options) {
559
559
  return [3 /*break*/, 2];
560
560
  case 6:
561
561
  extraFlushPromises = [];
562
- for (qrl in messages) {
563
- extraFlushPromises.push(flushMessages(qrl, opt));
562
+ for (qrl in sendBuffer) {
563
+ extraFlushPromises.push(flushMessages(qrl, opt, sendBuffer));
564
564
  }
565
565
  return [4 /*yield*/, Promise.all(extraFlushPromises)];
566
566
  case 7:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qdone",
3
- "version": "2.0.7-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')
package/src/enqueue.js CHANGED
@@ -209,7 +209,7 @@ export async function sendMessageBatch (qrl, messages, opt) {
209
209
  params.Entries = params.Entries.map(
210
210
  message => Object.assign({
211
211
  MessageGroupId: opt.groupIdPerMessage ? uuidFunction() : opt.groupId,
212
- MessageDeduplicationId: uuidFunction()
212
+ MessageDeduplicationId: opt.deduplicationId || uuidFunction()
213
213
  }, message)
214
214
  )
215
215
  }
@@ -267,7 +267,6 @@ export async function sendMessageBatch (qrl, messages, opt) {
267
267
  return backoff.run(send, shouldRetry)
268
268
  }
269
269
 
270
- const messages = {}
271
270
  let requestCount = 0
272
271
 
273
272
  //
@@ -275,20 +274,20 @@ let requestCount = 0
275
274
  // If the message is too large, batch is retried with half the messages.
276
275
  // Returns number of messages flushed.
277
276
  //
278
- export async function flushMessages (qrl, opt) {
277
+ export async function flushMessages (qrl, opt, sendBuffer) {
279
278
  debug('flushMessages', qrl)
280
279
  // Flush until empty
281
280
  let numFlushed = 0
282
281
  async function whileNotEmpty () {
283
- if (!(messages[qrl] && messages[qrl].length)) return numFlushed
282
+ if (!(sendBuffer[qrl] && sendBuffer[qrl].length)) return numFlushed
284
283
  // Construct batch until full
285
284
  const batch = []
286
- let nextSize = JSON.stringify(messages[qrl][0]).length
285
+ let nextSize = JSON.stringify(sendBuffer[qrl][0]).length
287
286
  let totalSize = 0
288
- while ((totalSize + nextSize) < 262144 && messages[qrl].length && batch.length < 10) {
289
- batch.push(messages[qrl].shift())
287
+ while ((totalSize + nextSize) < 262144 && sendBuffer[qrl].length && batch.length < 10) {
288
+ batch.push(sendBuffer[qrl].shift())
290
289
  totalSize += nextSize
291
- if (messages[qrl].length) nextSize = JSON.stringify(messages[qrl][0]).length
290
+ if (sendBuffer[qrl].length) nextSize = JSON.stringify(sendBuffer[qrl][0]).length
292
291
  else nextSize = 0
293
292
  }
294
293
 
@@ -321,14 +320,13 @@ export async function flushMessages (qrl, opt) {
321
320
  // Automaticaly flushes if queue has >= 10 messages.
322
321
  // Returns number of messages flushed.
323
322
  //
324
- let messageIndex = 0
325
- export async function addMessage (qrl, command, opt) {
326
- const message = formatMessage(command, messageIndex++)
327
- messages[qrl] = messages[qrl] || []
328
- messages[qrl].push(message)
329
- debug({ location: 'addMessage', messages })
330
- if (messages[qrl].length >= 10) {
331
- return flushMessages(qrl, opt)
323
+ export async function addMessage (qrl, command, messageIndex, opt, sendBuffer) {
324
+ const message = formatMessage(command, messageIndex)
325
+ sendBuffer[qrl] = sendBuffer[qrl] || []
326
+ sendBuffer[qrl].push(message)
327
+ debug({ location: 'addMessage', sendBuffer })
328
+ if (sendBuffer[qrl].length >= 10) {
329
+ return flushMessages(qrl, opt, sendBuffer)
332
330
  }
333
331
  return 0
334
332
  }
@@ -370,16 +368,18 @@ export async function enqueueBatch (pairs, options) {
370
368
  // After we've prefetched, all qrls are in cache
371
369
  // so go back through the list of pairs and fire off messages
372
370
  requestCount = 0
371
+ const sendBuffer = {}
372
+ let messageIndex = 0
373
373
  let initialFlushTotal = 0
374
374
  for (const { qname, command } of normalizedPairs) {
375
375
  const qrl = await getOrCreateQueue(qname, opt)
376
- initialFlushTotal += await addMessage(qrl, command, opt)
376
+ initialFlushTotal += await addMessage(qrl, command, messageIndex++, opt, sendBuffer)
377
377
  }
378
378
 
379
379
  // And flush any remaining messages
380
380
  const extraFlushPromises = []
381
- for (const qrl in messages) {
382
- extraFlushPromises.push(flushMessages(qrl, opt))
381
+ for (const qrl in sendBuffer) {
382
+ extraFlushPromises.push(flushMessages(qrl, opt, sendBuffer))
383
383
  }
384
384
  const extraFlushCounts = await Promise.all(extraFlushPromises)
385
385
  const extraFlushTotal = extraFlushCounts.reduce((a, b) => a + b, 0)