qdone 2.0.28-alpha → 2.0.30-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.
@@ -2,55 +2,19 @@
2
2
  /**
3
3
  * Functions that deal with SQS
4
4
  */
5
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
6
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
7
- return new (P || (P = Promise))(function (resolve, reject) {
8
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
9
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
10
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
11
- step((generator = generator.apply(thisArg, _arguments || [])).next());
12
- });
13
- };
14
- var __generator = (this && this.__generator) || function (thisArg, body) {
15
- var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
16
- return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
17
- function verb(n) { return function (v) { return step([n, v]); }; }
18
- function step(op) {
19
- if (f) throw new TypeError("Generator is already executing.");
20
- while (g && (g = 0, op[0] && (_ = 0)), _) try {
21
- if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
22
- if (y = 0, t) op = [op[0] & 2, t.value];
23
- switch (op[0]) {
24
- case 0: case 1: t = op; break;
25
- case 4: _.label++; return { value: op[1], done: false };
26
- case 5: _.label++; y = op[1]; op = [0]; continue;
27
- case 7: op = _.ops.pop(); _.trys.pop(); continue;
28
- default:
29
- if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
30
- if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
31
- if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
32
- if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
33
- if (t[2]) _.ops.pop();
34
- _.trys.pop(); continue;
35
- }
36
- op = body.call(thisArg, _);
37
- } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
38
- if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
39
- }
40
- };
41
5
  var __importDefault = (this && this.__importDefault) || function (mod) {
42
6
  return (mod && mod.__esModule) ? mod : { "default": mod };
43
7
  };
44
8
  Object.defineProperty(exports, "__esModule", { value: true });
45
9
  exports.getQueueAttributes = exports.getMatchingQueues = exports.setSQSClient = exports.getSQSClient = void 0;
46
- var client_sqs_1 = require("@aws-sdk/client-sqs");
47
- var path_1 = require("path");
48
- var debug_1 = __importDefault(require("debug"));
49
- var debug = (0, debug_1.default)('qdone:sqs');
10
+ const client_sqs_1 = require("@aws-sdk/client-sqs");
11
+ const path_1 = require("path");
12
+ const debug_1 = __importDefault(require("debug"));
13
+ const debug = (0, debug_1.default)('qdone:sqs');
50
14
  /**
51
15
  * Utility function to return an instantiated, shared SQSClient.
52
16
  */
