qdone 2.0.51-alpha → 2.0.52-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.
package/README.md CHANGED
@@ -561,13 +561,8 @@ If a queue name ends with the * (wildcard) character, worker will listen on all
561
561
 
562
562
  -o, --idle-for number Minutes of inactivity after which a queue is considered
563
563
  idle. [default: 60]
564
- --delete Delete the queue if it is idle. The fail queue also must be
565
- idle unless you use --unpair.
566
- --unpair Treat queues and their fail queues as independent. By default
567
- they are treated as a unit.
568
- --include-failed When using '*' do not ignore fail queues. This option only
569
- applies if you use --unpair. Otherwise, queues and fail queues
570
- are treated as a unit.
564
+ --delete Delete the queue if it is idle along with its corresponding
565
+ fail queue and dlq.
571
566
  --prefix string Prefix to place at the front of each SQS queue name [default: qdone_]
572
567
  --fail-suffix string Suffix to append to each queue to generate fail queue name [default: _failed]
573
568
  --region string AWS region for Queues [default: us-east-1]
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.setupVerbose = exports.setupAWS = exports.getOptionsWithDefaults = exports.validateMessageOptions = exports.validateInteger = exports.defaults = void 0;
3
+ exports.setupVerbose = exports.setupAWS = exports.getOptionsWithDefaults = exports.validateMessageOptions = exports.validateQueueName = exports.validateInteger = exports.defaults = void 0;
4
4
  /**
5
5
  * Default options for qdone. Accepts a command line options object and
6
6
  * returns nicely-named options.
@@ -46,7 +46,6 @@ exports.defaults = Object.freeze({
46
46
  // Idle Queues
47
47
  idleFor: 60,
48
48
  delete: false,
49
- unpair: false,
50
49
  // Check
51
50
  create: false,
52
51
  overwrite: false
@@ -58,6 +57,14 @@ function validateInteger(opt, name) {
58
57
  return parsed;
59
58
  }
60
59
  exports.validateInteger = validateInteger;
60
+ function validateQueueName(opt, name) {
61
+ if (typeof name !== 'string')
62
+ throw new Error(`${name} must be a string.`);
63
+ if (!name.match(/^[a-z0-9-_]+$/i))
64
+ throw new Error(`${name} can contain only numbers, letters, hypens and underscores.`);
65
+ return name;
66
+ }
67
+ exports.validateQueueName = validateQueueName;
61
68
  function validateMessageOptions(messageOptions) {
62
69
  const validKeys = ['deduplicationId', 'groupId'];
63
70
  if (typeof messageOptions === 'object' &&
@@ -128,7 +135,6 @@ function getOptionsWithDefaults(options) {
128
135
  // Idle Queues
129
136
  idleFor: options.idleFor || options['idle-for'] || process.env.QDONE_IDLE_FOR || exports.defaults.idleFor,
130
137
  delete: options.delete || process.env.QDONE_DELETE === 'true' || exports.defaults.delete,
131
- unpair: options.unpair || process.env.QDONE_UNPAIR === 'true' || exports.defaults.unpair,
132
138
  // Check
133
139
  create: options.create || process.env.QDONE_CREATE === 'true' || exports.defaults.create,
134
140
  overwrite: options.overwrite || process.env.QDONE_OVERWRITE === 'true' || exports.defaults.overwrite
@@ -148,6 +154,10 @@ function getOptionsWithDefaults(options) {
148
154
  opt.maxConcurrentJobs = validateInteger(opt, 'maxConcurrentJobs');
149
155
  opt.maxMemoryPercent = validateInteger(opt, 'maxMemoryPercent');
150
156
  opt.idleFor = validateInteger(opt, 'idleFor');
157
+ validateQueueName(opt, 'region');
158
+ validateQueueName(opt, 'prefix');
159
+ validateQueueName(opt, 'failSuffix');
160
+ validateQueueName(opt, 'dlqSuffix');
151
161
  // Validate dedup args
152
162
  if (opt.externalDedup && !opt.cacheUri)
153
163
  throw new Error('--external-dedup requires the --cache-uri argument');
@@ -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.processQueueSet = exports.processQueue = exports.deleteQueue = exports.checkIdle = exports.getMetric = exports.cheapIdleCheck = exports._cheapIdleCheck = exports.attributeNames = void 0;
6
+ exports.idleQueues = exports.stripSuffixes = exports.processQueueSet = 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
  */
