qdone 2.0.48-alpha → 2.0.50-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.
@@ -48,7 +48,8 @@ exports.defaults = Object.freeze({
48
48
  delete: false,
49
49
  unpair: false,
50
50
  // Check
51
- create: false
51
+ create: false,
52
+ overwrite: false
52
53
  });
53
54
  function validateInteger(opt, name) {
54
55
  const parsed = parseInt(opt[name], 10);
@@ -87,50 +88,50 @@ function getOptionsWithDefaults(options) {
87
88
  const dlq = options.dlq || !!(options['dlq-suffix'] || options['dlq-after'] || options['dlq-name'] || options.dlqSuffix || options.dlqAfter || options.dlqName);
88
89
  const opt = {
89
90
  // Shared
90
- prefix: options.prefix === '' ? options.prefix : (options.prefix || exports.defaults.prefix),
91
- failSuffix: options.failSuffix || options['fail-suffix'] || exports.defaults.failSuffix,
92
- region: options.region || process.env.AWS_REGION || exports.defaults.region,
93
- quiet: options.quiet || exports.defaults.quiet,
94
- verbose: options.verbose || exports.defaults.verbose,
95
- fifo: options.fifo || exports.defaults.fifo,
96
- sentryDsn: options.sentryDsn || options['sentry-dsn'],
97
- disableLog: options.disableLog || options['disable-log'] || exports.defaults.disableLog,
98
- includeFailed: options.includeFailed || options['include-failed'] || exports.defaults.includeFailed,
99
- includeDead: options.includeDead || options['include-dead'] || exports.defaults.includeDead,
100
- externalDedup: options.externalDedup || options['external-dedup'] || exports.defaults.externalDedup,
101
- dedupPeriod: options.dedupPeriod || options['dedup-period'] || exports.defaults.dedupPeriod,
102
- dedupStats: options.dedupStats || options['dedup-stats'] || exports.defaults.dedupStats,
91
+ prefix: options.prefix === '' ? options.prefix : (options.prefix || process.env.QDONE_PREFIX || exports.defaults.prefix),
92
+ failSuffix: options.failSuffix || options['fail-suffix'] || process.env.QDONE_FAIL_SUFFIX || exports.defaults.failSuffix,
93
+ region: options.region || process.env.QDONE_REGION || process.env.AWS_REGION || exports.defaults.region,
94
+ quiet: options.quiet || process.env.QDONE_QUIET === 'true' || exports.defaults.quiet,
95
+ verbose: options.verbose || process.env.QDONE_VERBOSE === 'true' || exports.defaults.verbose,
96
+ fifo: options.fifo || process.env.QDONE_FIFO === 'true' || exports.defaults.fifo,
97
+ sentryDsn: options.sentryDsn || options['sentry-dsn'] || process.env.QDONE_SENTRY_DSN,
98
+ disableLog: options.disableLog || options['disable-log'] || process.env.QDONE_DISABLE_LOG === 'true' || exports.defaults.disableLog,
99
+ includeFailed: options.includeFailed || options['include-failed'] || process.env.QDONE_INCLUDE_FAILED === 'true' || exports.defaults.includeFailed,
100
+ includeDead: options.includeDead || options['include-dead'] || process.env.QDONE_INCLUDE_DEAD === 'true' || exports.defaults.includeDead,
101
+ externalDedup: options.externalDedup || options['external-dedup'] || process.env.QDONE_EXTERNAL_DEDUP === 'true' || exports.defaults.externalDedup,
102
+ dedupPeriod: options.dedupPeriod || options['dedup-period'] || process.env.QDONE_DEDUP_PERIOD || exports.defaults.dedupPeriod,
103
+ dedupStats: options.dedupStats || options['dedup-stats'] || process.env.QDONE_DEDUP_STATS === 'true' || exports.defaults.dedupStats,
103
104
  // Cache
104
- cacheUri: options.cacheUri || options['cache-uri'] || exports.defaults.cacheUri,
105
- cachePrefix: options.cachePrefix || options['cache-prefix'] || exports.defaults.cachePrefix,
106
- cacheTtlSeconds: options.cacheTtlSeconds || options['cache-ttl-seconds'] || exports.defaults.cacheTtlSeconds,
105
+ cacheUri: options.cacheUri || options['cache-uri'] || process.env.QDONE_CACHE_URI || exports.defaults.cacheUri,
106
+ cachePrefix: options.cachePrefix || options['cache-prefix'] || process.env.QDONE_CACHE_PREFIX || exports.defaults.cachePrefix,
107
+ cacheTtlSeconds: options.cacheTtlSeconds || options['cache-ttl-seconds'] || process.env.QDONE_CACHE_TTL_SECONDS || exports.defaults.cacheTtlSeconds,
107
108
  // Enqueue
108
- groupId: options.groupId || options['group-id'] || exports.defaults.groupId,
109
+ groupId: options.groupId || options['group-id'] || process.env.QDONE_GROUP_ID || exports.defaults.groupId,
109
110
  groupIdPerMessage: false,
110
- deduplicationId: options.deduplicationId || options['deduplication-id'] || exports.defaults.deduplicationId,
111
- dedupIdPerMessage: options.dedupIdPerMessage || options['dedup-id-per-message'] || exports.defaults.dedupIdPerMessage,
112
- messageRetentionPeriod: options.messageRetentionPeriod || options['message-retention-period'] || exports.defaults.messageRetentionPeriod,
113
- delay: options.delay || exports.defaults.delay,
114
- sendRetries: options['send-retries'] || exports.defaults.sendRetries,
115
- failDelay: options.failDelay || options['fail-delay'] || exports.defaults.failDelay,
111
+ deduplicationId: options.deduplicationId || options['deduplication-id'] || process.env.QDONE_DEDUPLICATION_ID || exports.defaults.deduplicationId,
112
+ dedupIdPerMessage: options.dedupIdPerMessage || options['dedup-id-per-message'] || process.env.QDONE_DEDUP_ID_PER_MESSAGE === 'true' || exports.defaults.dedupIdPerMessage,
113
+ messageRetentionPeriod: options.messageRetentionPeriod || options['message-retention-period'] || process.env.QDONE_MESSAGE_RETENTION_PERIOD || exports.defaults.messageRetentionPeriod,
114
+ delay: options.delay || process.env.QDONE_DELAY || exports.defaults.delay,
115
+ sendRetries: options.sendRetries || options['send-retries'] || process.env.QDONE_SEND_RETRIES || exports.defaults.sendRetries,
116
+ failDelay: options.failDelay || options['fail-delay'] || process.env.QDONE_FAIL_DELAY || exports.defaults.failDelay,
116
117
  dlq: dlq === false ? false : exports.defaults.dlq,
117
- dlqSuffix: options.dlqSuffix || options['dlq-suffix'] || exports.defaults.dlqSuffix,
118
- dlqAfter: options.dlqAfter || options['dlq-after'] || exports.defaults.dlqAfter,
118
+ dlqSuffix: options.dlqSuffix || options['dlq-suffix'] || process.env.QDONE_DLQ_SUFFIX || exports.defaults.dlqSuffix,
119
+ dlqAfter: options.dlqAfter || options['dlq-after'] || process.env.QDONE_DLQ_AFTER || exports.defaults.dlqAfter,
119
120
  tags: options.tags || undefined,
120
121
  // Worker
121
- waitTime: options.waitTime || options['wait-time'] || exports.defaults.waitTime,
122
- killAfter: options.killAfter || options['kill-after'] || exports.defaults.killAfter,
123
- archive: options.archive || exports.defaults.archive,
124
- activeOnly: options.activeOnly || options['active-only'] || exports.defaults.activeOnly,
125
- maxConcurrentJobs: options.maxConcurrentJobs || exports.defaults.maxConcurrentJobs,
126
- maxMemoryPercent: options.maxMemoryPercent || exports.defaults.maxMemoryPercent,
122
+ waitTime: options.waitTime || options['wait-time'] || process.env.QDONE_WAIT_TIME || exports.defaults.waitTime,
123
+ killAfter: options.killAfter || options['kill-after'] || process.env.QDONE_KILL_AFTER || exports.defaults.killAfter,
124
+ archive: options.archive || process.env.QDONE_ARCHIVE === 'true' || exports.defaults.archive,
125
+ activeOnly: options.activeOnly || options['active-only'] || process.env.QDONE_ACTIVE_ONLY === 'true' || exports.defaults.activeOnly,
126
+ maxConcurrentJobs: options.maxConcurrentJobs || process.env.QDONE_MAX_CONCURRENT_JOBS || exports.defaults.maxConcurrentJobs,
127
+ maxMemoryPercent: options.maxMemoryPercent || process.env.QDONE_MAX_MEMORY_PERCENT || exports.defaults.maxMemoryPercent,
127
128
  // Idle Queues
128
- idleFor: options.idleFor || options['idle-for'] || exports.defaults.idleFor,
129
- delete: options.delete || exports.defaults.delete,
130
- unpair: options.delete || exports.defaults.unpair,
129
+ idleFor: options.idleFor || options['idle-for'] || process.env.QDONE_IDLE_FOR || exports.defaults.idleFor,
130
+ delete: options.delete || process.env.QDONE_DELETE === 'true' || exports.defaults.delete,
131
+ unpair: options.unpair || process.env.QDONE_UNPAIR === 'true' || exports.defaults.unpair,
131
132
  // Check
132
- create: options.create || exports.defaults.create,
133
- overwrite: options.overwrite || exports.defaults.overwrite
133
+ create: options.create || process.env.QDONE_CREATE === 'true' || exports.defaults.create,
134
+ overwrite: options.overwrite || process.env.QDONE_OVERWRITE === 'true' || exports.defaults.overwrite
134
135
  };
135
136
  // Setting this env here means we don't have to in AWS SDK constructors
136
137
  process.env.AWS_REGION = opt.region;
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.idleQueues = exports.processQueuePair = exports.processQueue = exports.deleteQueue = exports.checkIdle = exports.getMetric = exports.cheapIdleCheck = exports._cheapIdleCheck = exports.attributeNames = void 0;
6
+ exports.idleQueues = exports.processQueueSet = exports.processQueue = exports.deleteQueue = exports.checkIdle = exports.getMetric = exports.cheapIdleCheck = exports._cheapIdleCheck = exports.attributeNames = void 0;
7
7
  /**
8
8
  * Implementation of checks and caching of checks to determine if queues are idle.
9
9
  */
@@ -39,6 +39,7 @@ const metricNames = [
39
39
  * Actual SQS call, used in conjunction with cache.
40
40
  */
41
41
  async function _cheapIdleCheck(qname, qrl, opt) {
42
+ debug('_cheapIdleCheck', qname, qrl);
42
43
  try {
43
44
  const client = (0, sqs_js_1.getSQSClient)();
44
45
  const cmd = new client_sqs_1.GetQueueAttributesCommand({ AttributeNames: exports.attributeNames, QueueUrl: qrl });
@@ -48,12 +49,14 @@ async function _cheapIdleCheck(qname, qrl, opt) {
48
49
  result.queue = qname.slice(opt.prefix.length);
49
50
  // We are idle if all the messages attributes are zero
50
51
  result.idle = exports.attributeNames.filter(k => result[k] === '0').length === exports.attributeNames.length;
52
+ result.exists = true;
53
+ debug({ result, SQS: 1 });
51
54
  return { result, SQS: 1 };
52
55
  }
53
56
  catch (e) {
57
+ debug({ _cheapIdleCheck: e });
54
58
  if (e instanceof client_sqs_1.QueueDoesNotExist) {
55
- // Count deleted queues as idle
56
- return { result: { idle: true }, SQS: 1 };
59
+ return { result: { idle: undefined, exists: false }, SQS: 1 };
57
60
  }
58
61
  else {
59
62
  throw e;
@@ -66,6 +69,7 @@ exports._cheapIdleCheck = _cheapIdleCheck;
66
69
  * at this immediate moment.
67
70
  */
68
71
  async function cheapIdleCheck(qname, qrl, opt) {
72
+ debug('cheapIdleCheck', qname, qrl);
69
73
  // Just call the API if we don't have a cache
70
74
  if (!opt.cacheUri)
71
75
  return _cheapIdleCheck(qname, qrl, opt);
@@ -129,18 +133,19 @@ async function checkIdle(qname, qrl, opt) {
129
133
  debug('checkIdle', qname, qrl);
130
134
  const { result: cheapResult, SQS } = await cheapIdleCheck(qname, qrl, opt);
131
135
  debug('cheapResult', cheapResult);
132
- // Short circuit further calls if cheap result shows data
133
- if (cheapResult.idle === false) {
136
+ // Short circuit further calls if cheap result is conclusive
137
+ if (cheapResult.idle === false || cheapResult.exists === false) {
134
138
  return {
135
139
  queue: qname.slice(opt.prefix.length),
136
140
  cheap: cheapResult,
137
- idle: false,
141
+ idle: cheapResult.idle,
142
+ exists: cheapResult.exists,
138
143
  apiCalls: { SQS, CloudWatch: 0 }
139
144
  };
140
145
  }
141
146
  // If we get here, there's nothing in the queue at the moment,
142
147
  // so we have to check metrics one at a time
143
- const apiCalls = { SQS: 1, CloudWatch: 0 };
148
+ const apiCalls = { SQS, CloudWatch: 0 };
144
149
  const results = [];
145
150
  let idle = true;
146
151
  for (const metricName of metricNames) {
@@ -159,7 +164,8 @@ async function checkIdle(qname, qrl, opt) {
159
164
  queue: qname.slice(opt.prefix.length),
160
165
  cheap: cheapResult,
161
166
  apiCalls,
162
- idle
167
+ idle,
168
+ exists: true
163
169
  }, ...results // merge in results from CloudWatch
164
170
  );
165
171
  debug('checkIdle stats', stats);
@@ -211,90 +217,99 @@ async function processQueue(qname, qrl, opt) {
211
217
  }
212
218
  exports.processQueue = processQueue;
213
219
  /**
214
- * Processes a queue and its fail queue, treating them as a unit.
220
+ * Processes a queue and its fail and delete queue, treating them as a unit.
215
221
  */
216
- async function processQueuePair(qname, qrl, opt) {
222
+ async function processQueueSet(qname, qrl, opt) {
217
223
  const isFifo = qname.endsWith('.fifo');
218
224
  const normalizeOptions = Object.assign({}, opt, { fifo: isFifo });
225
+ // Generate DLQ name/url
226
+ const dqname = (0, qrlCache_js_1.normalizeDLQName)(qname, normalizeOptions);
227
+ const dqrl = (0, qrlCache_js_1.normalizeDLQName)(dqname, normalizeOptions);
219
228
  // Generate fail queue name/url
220
229
  const fqname = (0, qrlCache_js_1.normalizeFailQueueName)(qname, normalizeOptions);
221
230
  const fqrl = (0, qrlCache_js_1.normalizeFailQueueName)(fqname, normalizeOptions);
231
+ debug({ qname, qrl, dqname, dqrl, fqname, fqrl });
222
232
  // Idle check
223
- const result = await checkIdle(qname, qrl, opt);
224
- debug('result', result);
225
- // Queue is active
226
- const active = !result.idle;
227
- if (active) {
228
- if (opt.verbose)
229
- console.error(chalk_1.default.blue('Queue ') + qname.slice(opt.prefix.length) + chalk_1.default.blue(' has been ') + 'active' + chalk_1.default.blue(' in the last ') + opt.idleFor + chalk_1.default.blue(' minutes.'));
230
- return result;
231
- }
233
+ const qresult = await checkIdle(qname, qrl, opt);
234
+ const fqresult = await checkIdle(fqname, fqrl, opt);
235
+ const dqresult = await checkIdle(dqname, dqrl, opt);
236
+ debug({ qresult, fqresult, dqresult });
237
+ // Start building return value
238
+ const result = Object.assign({
239
+ queue: qname,
240
+ idle: (qresult.idle &&
241
+ (!fqresult.exists || fqresult.idle) &&
242
+ (!fqresult.exists || dqresult.idle)),
243
+ apiCalls: {
244
+ SQS: qresult.apiCalls.SQS + fqresult.apiCalls.SQS + dqresult.apiCalls.SQS,
245
+ CloudWatch: qresult.apiCalls.CloudWatch + fqresult.apiCalls.CloudWatch + dqresult.apiCalls.CloudWatch
246
+ }
247
+ });
232
248
  // Queue is idle
233
- if (opt.verbose)
249
+ if (qresult.idle && opt.verbose)
234
250
  console.error(chalk_1.default.blue('Queue ') + qname.slice(opt.prefix.length) + chalk_1.default.blue(' has been ') + 'idle' + chalk_1.default.blue(' for the last ') + opt.idleFor + chalk_1.default.blue(' minutes.'));
235
- // Check fail queue
236
- try {
237
- const fresult = await checkIdle(fqname, fqrl, opt);
238
- debug('fresult', fresult);
239
- const idleCheckResult = Object.assign(result, { idle: result.idle && fresult.idle, failq: fresult }, {
240
- apiCalls: {
241
- SQS: result.apiCalls.SQS + fresult.apiCalls.SQS,
242
- CloudWatch: result.apiCalls.CloudWatch + fresult.apiCalls.CloudWatch
251
+ if (fqresult.idle && opt.verbose)
252
+ console.error(chalk_1.default.blue('Queue ') + fqname.slice(opt.prefix.length) + chalk_1.default.blue(' has been ') + 'idle' + chalk_1.default.blue(' for the last ') + opt.idleFor + chalk_1.default.blue(' minutes.'));
253
+ if (dqresult.idle && opt.verbose)
254
+ console.error(chalk_1.default.blue('Queue ') + dqname.slice(opt.prefix.length) + chalk_1.default.blue(' has been ') + 'idle' + chalk_1.default.blue(' for the last ') + opt.idleFor + chalk_1.default.blue(' minutes.'));
255
+ // Delete if all are idle
256
+ const canDelete = ((qresult.idle || qresult.exists === false) &&
257
+ (fqresult.idle || fqresult.exists === false) &&
258
+ (dqresult.idle || dqresult.exists === false));
259
+ debug({ canDelete });
260
+ if (opt.delete && canDelete) {
261
+ // Normal
262
+ const qdresult = await (async () => {
263
+ debug({ qresult });
264
+ try {
265
+ if (qresult.idle)
266
+ return deleteQueue(qname, qrl, opt);
243
267
  }
244
- });
245
- // Queue is active
246
- const factive = !fresult.idle;
247
- if (factive) {
248
- if (opt.verbose)
249
- console.error(chalk_1.default.blue('Queue ') + fqname.slice(opt.prefix.length) + chalk_1.default.blue(' has been ') + 'active' + chalk_1.default.blue(' in the last ') + opt.idleFor + chalk_1.default.blue(' minutes.'));
250
- return idleCheckResult;
268
+ catch (e) {
269
+ if (!(e instanceof client_sqs_1.QueueDoesNotExist))
270
+ throw e;
271
+ }
272
+ })();
273
+ if (qdresult) {
274
+ result.apiCalls.SQS += qdresult.apiCalls.SQS;
251
275
  }
252
- // Queue is idle
253
- if (opt.verbose)
254
- console.error(chalk_1.default.blue('Queue ') + fqname.slice(opt.prefix.length) + chalk_1.default.blue(' has been ') + 'idle' + chalk_1.default.blue(' for the last ') + opt.idleFor + chalk_1.default.blue(' minutes.'));
255
- // Trigger a delete if the user wants it
256
- if (!opt.delete)
257
- return idleCheckResult;
258
- const [dresult, dfresult] = await Promise.all([
259
- deleteQueue(qname, qrl, opt),
260
- deleteQueue(fqname, fqrl, opt)
261
- ]);
262
- return Object.assign(idleCheckResult, {
263
- apiCalls: {
264
- // Sum the SQS calls across all four
265
- SQS: [result, fresult, dresult, dfresult]
266
- .map(r => r.apiCalls.SQS)
267
- .reduce((a, b) => a + b, 0),
268
- // Sum the CloudWatch calls across all four
269
- CloudWatch: [result, fresult, dresult, dfresult]
270
- .map(r => r.apiCalls.CloudWatch)
271
- .reduce((a, b) => a + b, 0)
276
+ debug({ qdresult });
277
+ // Fail
278
+ const fqdresult = await (async () => {
279
+ debug({ fqresult });
280
+ try {
281
+ if (fqresult.idle)
282
+ return deleteQueue(fqname, fqrl, opt);
272
283
  }
273
- });
274
- }
275
- catch (e) {
276
- // Handle the case where the fail queue has been deleted or was never
277
- // created for some reason
278
- if (!(e instanceof client_sqs_1.QueueDoesNotExist))
279
- throw e;
280
- // Fail queue doesn't exist if we get here
281
- if (opt.verbose)
282
- console.error(chalk_1.default.blue('Queue ') + fqname.slice(opt.prefix.length) + chalk_1.default.blue(' does not exist.'));
283
- // Handle delete
284
- if (!opt.delete)
285
- return result;
286
- const deleteResult = await deleteQueue(qname, qrl, opt);
287
- const resultIncludingDelete = Object.assign(result, {
288
- deleted: deleteResult.deleted,
289
- apiCalls: {
290
- SQS: result.apiCalls.SQS + deleteResult.apiCalls.SQS,
291
- CloudWatch: result.apiCalls.CloudWatch + deleteResult.apiCalls.CloudWatch
284
+ catch (e) {
285
+ if (!(e instanceof client_sqs_1.QueueDoesNotExist))
286
+ throw e;
292
287
  }
293
- });
294
- return resultIncludingDelete;
288
+ })();
289
+ if (fqdresult) {
290
+ result.apiCalls.SQS += fqdresult.apiCalls.SQS;
291
+ }
292
+ debug({ fqdresult });
293
+ // Dead
294
+ const dqdresult = await (async () => {
295
+ debug({ dqresult });
296
+ try {
297
+ if (dqresult.idle)
298
+ return deleteQueue(dqname, dqrl, opt);
299
+ }
300
+ catch (e) {
301
+ if (!(e instanceof client_sqs_1.QueueDoesNotExist))
302
+ throw e;
303
+ }
304
+ })();
305
+ if (dqdresult) {
306
+ result.apiCalls.SQS += dqdresult.apiCalls.SQS;
307
+ }
308
+ debug({ dqdresult });
295
309
  }
310
+ return result;
296
311
  }
297
- exports.processQueuePair = processQueuePair;
312
+ exports.processQueueSet = processQueueSet;
298
313
  //
299
314
  // Resolve queues for listening loop listen
300
315
  //
@@ -315,7 +330,11 @@ async function idleQueues(queues, options) {
315
330
  const sufFifo = opt.failSuffix + qrlCache_js_1.fifoSuffix;
316
331
  const isFail = entry.qname.endsWith(suf);
317
332
  const isFifoFail = entry.qname.endsWith(sufFifo);
318
- return opt.includeFailed ? true : (!isFail && !isFifoFail);
333
+ const sufDead = opt.dlqSuffix;
334
+ const sufFifoDead = opt.dlqSuffix + qrlCache_js_1.fifoSuffix;
335
+ const isDead = entry.qname.endsWith(sufDead);
336
+ const isFifoDead = entry.qname.endsWith(sufFifoDead);
337
+ return opt.includeFailed ? true : (!isFail && !isFifoFail && !isDead && !isFifoDead);
319
338
  });
320
339
  // But only if we have queues to remove
321
340
  if (filteredEntries.length) {
@@ -327,7 +346,7 @@ async function idleQueues(queues, options) {
327
346
  // Check each queue in parallel
328
347
  if (opt.unpair)
329
348
  return Promise.all(filteredEntries.map(e => processQueue(e.qname, e.qrl, opt)));
330
- return Promise.all(filteredEntries.map(e => processQueuePair(e.qname, e.qrl, opt)));
349
+ return Promise.all(filteredEntries.map(e => processQueueSet(e.qname, e.qrl, opt)));
331
350
  }
332
351
  // Otherwise, let caller know
333
352
  return 'noQueues';