53
- var client;
17
+ let client;
54
18
  function getSQSClient() {
55
19
  if (client)
56
20
  return client;
@@ -68,113 +32,66 @@ exports.setSQSClient = setSQSClient;
68
32
  /**
69
33
  * Returns qrls for queues matching the given prefix and regex.
70
34
  */
71
- function getMatchingQueues(prefix, regex) {
72
- return __awaiter(this, void 0, void 0, function () {
73
- function processQueues(nextToken) {
74
- return __awaiter(this, void 0, void 0, function () {
75
- var command, result, qrls, nextToken2, _a, _b, _c;
76
- return __generator(this, function (_d) {
77
- switch (_d.label) {
78
- case 0:
79
- if (nextToken)
80
- input.NextToken = nextToken;
81
- command = new client_sqs_1.ListQueuesCommand(input);
82
- return [4 /*yield*/, client.send(command)
83
- // debug({ result })
84
- ];
85
- case 1:
86
- result = _d.sent();
87
- qrls = result.QueueUrls, nextToken2 = result.NextToken;
88
- _b = (_a = (qrls || []).filter(function (q) { return regex.test(q); })).concat;
89
- if (!nextToken2) return [3 /*break*/, 3];
90
- return [4 /*yield*/, processQueues(nextToken2)];
91
- case 2:
92
- _c = _d.sent();
93
- return [3 /*break*/, 4];
94
- case 3:
95
- _c = [];
96
- _d.label = 4;
97
- case 4:
98
- // debug({ qrls, nextToken2 })
99
- return [2 /*return*/, _b.apply(_a, [_c])];
100
- }
101
- });
102
- });
103
- }
104
- var input, client;
105
- return __generator(this, function (_a) {
106
- input = { QueueNamePrefix: prefix, MaxResults: 1000 };
107
- client = getSQSClient();
108
- return [2 /*return*/, processQueues()];
109
- });
110
- });
35
+ async function getMatchingQueues(prefix, regex) {
36
+ const input = { QueueNamePrefix: prefix, MaxResults: 1000 };
37
+ const client = getSQSClient();
38
+ async function processQueues(nextToken) {
39
+ if (nextToken)
40
+ input.NextToken = nextToken;
41
+ const command = new client_sqs_1.ListQueuesCommand(input);
42
+ // debug({ nextToken, input, command })
43
+ const result = await client.send(command);
44
+ // debug({ result })
45
+ const { QueueUrls: qrls, NextToken: nextToken2 } = result;
46
+ // debug({ qrls, nextToken2 })
47
+ return (qrls || []).filter(q => regex.test(q)).concat(nextToken2 ? await processQueues(nextToken2) : []);
48
+ }
49
+ return processQueues();
111
50
  }
112
51
  exports.getMatchingQueues = getMatchingQueues;
113
52
  /**
114
53
  * Gets attributes on every queue in parallel.
115
54
  */
116
- function getQueueAttributes(qrls) {
117
- return __awaiter(this, void 0, void 0, function () {
118
- var promises, _loop_1, _i, qrls_1, qrl;
119
- var _this = this;
120
- return __generator(this, function (_a) {
121
- promises = [];
122
- _loop_1 = function (qrl) {
123
- var input = {
124
- QueueUrl: qrl,
125
- AttributeNames: [
126
- 'ApproximateNumberOfMessages',
127
- 'ApproximateNumberOfMessagesNotVisible',
128
- 'ApproximateNumberOfMessagesDelayed'
129
- ]
130
- };
131
- var command = new client_sqs_1.GetQueueAttributesCommand(input);
132
- // debug({ input, command })
133
- promises.push((function () { return __awaiter(_this, void 0, void 0, function () {
134
- var queue, result, e_1;
135
- return __generator(this, function (_a) {
136
- switch (_a.label) {
137
- case 0:
138
- queue = (0, path_1.basename)(qrl);
139
- _a.label = 1;
140
- case 1:
141
- _a.trys.push([1, 3, , 4]);
142
- return [4 /*yield*/, client.send(command)
143
- // debug({ queue, result })
144
- ];
145
- case 2:
146
- result = _a.sent();
147
- // debug({ queue, result })
148
- return [2 /*return*/, { queue: queue, result: result }];
149
- case 3:
150
- e_1 = _a.sent();
151
- if (e_1 instanceof client_sqs_1.QueueDoesNotExist) {
152
- // For queues that have been deleted in the meantime for whatever
153
- // reason, just show as having no messages instead of failing the
154
- // whole batch
155
- return [2 /*return*/, {
156
- queue: queue,
157
- Attributes: {
158
- ApproximateNumberOfMessages: '0',
159
- ApproximateNumberOfMessagesNotVisible: '0',
160
- ApproximateNumberOfMessagesDelayed: '0'
161
- }
162
- }];
163
- }
164
- throw e_1;
165
- case 4: return [2 /*return*/];
55
+ async function getQueueAttributes(qrls) {
56
+ const promises = [];
57
+ // debug({ qrls })
58
+ for (const qrl of qrls) {
59
+ const input = {
60
+ QueueUrl: qrl,
61
+ AttributeNames: [
62
+ 'ApproximateNumberOfMessages',
63
+ 'ApproximateNumberOfMessagesNotVisible',
64
+ 'ApproximateNumberOfMessagesDelayed'
65
+ ]
66
+ };
67
+ const command = new client_sqs_1.GetQueueAttributesCommand(input);
68
+ // debug({ input, command })
69
+ promises.push((async () => {
70
+ const queue = (0, path_1.basename)(qrl);
71
+ try {
72
+ const result = await client.send(command);
73
+ // debug({ queue, result })
74
+ return { queue, result };
75
+ }
76
+ catch (e) {
77
+ if (e instanceof client_sqs_1.QueueDoesNotExist) {
78
+ // For queues that have been deleted in the meantime for whatever
79
+ // reason, just show as having no messages instead of failing the
80
+ // whole batch
81
+ return {
82
+ queue,
83
+ Attributes: {
84
+ ApproximateNumberOfMessages: '0',
85
+ ApproximateNumberOfMessagesNotVisible: '0',
86
+ ApproximateNumberOfMessagesDelayed: '0'
166
87
  }
167
- });
168
- }); })());
169
- };
170
- // debug({ qrls })
171
- for (_i = 0, qrls_1 = qrls; _i < qrls_1.length; _i++) {
172
- qrl = qrls_1[_i];
173
- _loop_1(qrl);
88
+ };
89
+ }
90
+ throw e;
174
91
  }
175
- return [2 /*return*/, Promise.all(promises)];
176
- });
177
- });
92
+ })());
93
+ }
94
+ return Promise.all(promises);
178
95
  }
179
96
  exports.getQueueAttributes = getQueueAttributes;
180
97
  debug('loaded');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "qdone",
3
- "version": "2.0.28-alpha",
3
+ "version": "2.0.30-alpha",
4
4
  "description": "Language agnostic job queue for SQS",
5
5
  "type": "module",
6
6
  "main": "./index.js",
@@ -47,7 +47,7 @@
47
47
  "scripts": {
48
48
  "start": "./src/bin.js",
49
49
  "test": "NODE_OPTIONS='--experimental-json-modules --experimental-vm-modules --no-warnings' jest",
50
- "build": "tsc --allowJs index.js --outdir commonjs --esModuleInterop --module commonjs",
50
+ "build": "tsc --allowJs index.js --outdir commonjs --esModuleInterop --module commonjs --target es2021 --strict --skipLibCheck --forceConsistentCasingInFileNames",
51
51
  "clean": "rm -rf commonjs/src commonjs/*.js coverage",
52
52
  "lint": "standard",
53
53
  "coverage": "nyc report --reporter=text-lcov | coveralls",
package/src/consumer.js CHANGED
@@ -96,9 +96,7 @@ export async function processMessages (queues, callback, options) {
96
96
 
97
97
  if (!shutdownRequested) {
98
98
  if (messages.length) {
99
- for (const message of messages) {
100
- jobExecutor.executeJob(message, callback, qname, qrl, () => queueManager.updateIcehouse(qrl, true))
101
- }
99
+ jobExecutor.executeJobs(messages, callback, qname, qrl)
102
100
  queueManager.updateIcehouse(qrl, false)
103
101
  } else {
104
102
  // If we didn't get any, update the icehouse so we can back off
@@ -107,7 +105,7 @@ export async function processMessages (queues, callback, options) {
107
105
  }
108
106
 
109
107
  // Max job accounting
110
- maxReturnCount -= maxMessages
108
+ if (messages.length) maxReturnCount -= messages.length
111
109
  activeQrls.delete(qrl)
112
110
  } catch (e) {
113
111
  // If the queue has been cleaned up, we should back off anyway
@@ -146,7 +144,7 @@ export async function processMessages (queues, callback, options) {
146
144
  let jobsLeft = targetJobs
147
145
 
148
146
  if (opt.verbose) {
149
- console.error({ jobCount: jobExecutor.activeJobCount(), freeMemory, totalMemory, freememThreshold, remainingMemory, memoryThreshold, maxReturnCount, allowedJobs, maxLatency, latency, latencyFactor, freememFactor, oneMinuteLoad, loadPerCore, loadFactor, overallFactor, targetJobs, activeQrls })
147
+ // console.error({ maxConcurrentJobs: opt.maxConcurrentJobs, jobCount: jobExecutor.activeJobCount(), allowedJobs, maxLatency, latencyFactor, freememFactor, loadFactor, overallFactor, targetJobs, activeQrls })
150
148
  }
151
149
  for (const { qname, qrl } of queueManager.getPairs()) {
152
150
  // debug({ evaluating: { qname, qrl, jobsLeft, activeQrlsHasQrl: activeQrls.has(qrl) } })
package/src/defaults.js CHANGED
@@ -26,8 +26,8 @@ export const defaults = Object.freeze({
26
26
  messageRetentionPeriod: 1209600,
27
27
  delay: 0,
28
28
  sendRetries: 6,
29
- failDelay: 0,
30
- dlq: false,
29
+ failDelay: 120,
30
+ dlq: true,
31
31
  dlqSuffix: '_dead',
32
32
  dlqAfter: 3,
33
33
 
@@ -90,7 +90,7 @@ export function getOptionsWithDefaults (options) {
90
90
  delay: options.delay || defaults.delay,
91
91
  sendRetries: options['send-retries'] || defaults.sendRetries,
92
92
  failDelay: options.failDelay || options['fail-delay'] || defaults.failDelay,
93
- dlq: dlq || defaults.dlq,
93
+ dlq: dlq === false ? false : (dlq || defaults.dlq),
94
94
  dlqSuffix: options.dlqSuffix || options['dlq-suffix'] || defaults.dlqSuffix,
95
95
  dlqAfter: options.dlqAfter || options['dlq-after'] || defaults.dlqAfter,
96
96
  tags: options.tags || undefined,
package/src/enqueue.js CHANGED
@@ -79,7 +79,7 @@ export async function getOrCreateFailQueue (queue, opt) {
79
79
  maxReceiveCount: opt.dlqAfter + ''
80
80
  })
81
81
  }
82
- if (opt.failDelay) params.Attributes.DelaySeconds = opt.failDelay
82
+ if (opt.failDelay) params.Attributes.DelaySeconds = opt.failDelay + ''
83
83
  if (opt.tags) params.tags = opt.tags
84
84
  if (opt.fifo) params.Attributes.FifoQueue = 'true'
85
85
  const cmd = new CreateQueueCommand(params)
@@ -21,6 +21,8 @@ export class JobExecutor {
21
21
  this.jobsByMessageId = {}
22
22
  this.stats = {
23
23
  activeJobs: 0,
24
+ waitingJobs: 0,
25
+ runningJobs: 0,
24
26
  sqsCalls: 0,
25
27
  timeoutsExtended: 0,
26
28
  jobsSucceeded: 0,
@@ -71,15 +73,12 @@ export class JobExecutor {
71
73
  const jobsToDeleteByQrl = {}
72
74
  const jobsToCleanup = new Set()
73
75
 
74
- if (this.opt.verbose) {
75
- console.error(chalk.blue('Stats: '), this.stats)
76
- console.error(chalk.blue('Running: '), this.jobs.filter(j => j.status === 'processing').map(({ qname, message }) => ({ qname, payload: message.Body })))
77
- }
78
-
79
76
  // Build list of jobs we need to deal with
77
+ const jobStatuses = {}
80
78
  for (let i = 0; i < this.jobs.length; i++) {
81
79
  const job = this.jobs[i]
82
80
  const jobRunTime = Math.round((start - job.start) / 1000)
81
+ jobStatuses[job.status] = (jobStatuses[job.status] || 0) + 1
83
82
  // debug('considering job', job)
84
83
  if (job.status === 'complete') {
85
84
  const jobsToDelete = jobsToDeleteByQrl[job.qrl] || []
@@ -88,7 +87,8 @@ export class JobExecutor {
88
87
  jobsToDeleteByQrl[job.qrl] = jobsToDelete
89
88
  } else if (job.status === 'failed') {
90
89
  jobsToCleanup.add(job)
91
- } else if (job.status === 'processing') {
90
+ } else if (job.status !== 'deleting') {
91
+ // Any other job state gets visibility accounting
92
92
  debug('processing', { job, jobRunTime })
93
93
  if (jobRunTime >= job.extendAtSecond) {
94
94
  // Add it to our organized list of jobs
@@ -106,7 +106,11 @@ export class JobExecutor {
106
106
  }
107
107
  }
108
108
  }
109
- // debug('maintainVisibility', { jobsToDeleteByQrl, jobsToExtendByQrl })
109
+
110
+ if (this.opt.verbose) {
111
+ console.error(chalk.blue('Stats: '), { stats: this.stats, jobStatuses })
112
+ console.error(chalk.blue('Running: '), this.jobs.filter(j => j.status === 'processing').map(({ qname, message }) => ({ qname, payload: message.Body })))
113
+ }
110
114
 
111
115
  // Extend in batches for each queue
112
116
  for (const qrl in jobsToExtendByQrl) {
@@ -207,20 +211,19 @@ export class JobExecutor {
207
211
  })
208
212
  }
209
213
 
210
- async executeJob (message, callback, qname, qrl, failedCallback) {
211
- if (this.shutdownRequested) throw new Error('jobExecutor is shutting down so cannot execute new job')
214
+ addJob (message, callback, qname, qrl) {
212
215
  // Create job entry and track it
213
- const payload = this.opt.json ? JSON.parse(message.Body) : message.Body
214
- const visibilityTimeout = 60
216
+ const defaultVisibilityTimeout = 60
215
217
  const job = {
216
- status: 'processing',
218
+ status: 'waiting',
217
219
  start: new Date(),
218
- visibilityTimeout,
219
- extendAtSecond: visibilityTimeout / 2,
220
+ visibilityTimeout: defaultVisibilityTimeout,
221
+ extendAtSecond: defaultVisibilityTimeout / 2,
220
222
  payload: this.opt.json ? JSON.parse(message.Body) : message.Body,
221
223
  message,
222
224
  callback,
223
225
  qname,
226
+ prettyQname: qname.slice(this.opt.prefix.length),
224
227
  qrl
225
228
  }
226
229
 
@@ -237,29 +240,45 @@ export class JobExecutor {
237
240
  throw e
238
241
  }
239
242
 
240
- // debug('executeJob', job)
241
243
  this.jobs.push(job)
242
244
  this.jobsByMessageId[job.message.MessageId] = job
243
245
  this.stats.activeJobs++
246
+ this.stats.waitingJobs++
244
247
  if (this.opt.verbose) {
245
- console.error(chalk.blue('Executing:'), qname, chalk.blue('-->'), job.payload)
248
+ console.error(chalk.blue('Got message:'), job.prettyQname, chalk.blue('-->'), job.payload, job.message.MessageId)
246
249
  } else if (!this.opt.disableLog) {
247
250
  console.log(JSON.stringify({
248
- event: 'MESSAGE_PROCESSING_START',
251
+ event: 'MESSAGE_RECEIVED',
249
252
  timestamp: new Date(),
250
- qrl,
253
+ queue: job.qname,
251
254
  messageId: message.MessageId,
252
255
  payload: job.payload
253
256
  }))
254
257
  }
258
+ return job
259
+ }
255
260
 
256
- // Execute job
261
+ async runJob (job) {
257
262
  try {
258
- const queue = qname.slice(this.opt.prefix.length)
259
- const result = await callback(queue, payload)
260
- debug('executeJob callback finished', { payload, result })
261
263
  if (this.opt.verbose) {
262
- console.error(chalk.green('SUCCESS'), message.Body)
264
+ console.error(chalk.blue('Running:'), job.prettyQname, chalk.blue('-->'), job.payload, job.message.MessageId)
265
+ } else if (!this.opt.disableLog) {
266
+ console.log(JSON.stringify({
267
+ event: 'MESSAGE_PROCESSING_START',
268
+ timestamp: new Date(),
269
+ queue: job.qname,
270
+ messageId: job.message.MessageId,
271
+ payload: job.payload
272
+ }))
273
+ }
274
+ job.status = 'running'
275
+ this.stats.runningJobs++
276
+ this.stats.waitingJobs--
277
+ const queue = job.qname.slice(this.opt.prefix.length)
278
+ const result = await job.callback(queue, job.payload)
279
+ debug('executeJob callback finished', { payload: job.payload, result })
280
+ if (this.opt.verbose) {
281
+ console.error(chalk.green('SUCCESS'), job.payload)
263
282
  }
264
283
  job.status = 'complete'
265
284
 
@@ -269,35 +288,60 @@ export class JobExecutor {
269
288
  } else if (!this.opt.disableLog) {
270
289
  console.log(JSON.stringify({
271
290
  event: 'MESSAGE_PROCESSING_COMPLETE',
291
+ queue: job.qname,
272
292
  timestamp: new Date(),
273
- messageId: message.MessageId,
274
- payload
293
+ messageId: job.message.MessageId,
294
+ payload: job.payload
275
295
  }))
276
296
  }
277
297
  this.stats.jobsSucceeded++
278
298
  } catch (err) {
279
- // Notify caller that we failed
280
- if (failedCallback) failedCallback(message, qname, qrl)
299
+ job.status = 'failed'
300
+ this.stats.jobsFailed++
281
301
  // Fail path for job execution
282
302
  if (this.opt.verbose) {
283
- console.error(chalk.red('FAILED'), message.Body)
303
+ console.error(chalk.red('FAILED'), job.payload)
284
304
  console.error(chalk.blue(' error : ') + err)
285
305
  } else if (!this.opt.disableLog) {
286
306
  // Production error logging
287
307
  console.log(JSON.stringify({
288
308
  event: 'MESSAGE_PROCESSING_FAILED',
289
309
  reason: 'exception thrown',
290
- qrl,
310
+ queue: job.qname,
291
311
  timestamp: new Date(),
292
- messageId: message.MessageId,
293
- payload,
312
+ messageId: job.message.MessageId,
313
+ payload: job.payload,
294
314
  errorMessage: err.toString().split('\n').slice(1).join('\n').trim() || undefined,
295
315
  err
296
316
  }))
297
317
  }
298
- job.status = 'failed'
299
- this.stats.jobsFailed++
300
318
  }
301
319
  this.stats.activeJobs--
320
+ this.stats.runningJobs--
321
+ }
322
+
323
+ async executeJobs (messages, callback, qname, qrl) {
324
+ if (this.shutdownRequested) throw new Error('jobExecutor is shutting down so cannot execute new jobs')
325
+
326
+ // Begin tracking jobs
327
+ const jobs = messages.map(message => this.addJob(message, callback, qname, qrl))
328
+ const isFifo = qrl.endsWith('.fifo')
329
+
330
+ // console.log(jobs)
331
+
332
+ // Begin executing
333
+ for (const [job, i] of jobs.map((job, i) => [job, i])) {
334
+ // Figure out if the next job needs to happen in serial, otherwise we can parallel execute
335
+ // const job = jobs[i]
336
+ const nextJob = jobs[i + 1]
337
+ const nextJobIsSerial =
338
+ isFifo && nextJob &&
339
+ job.message?.Attributes?.GroupId === nextJob.message?.Attributes?.GroupId
340
+
341
+ console.log({ i, nextJobAtt: nextJob.message.Attributes, nextJobIsSerial })
342
+ // Execute serial or parallel
343
+ if (nextJobIsSerial) await this.runJob(job)
344
+ else this.runJob(job)
345
+ }
302
346
  }
303
347
  }
@@ -35,7 +35,7 @@ export class QueueManager {
35
35
  const now = new Date()
36
36
  const secondsElapsed = lastCheck - now
37
37
  const minWait = 10
38
- const maxWait = 600
38
+ const maxWait = 120
39
39
  const baseSeconds = numEmptyReceives ** 2 * 20
40
40
  const jitterSeconds = Math.round((Math.random() - 0.5) * baseSeconds)
41
41
  const newSecondsToWait = Math.max(minWait, Math.min(maxWait, baseSeconds + jitterSeconds))
@@ -57,8 +57,11 @@ export class QueueManager {
57
57
  const secondsElapsed = Math.round((now - lastCheck) / 1000)
58
58
  debug({ icehouseCheck: { qrl, lastCheck, secondsToWait, secondsElapsed } })
59
59
  const letOut = secondsElapsed > secondsToWait
60
- if (letOut && this.opt.verbose) {
61
- console.error(chalk.blue('Coming out of icehouse:'), qrl)
60
+ if (letOut) {
61
+ delete this.icehouse[qrl]
62
+ if (this.opt.verbose) {
63
+ console.error(chalk.blue('Coming out of icehouse:'), qrl)
64
+ }
62
65
  }
63
66
  return !letOut
64
67
  } else {
@@ -79,7 +82,7 @@ export class QueueManager {
79
82
  // Start processing
80
83
  const qnames = this.queues.map(queue => normalizeQueueName(queue, this.opt))
81
84
  const pairs = await getQnameUrlPairs(qnames, this.opt)
82
- if (this.opt.verbose) console.error(chalk.blue('Resolving queues:'))
85
+ if (this.opt.verbose) console.error(chalk.blue('Resolving queues:'), pairs.map(({ qname }) => qname))
83
86
 
84
87
  if (this.shutdownRequested) return
85
88
 
@@ -97,6 +100,12 @@ export class QueueManager {
97
100
  const isFifo = qname.endsWith('.fifo')
98
101
  return this.opt.fifo ? isFifo : true
99
102
  })
103
+ // next dead
104
+ .filter(({ qname, qrl }) => {
105
+ const isFifo = qname.endsWith('.fifo')
106
+ const isDead = isFifo ? qname.endsWith('_dead.fifo') : qname.endsWith('_dead')
107
+ return !isDead
108
+ })
100
109
  // then icehouse
101
110
  .filter(({ qname, qrl }) => !this.keepInIcehouse(qrl, now))
102
111