@@ -187,35 +187,6 @@ async function deleteQueue(qname, qrl, opt) {
187
187
  };
188
188
  }
189
189
  exports.deleteQueue = deleteQueue;
190
- /**
191
- * Processes a single queue, checking for idle, deleting if applicable.
192
- */
193
- async function processQueue(qname, qrl, opt) {
194
- const result = await checkIdle(qname, qrl, opt);
195
- debug(qname, result);
196
- // Queue is active
197
- if (!result.idle) {
198
- // Notify and return
199
- if (opt.verbose)
200
- 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.'));
201
- return result;
202
- }
203
- // Queue is idle
204
- if (opt.verbose)
205
- 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.'));
206
- if (opt.delete) {
207
- const deleteResult = await deleteQueue(qname, qrl, opt);
208
- const resultIncludingDelete = Object.assign(result, {
209
- deleted: deleteResult.deleted,
210
- apiCalls: {
211
- SQS: result.apiCalls.SQS + deleteResult.apiCalls.SQS,
212
- CloudWatch: result.apiCalls.CloudWatch + deleteResult.apiCalls.CloudWatch
213
- }
214
- });
215
- return resultIncludingDelete;
216
- }
217
- }
218
- exports.processQueue = processQueue;
219
190
  /**
220
191
  * Processes a queue and its fail and delete queue, treating them as a unit.
221
192
  */
@@ -311,6 +282,14 @@ async function processQueueSet(qname, qrl, opt) {
311
282
  }
312
283
  exports.processQueueSet = processQueueSet;
313
284
  //
285
+ // Strips failed and dlq suffix from a queue name or URL
286
+ //
287
+ function stripSuffixes(queueName, opt) {
288
+ const suffixFinder = new RegExp(`(${opt.dlqSuffix}|${opt.failSuffix}){1}(|${qrlCache_js_1.fifoSuffix})$`);
289
+ return queueName.replace(suffixFinder, '$2');
290
+ }
291
+ exports.stripSuffixes = stripSuffixes;
292
+ //
314
293
  // Resolve queues for listening loop listen
315
294
  //
316
295
  async function idleQueues(queues, options) {
@@ -324,17 +303,18 @@ async function idleQueues(queues, options) {
324
303
  console.error(chalk_1.default.blue(' done'));
325
304
  console.error();
326
305
  }
327
- // Filter out any queue ending in suffix unless --include-failed is set
306
+ // Filter out failed and dead queues, but if we have an orphaned fail or
307
+ // dead queue, keep the original parent queue name so that orphans can be
308
+ // deleted.
309
+ const queueNames = new Set();
328
310
  const filteredEntries = entries.filter(entry => {
329
- const suf = opt.failSuffix;
330
- const sufFifo = opt.failSuffix + qrlCache_js_1.fifoSuffix;
331
- const isFail = entry.qname.endsWith(suf);
332
- const isFifoFail = entry.qname.endsWith(sufFifo);
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);
311
+ const stripped = stripSuffixes(entry.qname, opt);
312
+ if (queueNames.has(stripped))
313
+ return false;
314
+ queueNames.add(stripped);
315
+ entry.qname = stripped;
316
+ entry.qrl = stripSuffixes(entry.qrl, opt);
317
+ return true;
338
318
  });
339
319
  // But only if we have queues to remove
340
320
  if (filteredEntries.length) {
@@ -344,8 +324,6 @@ async function idleQueues(queues, options) {
344
324
  console.error();
345
325
  }
346
326
  // Check each queue in parallel
347
- if (opt.unpair)
348
- return Promise.all(filteredEntries.map(e => processQueue(e.qname, e.qrl, opt)));
349
327
  return Promise.all(filteredEntries.map(e => processQueueSet(e.qname, e.qrl, opt)));
350
328
  }
351
329
  // Otherwise, let caller know
@@ -17,9 +17,11 @@ const debug = (0, debug_1.default)('qdone:monitor');
17
17
  * Splits a queue name with a single wildcard into prefix and suffix regex.
18
18
  */
19
19
  async function monitor(queue, save, options) {
20
+ if (queue.endsWith('.fifo'))
21
+ options.fifo = true;
20
22
  const opt = (0, defaults_js_1.getOptionsWithDefaults)(options);
21
23
  const queueName = (0, qrlCache_js_1.normalizeQueueName)(queue, opt);
22
- debug({ opt, queueName });
24
+ debug({ options, opt, queue, queueName });
23
25
  const data = await getAggregateData(queueName);
24
26
  console.log(data);
25
27
  if (save) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qdone",
3
- "version": "2.0.51-alpha",
3
+ "version": "2.0.52-alpha",
4
4
  "description": "A distributed scheduler for SQS",
5
5
  "type": "module",
6
6
  "main": "./index.js",
package/src/cli.js CHANGED
@@ -440,9 +440,7 @@ export async function worker (argv, testHook) {
440
440
  export async function idleQueues (argv, testHook) {
441
441
  const optionDefinitions = [
442
442
  { name: 'idle-for', alias: 'o', type: Number, defaultValue: defaults.idleFor, description: `Minutes of inactivity after which a queue is considered idle. [default: ${defaults.idleFor}]` },
443
- { name: 'delete', type: Boolean, description: 'Delete the queue if it is idle. The fail queue also must be idle unless you use --unpair.' },
444
- { name: 'unpair', type: Boolean, description: 'Treat queues and their fail queues as independent. By default they are treated as a unit.' },
445
- { name: 'include-failed', type: Boolean, description: 'When using \'*\' do not ignore fail queues. This option only applies if you use --unpair. Otherwise, queues and fail queues are treated as a unit.' }
443
+ { name: 'delete', type: Boolean, description: 'Delete the queue if it is idle along with its corresponding fail queue and dlq.' }
446
444
  ].concat(globalOptionDefinitions)
447
445
 
448
446
  const usageSections = [
package/src/defaults.js CHANGED
@@ -47,7 +47,6 @@ export const defaults = Object.freeze({
47
47
  // Idle Queues
48
48
  idleFor: 60,
49
49
  delete: false,
50
- unpair: false,
51
50
 
52
51
  // Check
53
52
  create: false,
@@ -60,6 +59,12 @@ export function validateInteger (opt, name) {
60
59
  return parsed
61
60
  }
62
61
 
62
+ export function validateQueueName (opt, name) {
63
+ if (typeof name !== 'string') throw new Error(`${name} must be a string.`)
64
+ if (!name.match(/^[a-z0-9-_]+$/i)) throw new Error(`${name} can contain only numbers, letters, hypens and underscores.`)
65
+ return name
66
+ }
67
+
63
68
  export function validateMessageOptions (messageOptions) {
64
69
  const validKeys = ['deduplicationId', 'groupId']
65
70
  if (typeof messageOptions === 'object' &&
@@ -134,7 +139,6 @@ export function getOptionsWithDefaults (options) {
134
139
  // Idle Queues
135
140
  idleFor: options.idleFor || options['idle-for'] || process.env.QDONE_IDLE_FOR || defaults.idleFor,
136
141
  delete: options.delete || process.env.QDONE_DELETE === 'true' || defaults.delete,
137
- unpair: options.unpair || process.env.QDONE_UNPAIR === 'true' || defaults.unpair,
138
142
 
139
143
  // Check
140
144
  create: options.create || process.env.QDONE_CREATE === 'true' || defaults.create,
@@ -158,6 +162,11 @@ export function getOptionsWithDefaults (options) {
158
162
  opt.maxMemoryPercent = validateInteger(opt, 'maxMemoryPercent')
159
163
  opt.idleFor = validateInteger(opt, 'idleFor')
160
164
 
165
+ validateQueueName(opt, 'region')
166
+ validateQueueName(opt, 'prefix')
167
+ validateQueueName(opt, 'failSuffix')
168
+ validateQueueName(opt, 'dlqSuffix')
169
+
161
170
  // Validate dedup args
162
171
  if (opt.externalDedup && !opt.cacheUri) throw new Error('--external-dedup requires the --cache-uri argument')
163
172
  if (opt.externalDedup && (opt.dedupPeriod < 1)) throw new Error('--external-dedup of redis requires a --dedup-period > 1 second')
package/src/idleQueues.js CHANGED
@@ -186,35 +186,6 @@ export async function deleteQueue (qname, qrl, opt) {
186
186
  }
187
187
  }
188
188
 
189
- /**
190
- * Processes a single queue, checking for idle, deleting if applicable.
191
- */
192
- export async function processQueue (qname, qrl, opt) {
193
- const result = await checkIdle(qname, qrl, opt)
194
- debug(qname, result)
195
-
196
- // Queue is active
197
- if (!result.idle) {
198
- // Notify and return
199
- if (opt.verbose) console.error(chalk.blue('Queue ') + qname.slice(opt.prefix.length) + chalk.blue(' has been ') + 'active' + chalk.blue(' in the last ') + opt.idleFor + chalk.blue(' minutes.'))
200
- return result
201
- }
202
-
203
- // Queue is idle
204
- if (opt.verbose) console.error(chalk.blue('Queue ') + qname.slice(opt.prefix.length) + chalk.blue(' has been ') + 'idle' + chalk.blue(' for the last ') + opt.idleFor + chalk.blue(' minutes.'))
205
- if (opt.delete) {
206
- const deleteResult = await deleteQueue(qname, qrl, opt)
207
- const resultIncludingDelete = Object.assign(result, {
208
- deleted: deleteResult.deleted,
209
- apiCalls: {
210
- SQS: result.apiCalls.SQS + deleteResult.apiCalls.SQS,
211
- CloudWatch: result.apiCalls.CloudWatch + deleteResult.apiCalls.CloudWatch
212
- }
213
- })
214
- return resultIncludingDelete
215
- }
216
- }
217
-
218
189
  /**
219
190
  * Processes a queue and its fail and delete queue, treating them as a unit.
220
191
  */
@@ -308,6 +279,14 @@ export async function processQueueSet (qname, qrl, opt) {
308
279
  return result
309
280
  }
310
281
 
282
+ //
283
+ // Strips failed and dlq suffix from a queue name or URL
284
+ //
285
+ export function stripSuffixes (queueName, opt) {
286
+ const suffixFinder = new RegExp(`(${opt.dlqSuffix}|${opt.failSuffix}){1}(|${fifoSuffix})$`)
287
+ return queueName.replace(suffixFinder, '$2')
288
+ }
289
+
311
290
  //
312
291
  // Resolve queues for listening loop listen
313
292
  //
@@ -322,17 +301,17 @@ export async function idleQueues (queues, options) {
322
301
  console.error()
323
302
  }
324
303
 
325
- // Filter out any queue ending in suffix unless --include-failed is set
304
+ // Filter out failed and dead queues, but if we have an orphaned fail or
305
+ // dead queue, keep the original parent queue name so that orphans can be
306
+ // deleted.
307
+ const queueNames = new Set()
326
308
  const filteredEntries = entries.filter(entry => {
327
- const suf = opt.failSuffix
328
- const sufFifo = opt.failSuffix + fifoSuffix
329
- const isFail = entry.qname.endsWith(suf)
330
- const isFifoFail = entry.qname.endsWith(sufFifo)
331
- const sufDead = opt.dlqSuffix
332
- const sufFifoDead = opt.dlqSuffix + fifoSuffix
333
- const isDead = entry.qname.endsWith(sufDead)
334
- const isFifoDead = entry.qname.endsWith(sufFifoDead)
335
- return opt.includeFailed ? true : (!isFail && !isFifoFail && !isDead && !isFifoDead)
309
+ const stripped = stripSuffixes(entry.qname, opt)
310
+ if (queueNames.has(stripped)) return false
311
+ queueNames.add(stripped)
312
+ entry.qname = stripped
313
+ entry.qrl = stripSuffixes(entry.qrl, opt)
314
+ return true
336
315
  })
337
316
 
338
317
  // But only if we have queues to remove
@@ -345,7 +324,6 @@ export async function idleQueues (queues, options) {
345
324
  console.error()
346
325
  }
347
326
  // Check each queue in parallel
348
- if (opt.unpair) return Promise.all(filteredEntries.map(e => processQueue(e.qname, e.qrl, opt)))
349
327
  return Promise.all(filteredEntries.map(e => processQueueSet(e.qname, e.qrl, opt)))
350
328
  }
351
329
 
package/src/monitor.js CHANGED
@@ -13,9 +13,10 @@ const debug = Debug('qdone:monitor')
13
13
  * Splits a queue name with a single wildcard into prefix and suffix regex.
14
14
  */
15
15
  export async function monitor (queue, save, options) {
16
+ if (queue.endsWith('.fifo')) options.fifo = true
16
17
  const opt = getOptionsWithDefaults(options)
17
18
  const queueName = normalizeQueueName(queue, opt)
18
- debug({ opt, queueName })
19
+ debug({ options, opt, queue, queueName })
19
20
  const data = await getAggregateData(queueName)
20
21
  console.log(data)
21
22
  if (save) {