qdone 2.0.12-alpha → 2.0.14-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/commonjs/src/consumer.js +91 -29
- package/commonjs/src/defaults.js +20 -1
- package/commonjs/src/idleQueues.js +2 -3
- package/commonjs/src/scheduler/jobExecutor.js +136 -100
- package/commonjs/src/scheduler/queueManager.js +116 -35
- package/commonjs/src/scheduler/systemMonitor.js +21 -37
- package/package.json +1 -1
- package/src/consumer.js +50 -22
- package/src/defaults.js +22 -1
- package/src/idleQueues.js +1 -1
- package/src/scheduler/jobExecutor.js +95 -51
- package/src/scheduler/queueManager.js +86 -23
- package/src/scheduler/systemMonitor.js +18 -32
package/commonjs/src/consumer.js
CHANGED
|
@@ -56,15 +56,34 @@ var debug = (0, debug_1.default)('qdone:consumer');
|
|
|
56
56
|
var shutdownRequested = false;
|
|
57
57
|
var shutdownCallbacks = [];
|
|
58
58
|
function requestShutdown() {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
59
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
60
|
+
var _i, shutdownCallbacks_1, callback;
|
|
61
|
+
return __generator(this, function (_a) {
|
|
62
|
+
switch (_a.label) {
|
|
63
|
+
case 0:
|
|
64
|
+
debug('requestShutdown');
|
|
65
|
+
shutdownRequested = true;
|
|
66
|
+
_i = 0, shutdownCallbacks_1 = shutdownCallbacks;
|
|
67
|
+
_a.label = 1;
|
|
68
|
+
case 1:
|
|
69
|
+
if (!(_i < shutdownCallbacks_1.length)) return [3 /*break*/, 4];
|
|
70
|
+
callback = shutdownCallbacks_1[_i];
|
|
71
|
+
debug('callback', callback);
|
|
72
|
+
return [4 /*yield*/, callback()
|
|
73
|
+
// try { callback() } catch (e) { }
|
|
74
|
+
];
|
|
75
|
+
case 2:
|
|
76
|
+
_a.sent();
|
|
77
|
+
_a.label = 3;
|
|
78
|
+
case 3:
|
|
79
|
+
_i++;
|
|
80
|
+
return [3 /*break*/, 1];
|
|
81
|
+
case 4:
|
|
82
|
+
debug('requestShutdown done');
|
|
83
|
+
return [2 /*return*/];
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
68
87
|
}
|
|
69
88
|
exports.requestShutdown = requestShutdown;
|
|
70
89
|
function getMessages(qrl, opt, maxMessages) {
|
|
@@ -78,7 +97,7 @@ function getMessages(qrl, opt, maxMessages) {
|
|
|
78
97
|
MaxNumberOfMessages: maxMessages,
|
|
79
98
|
MessageAttributeNames: ['All'],
|
|
80
99
|
QueueUrl: qrl,
|
|
81
|
-
VisibilityTimeout:
|
|
100
|
+
VisibilityTimeout: 60,
|
|
82
101
|
WaitTimeSeconds: opt.waitTime
|
|
83
102
|
};
|
|
84
103
|
return [4 /*yield*/, (0, sqs_js_1.getSQSClient)().send(new client_sqs_1.ReceiveMessageCommand(params))
|
|
@@ -98,58 +117,101 @@ exports.getMessages = getMessages;
|
|
|
98
117
|
//
|
|
99
118
|
function processMessages(queues, callback, options) {
|
|
100
119
|
return __awaiter(this, void 0, void 0, function () {
|
|
101
|
-
var opt, systemMonitor, jobExecutor, queueManager,
|
|
120
|
+
var opt, lastLatency, systemMonitor, jobExecutor, queueManager, delayTimeout, delay, activeQrls, maxReturnCount, listen, allowedJobs, maxLatency, latency, latencyFactor, targetJobs, jobsLeft, _i, _a, _b, qname, qrl, maxMessages;
|
|
102
121
|
var _this = this;
|
|
103
122
|
return __generator(this, function (_c) {
|
|
104
123
|
switch (_c.label) {
|
|
105
124
|
case 0:
|
|
125
|
+
debug({ options: options });
|
|
106
126
|
opt = (0, defaults_js_1.getOptionsWithDefaults)(options);
|
|
107
|
-
debug('processMessages', { queues: queues, callback: callback, options: options, opt: opt });
|
|
108
|
-
|
|
127
|
+
debug('processMessages', { queues: queues, callback: callback, options: options, opt: opt, argv: process.argv });
|
|
128
|
+
lastLatency = 10;
|
|
129
|
+
systemMonitor = new systemMonitor_js_1.SystemMonitor(function (latency) {
|
|
130
|
+
var percentDifference = 100 * Math.abs(lastLatency - latency) / lastLatency;
|
|
131
|
+
if (percentDifference > 10 && opt.verbose) {
|
|
132
|
+
console.error(chalk_1.default.blue('Latency:', Math.round(latency), 'ms'));
|
|
133
|
+
}
|
|
134
|
+
lastLatency = latency;
|
|
135
|
+
});
|
|
109
136
|
jobExecutor = new jobExecutor_js_1.JobExecutor(opt);
|
|
110
137
|
queueManager = new queueManager_js_1.QueueManager(opt, queues);
|
|
111
|
-
maxActiveJobs = 100;
|
|
112
|
-
debug({ systemMonitor: systemMonitor, jobExecutor: jobExecutor, queueManager: queueManager });
|
|
113
|
-
shutdownCallbacks.push(function () {
|
|
114
|
-
systemMonitor.shutdown();
|
|
115
|
-
queueManager.shutdown();
|
|
116
|
-
jobExecutor.shutdown();
|
|
117
|
-
});
|
|
118
138
|
delay = function (ms) { return new Promise(function (resolve) {
|
|
119
139
|
delayTimeout = setTimeout(resolve, ms);
|
|
120
140
|
}); };
|
|
141
|
+
shutdownCallbacks.push(function () { return __awaiter(_this, void 0, void 0, function () {
|
|
142
|
+
return __generator(this, function (_a) {
|
|
143
|
+
switch (_a.label) {
|
|
144
|
+
case 0:
|
|
145
|
+
clearTimeout(delayTimeout);
|
|
146
|
+
return [4 /*yield*/, queueManager.shutdown()];
|
|
147
|
+
case 1:
|
|
148
|
+
_a.sent();
|
|
149
|
+
debug({ queueManager: 'done' });
|
|
150
|
+
return [4 /*yield*/, jobExecutor.shutdown()];
|
|
151
|
+
case 2:
|
|
152
|
+
_a.sent();
|
|
153
|
+
debug({ jobExecutor: 'done' });
|
|
154
|
+
return [4 /*yield*/, systemMonitor.shutdown()];
|
|
155
|
+
case 3:
|
|
156
|
+
_a.sent();
|
|
157
|
+
debug({ systemMonitor: 'done' });
|
|
158
|
+
return [2 /*return*/];
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
}); });
|
|
121
162
|
activeQrls = new Set();
|
|
122
163
|
maxReturnCount = 0;
|
|
123
164
|
listen = function (qname, qrl, maxMessages) { return __awaiter(_this, void 0, void 0, function () {
|
|
124
|
-
var messages, _i, messages_1, message;
|
|
165
|
+
var messages, _i, messages_1, message, e_1;
|
|
125
166
|
return __generator(this, function (_a) {
|
|
126
167
|
switch (_a.label) {
|
|
127
168
|
case 0:
|
|
128
169
|
activeQrls.add(qrl);
|
|
129
170
|
maxReturnCount += maxMessages;
|
|
130
|
-
|
|
171
|
+
_a.label = 1;
|
|
131
172
|
case 1:
|
|
173
|
+
_a.trys.push([1, 3, , 4]);
|
|
174
|
+
return [4 /*yield*/, getMessages(qrl, opt, maxMessages)];
|
|
175
|
+
case 2:
|
|
132
176
|
messages = _a.sent();
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
177
|
+
if (messages.length) {
|
|
178
|
+
for (_i = 0, messages_1 = messages; _i < messages_1.length; _i++) {
|
|
179
|
+
message = messages_1[_i];
|
|
180
|
+
jobExecutor.executeJob(message, callback, qname, qrl);
|
|
181
|
+
}
|
|
182
|
+
queueManager.updateIcehouse(qrl, false);
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
185
|
+
// If we didn't get any, update the icehouse so we can back off
|
|
186
|
+
queueManager.updateIcehouse(qrl, true);
|
|
136
187
|
}
|
|
188
|
+
// Max job accounting
|
|
137
189
|
maxReturnCount -= maxMessages;
|
|
138
190
|
activeQrls.delete(qrl);
|
|
139
|
-
return [
|
|
191
|
+
return [3 /*break*/, 4];
|
|
192
|
+
case 3:
|
|
193
|
+
e_1 = _a.sent();
|
|
194
|
+
// If the queue has been cleaned up, we should back off anyway
|
|
195
|
+
if (e_1 instanceof client_sqs_1.QueueDoesNotExist) {
|
|
196
|
+
queueManager.updateIcehouse(qrl, true);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
throw e_1;
|
|
200
|
+
}
|
|
201
|
+
return [3 /*break*/, 4];
|
|
202
|
+
case 4: return [2 /*return*/];
|
|
140
203
|
}
|
|
141
204
|
});
|
|
142
205
|
}); };
|
|
143
206
|
_c.label = 1;
|
|
144
207
|
case 1:
|
|
145
208
|
if (!!shutdownRequested) return [3 /*break*/, 3];
|
|
146
|
-
allowedJobs =
|
|
209
|
+
allowedJobs = opt.maxConcurrentJobs - jobExecutor.activeJobCount() - maxReturnCount;
|
|
147
210
|
maxLatency = 100;
|
|
148
211
|
latency = systemMonitor.getLatency().setTimeout;
|
|
149
212
|
latencyFactor = 1 - Math.abs(Math.min(latency / maxLatency, 1)) // 0 if latency is at max, 1 if latency 0
|
|
150
213
|
;
|
|
151
214
|
targetJobs = Math.round(allowedJobs * latencyFactor);
|
|
152
|
-
debug({ allowedJobs: allowedJobs, maxLatency: maxLatency, latency: latency, latencyFactor: latencyFactor, targetJobs: targetJobs, activeQrls: activeQrls });
|
|
153
215
|
jobsLeft = targetJobs;
|
|
154
216
|
for (_i = 0, _a = queueManager.getPairs(); _i < _a.length; _i++) {
|
|
155
217
|
_b = _a[_i], qname = _b.qname, qrl = _b.qrl;
|
|
@@ -161,7 +223,7 @@ function processMessages(queues, callback, options) {
|
|
|
161
223
|
if (opt.verbose) {
|
|
162
224
|
console.error(chalk_1.default.blue('Listening on: '), qname);
|
|
163
225
|
}
|
|
164
|
-
debug({ listenedTo: { qname
|
|
226
|
+
// debug({ listenedTo: { qname, maxMessages, jobsLeft } })
|
|
165
227
|
}
|
|
166
228
|
return [4 /*yield*/, delay(1000)];
|
|
167
229
|
case 2:
|
package/commonjs/src/defaults.js
CHANGED
|
@@ -36,11 +36,18 @@ exports.defaults = Object.freeze({
|
|
|
36
36
|
killAfter: 30,
|
|
37
37
|
archive: false,
|
|
38
38
|
activeOnly: false,
|
|
39
|
+
maxConcurrentJobs: 100,
|
|
39
40
|
// Idle Queues
|
|
40
41
|
idleFor: 60,
|
|
41
42
|
delete: false,
|
|
42
43
|
unpair: false
|
|
43
44
|
});
|
|
45
|
+
function validateInteger(opt, name) {
|
|
46
|
+
var parsed = parseInt(opt[name], 10);
|
|
47
|
+
if (isNaN(parsed))
|
|
48
|
+
throw new Error("".concat(name, " needs to be an integer."));
|
|
49
|
+
return parsed;
|
|
50
|
+
}
|
|
44
51
|
/**
|
|
45
52
|
* This function should be called by each exposed API entry point on the
|
|
46
53
|
* options passed in from the caller. It supports options named in camelCase
|
|
@@ -87,13 +94,25 @@ function getOptionsWithDefaults(options) {
|
|
|
87
94
|
archive: options.archive || exports.defaults.archive,
|
|
88
95
|
activeOnly: options.activeOnly || options['active-only'] || exports.defaults.activeOnly,
|
|
89
96
|
includeFailed: options.includeFailed || options['include-failed'] || exports.defaults.includeFailed,
|
|
97
|
+
maxConcurrentJobs: options.maxConcurrentJobs || exports.defaults.maxConcurrentJobs,
|
|
90
98
|
// Idle Queues
|
|
91
99
|
idleFor: options.idleFor || options['idle-for'] || exports.defaults.idleFor,
|
|
92
100
|
delete: options.delete || exports.defaults.delete,
|
|
93
101
|
unpair: options.delete || exports.defaults.unpair
|
|
94
102
|
};
|
|
103
|
+
// Setting this env here means we don't have to in AWS SDK constructors
|
|
95
104
|
process.env.AWS_REGION = opt.region;
|
|
96
|
-
//
|
|
105
|
+
// Validation
|
|
106
|
+
opt.cacheTtlSeconds = validateInteger(opt, 'cacheTtlSeconds');
|
|
107
|
+
opt.messageRetentionPeriod = validateInteger(opt, 'messageRetentionPeriod');
|
|
108
|
+
opt.delay = validateInteger(opt, 'delay');
|
|
109
|
+
opt.sendRetries = validateInteger(opt, 'sendRetries');
|
|
110
|
+
opt.failDelay = validateInteger(opt, 'failDelay');
|
|
111
|
+
opt.dlqAfter = validateInteger(opt, 'dlqAfter');
|
|
112
|
+
opt.waitTime = validateInteger(opt, 'waitTime');
|
|
113
|
+
opt.killAfter = validateInteger(opt, 'killAfter');
|
|
114
|
+
opt.maxConcurrentJobs = validateInteger(opt, 'maxConcurrentJobs');
|
|
115
|
+
opt.idleFor = validateInteger(opt, 'idleFor');
|
|
97
116
|
return opt;
|
|
98
117
|
}
|
|
99
118
|
exports.getOptionsWithDefaults = getOptionsWithDefaults;
|
|
@@ -91,11 +91,10 @@ function _cheapIdleCheck(qname, qrl, opt) {
|
|
|
91
91
|
case 0:
|
|
92
92
|
client = (0, sqs_js_1.getSQSClient)();
|
|
93
93
|
cmd = new client_sqs_1.GetQueueAttributesCommand({ AttributeNames: exports.attributeNames, QueueUrl: qrl });
|
|
94
|
-
return [4 /*yield*/, client.send(cmd)
|
|
95
|
-
// debug('data', data)
|
|
96
|
-
];
|
|
94
|
+
return [4 /*yield*/, client.send(cmd)];
|
|
97
95
|
case 1:
|
|
98
96
|
data = _a.sent();
|
|
97
|
+
debug('data', data);
|
|
99
98
|
result = data.Attributes;
|
|
100
99
|
result.queue = qname.slice(opt.prefix.length);
|
|
101
100
|
// We are idle if all the messages attributes are zero
|
|
@@ -54,6 +54,7 @@ var JobExecutor = /** @class */ (function () {
|
|
|
54
54
|
function JobExecutor(opt) {
|
|
55
55
|
this.opt = opt;
|
|
56
56
|
this.jobs = [];
|
|
57
|
+
this.jobsByMessageId = {};
|
|
57
58
|
this.stats = {
|
|
58
59
|
activeJobs: 0,
|
|
59
60
|
sqsCalls: 0,
|
|
@@ -62,14 +63,27 @@ var JobExecutor = /** @class */ (function () {
|
|
|
62
63
|
jobsFailed: 0,
|
|
63
64
|
jobsDeleted: 0
|
|
64
65
|
};
|
|
65
|
-
this.maintainVisibility();
|
|
66
|
+
this.maintainPromise = this.maintainVisibility();
|
|
66
67
|
debug({ this: this });
|
|
67
68
|
}
|
|
68
69
|
JobExecutor.prototype.shutdown = function () {
|
|
69
|
-
this
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
71
|
+
return __generator(this, function (_a) {
|
|
72
|
+
switch (_a.label) {
|
|
73
|
+
case 0:
|
|
74
|
+
this.shutdownRequested = true;
|
|
75
|
+
// Trigger a maintenance run right away in case it speeds us up
|
|
76
|
+
clearTimeout(this.maintainVisibilityTimeout);
|
|
77
|
+
return [4 /*yield*/, this.maintainPromise];
|
|
78
|
+
case 1:
|
|
79
|
+
_a.sent();
|
|
80
|
+
return [4 /*yield*/, this.maintainVisibility()];
|
|
81
|
+
case 2:
|
|
82
|
+
_a.sent();
|
|
83
|
+
return [2 /*return*/];
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
});
|
|
73
87
|
};
|
|
74
88
|
JobExecutor.prototype.activeJobCount = function () {
|
|
75
89
|
return this.stats.activeJobs;
|
|
@@ -79,13 +93,13 @@ var JobExecutor = /** @class */ (function () {
|
|
|
79
93
|
*/
|
|
80
94
|
JobExecutor.prototype.maintainVisibility = function () {
|
|
81
95
|
return __awaiter(this, void 0, void 0, function () {
|
|
82
|
-
var
|
|
96
|
+
var start, jobsToExtendByQrl, jobsToDeleteByQrl, jobsToCleanup, i, job, jobRunTime, jobsToDelete, jobsToExtend, doubled, secondsUntilMax, _a, _b, _c, _i, qrl, jobsToExtend, entries, messageId, job, entry, input, result, count, _d, _e, _f, _g, qrl, jobsToDelete, entries, messageId, job, entry, input, result, count, msElapsed, msPeriod, msLeft, msMin, nextCheckInMs;
|
|
83
97
|
var _this = this;
|
|
84
|
-
return __generator(this, function (
|
|
85
|
-
switch (
|
|
98
|
+
return __generator(this, function (_h) {
|
|
99
|
+
switch (_h.label) {
|
|
86
100
|
case 0:
|
|
87
|
-
|
|
88
|
-
|
|
101
|
+
clearTimeout(this.maintainVisibilityTimeout);
|
|
102
|
+
start = new Date();
|
|
89
103
|
jobsToExtendByQrl = {};
|
|
90
104
|
jobsToDeleteByQrl = {};
|
|
91
105
|
jobsToCleanup = new Set();
|
|
@@ -97,43 +111,48 @@ var JobExecutor = /** @class */ (function () {
|
|
|
97
111
|
}));
|
|
98
112
|
}
|
|
99
113
|
// Build list of jobs we need to deal with
|
|
100
|
-
for (
|
|
101
|
-
job =
|
|
102
|
-
jobRunTime = (
|
|
114
|
+
for (i = 0; i < this.jobs.length; i++) {
|
|
115
|
+
job = this.jobs[i];
|
|
116
|
+
jobRunTime = Math.round((start - job.start) / 1000);
|
|
117
|
+
// debug('considering job', job)
|
|
103
118
|
if (job.status === 'complete') {
|
|
104
119
|
jobsToDelete = jobsToDeleteByQrl[job.qrl] || [];
|
|
120
|
+
job.status = 'deleting';
|
|
105
121
|
jobsToDelete.push(job);
|
|
106
122
|
jobsToDeleteByQrl[job.qrl] = jobsToDelete;
|
|
107
123
|
}
|
|
108
124
|
else if (job.status === 'failed') {
|
|
109
125
|
jobsToCleanup.add(job);
|
|
110
126
|
}
|
|
111
|
-
else if (
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
127
|
+
else if (job.status === 'processing') {
|
|
128
|
+
debug('processing', { job: job, jobRunTime: jobRunTime });
|
|
129
|
+
if (jobRunTime >= job.extendAtSecond) {
|
|
130
|
+
jobsToExtend = jobsToExtendByQrl[job.qrl] || [];
|
|
131
|
+
jobsToExtend.push(job);
|
|
132
|
+
jobsToExtendByQrl[job.qrl] = jobsToExtend;
|
|
133
|
+
doubled = job.visibilityTimeout * 2;
|
|
134
|
+
secondsUntilMax = Math.max(1, maxJobSeconds - jobRunTime);
|
|
135
|
+
// const secondsUntilKill = Math.max(1, this.opt.killAfter - jobRunTime)
|
|
136
|
+
job.visibilityTimeout = Math.min(doubled, secondsUntilMax); //, secondsUntilKill)
|
|
137
|
+
job.extendAtSecond = Math.round(jobRunTime + job.visibilityTimeout); // this is what we use next time
|
|
138
|
+
debug({ doubled: doubled, secondsUntilMax: secondsUntilMax, job: job });
|
|
139
|
+
}
|
|
120
140
|
}
|
|
121
141
|
}
|
|
122
|
-
|
|
123
|
-
_b =
|
|
124
|
-
_c
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
_k.label = 1;
|
|
142
|
+
_a = jobsToExtendByQrl;
|
|
143
|
+
_b = [];
|
|
144
|
+
for (_c in _a)
|
|
145
|
+
_b.push(_c);
|
|
146
|
+
_i = 0;
|
|
147
|
+
_h.label = 1;
|
|
129
148
|
case 1:
|
|
130
|
-
if (!(
|
|
131
|
-
|
|
132
|
-
if (!(
|
|
133
|
-
qrl =
|
|
149
|
+
if (!(_i < _b.length)) return [3 /*break*/, 5];
|
|
150
|
+
_c = _b[_i];
|
|
151
|
+
if (!(_c in _a)) return [3 /*break*/, 4];
|
|
152
|
+
qrl = _c;
|
|
134
153
|
jobsToExtend = jobsToExtendByQrl[qrl];
|
|
135
154
|
debug({ qrl: qrl, jobsToExtend: jobsToExtend });
|
|
136
|
-
|
|
155
|
+
_h.label = 2;
|
|
137
156
|
case 2:
|
|
138
157
|
if (!jobsToExtend.length) return [3 /*break*/, 4];
|
|
139
158
|
entries = [];
|
|
@@ -147,50 +166,44 @@ var JobExecutor = /** @class */ (function () {
|
|
|
147
166
|
};
|
|
148
167
|
entries.push(entry);
|
|
149
168
|
}
|
|
169
|
+
debug({ entries: entries });
|
|
150
170
|
input = { QueueUrl: qrl, Entries: entries };
|
|
151
171
|
debug({ ChangeMessageVisibilityBatch: input });
|
|
152
172
|
return [4 /*yield*/, (0, sqs_js_1.getSQSClient)().send(new client_sqs_1.ChangeMessageVisibilityBatchCommand(input))];
|
|
153
173
|
case 3:
|
|
154
|
-
result =
|
|
174
|
+
result = _h.sent();
|
|
155
175
|
debug('ChangeMessageVisibilityBatch returned', result);
|
|
156
176
|
this.stats.sqsCalls++;
|
|
157
|
-
if (result.Successful)
|
|
158
|
-
|
|
177
|
+
if (result.Successful) {
|
|
178
|
+
count = result.Successful.length || 0;
|
|
179
|
+
this.stats.timeoutsExtended += count;
|
|
180
|
+
if (this.opt.verbose) {
|
|
181
|
+
console.error(chalk_1.default.blue('Extended'), count, chalk_1.default.blue('jobs'));
|
|
182
|
+
}
|
|
183
|
+
else if (!this.opt.disableLog) {
|
|
184
|
+
console.log(JSON.stringify({ event: 'EXTEND_VISIBILITY_TIMEOUTS', timestamp: start, count: count, qrl: qrl }));
|
|
185
|
+
}
|
|
186
|
+
}
|
|
159
187
|
return [3 /*break*/, 2];
|
|
160
188
|
case 4:
|
|
161
|
-
|
|
162
|
-
console.error(chalk_1.default.blue('Extended these jobs: '), jobsToExtend);
|
|
163
|
-
}
|
|
164
|
-
else if (!this.opt.disableLog) {
|
|
165
|
-
console.log(JSON.stringify({
|
|
166
|
-
event: 'EXTEND_VISIBILITY_TIMEOUTS',
|
|
167
|
-
timestamp: now,
|
|
168
|
-
messageIds: jobsToExtend.map(function (_a) {
|
|
169
|
-
var message = _a.message;
|
|
170
|
-
return message.MessageId;
|
|
171
|
-
})
|
|
172
|
-
}));
|
|
173
|
-
}
|
|
174
|
-
_k.label = 5;
|
|
175
|
-
case 5:
|
|
176
|
-
_e++;
|
|
189
|
+
_i++;
|
|
177
190
|
return [3 /*break*/, 1];
|
|
191
|
+
case 5:
|
|
192
|
+
_d = jobsToDeleteByQrl;
|
|
193
|
+
_e = [];
|
|
194
|
+
for (_f in _d)
|
|
195
|
+
_e.push(_f);
|
|
196
|
+
_g = 0;
|
|
197
|
+
_h.label = 6;
|
|
178
198
|
case 6:
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
_j = 0;
|
|
184
|
-
_k.label = 7;
|
|
185
|
-
case 7:
|
|
186
|
-
if (!(_j < _g.length)) return [3 /*break*/, 12];
|
|
187
|
-
_h = _g[_j];
|
|
188
|
-
if (!(_h in _f)) return [3 /*break*/, 11];
|
|
189
|
-
qrl = _h;
|
|
199
|
+
if (!(_g < _e.length)) return [3 /*break*/, 10];
|
|
200
|
+
_f = _e[_g];
|
|
201
|
+
if (!(_f in _d)) return [3 /*break*/, 9];
|
|
202
|
+
qrl = _f;
|
|
190
203
|
jobsToDelete = jobsToDeleteByQrl[qrl];
|
|
191
|
-
|
|
192
|
-
case
|
|
193
|
-
if (!jobsToDelete.length) return [3 /*break*/,
|
|
204
|
+
_h.label = 7;
|
|
205
|
+
case 7:
|
|
206
|
+
if (!jobsToDelete.length) return [3 /*break*/, 9];
|
|
194
207
|
entries = [];
|
|
195
208
|
messageId = 0;
|
|
196
209
|
while (messageId++ < 10 && jobsToDelete.length) {
|
|
@@ -202,41 +215,52 @@ var JobExecutor = /** @class */ (function () {
|
|
|
202
215
|
};
|
|
203
216
|
entries.push(entry);
|
|
204
217
|
}
|
|
218
|
+
debug({ entries: entries });
|
|
205
219
|
input = { QueueUrl: qrl, Entries: entries };
|
|
206
220
|
debug({ DeleteMessageBatch: input });
|
|
207
221
|
return [4 /*yield*/, (0, sqs_js_1.getSQSClient)().send(new client_sqs_1.DeleteMessageBatchCommand(input))];
|
|
208
|
-
case
|
|
209
|
-
result =
|
|
222
|
+
case 8:
|
|
223
|
+
result = _h.sent();
|
|
210
224
|
this.stats.sqsCalls++;
|
|
211
|
-
if (result.Successful)
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
console.log(JSON.stringify({
|
|
221
|
-
event: 'DELETE_MESSAGES',
|
|
222
|
-
timestamp: now,
|
|
223
|
-
messageIds: jobsToDelete.map(function (_a) {
|
|
224
|
-
var message = _a.message;
|
|
225
|
-
return message.MessageId;
|
|
226
|
-
})
|
|
227
|
-
}));
|
|
225
|
+
if (result.Successful) {
|
|
226
|
+
count = result.Successful.length || 0;
|
|
227
|
+
this.stats.jobsDeleted += count;
|
|
228
|
+
if (this.opt.verbose) {
|
|
229
|
+
console.error(chalk_1.default.blue('Deleted'), count, chalk_1.default.blue('jobs'));
|
|
230
|
+
}
|
|
231
|
+
else if (!this.opt.disableLog) {
|
|
232
|
+
console.log(JSON.stringify({ event: 'DELETE_MESSAGES', timestamp: start, count: count, qrl: qrl }));
|
|
233
|
+
}
|
|
228
234
|
}
|
|
229
|
-
|
|
230
|
-
case 11:
|
|
231
|
-
_j++;
|
|
235
|
+
debug('DeleteMessageBatch returned', result);
|
|
232
236
|
return [3 /*break*/, 7];
|
|
233
|
-
case
|
|
237
|
+
case 9:
|
|
238
|
+
_g++;
|
|
239
|
+
return [3 /*break*/, 6];
|
|
240
|
+
case 10:
|
|
234
241
|
// Get rid of deleted and failed jobs
|
|
235
|
-
this.jobs = this.jobs.filter(function (
|
|
236
|
-
|
|
242
|
+
this.jobs = this.jobs.filter(function (job) {
|
|
243
|
+
if (job.status === 'deleting' || job.status === 'failed') {
|
|
244
|
+
debug('removed', job.message.MessageId);
|
|
245
|
+
delete _this.jobsByMessageId[job.message.MessageId];
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
else {
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
// Bail if we are shutting down
|
|
237
253
|
if (this.shutdownRequested && this.stats.activeJobs === 0 && this.jobs.length === 0)
|
|
238
254
|
return [2 /*return*/];
|
|
239
|
-
|
|
255
|
+
msElapsed = new Date() - start;
|
|
256
|
+
msPeriod = this.shutdownRequested ? 1 * 1000 : 10 * 1000;
|
|
257
|
+
msLeft = Math.max(0, msPeriod - msElapsed);
|
|
258
|
+
msMin = this.shutdownRequested ? 1000 : 0;
|
|
259
|
+
nextCheckInMs = Math.max(msMin, msLeft);
|
|
260
|
+
debug({ msElapsed: msElapsed, msPeriod: msPeriod, msLeft: msLeft, msMin: msMin, nextCheckInMs: nextCheckInMs });
|
|
261
|
+
this.maintainVisibilityTimeout = setTimeout(function () {
|
|
262
|
+
_this.maintainPromise = _this.maintainVisibility();
|
|
263
|
+
}, nextCheckInMs);
|
|
240
264
|
return [2 /*return*/];
|
|
241
265
|
}
|
|
242
266
|
});
|
|
@@ -244,16 +268,16 @@ var JobExecutor = /** @class */ (function () {
|
|
|
244
268
|
};
|
|
245
269
|
JobExecutor.prototype.executeJob = function (message, callback, qname, qrl) {
|
|
246
270
|
return __awaiter(this, void 0, void 0, function () {
|
|
247
|
-
var payload, visibilityTimeout, job, queue, result, err_1;
|
|
271
|
+
var payload, visibilityTimeout, job, oldJob, e, queue, result, err_1;
|
|
248
272
|
return __generator(this, function (_a) {
|
|
249
273
|
switch (_a.label) {
|
|
250
274
|
case 0:
|
|
251
275
|
payload = this.opt.json ? JSON.parse(message.Body) : message.Body;
|
|
252
|
-
visibilityTimeout =
|
|
276
|
+
visibilityTimeout = 60;
|
|
253
277
|
job = {
|
|
254
278
|
status: 'processing',
|
|
255
279
|
start: new Date(),
|
|
256
|
-
visibilityTimeout:
|
|
280
|
+
visibilityTimeout: visibilityTimeout,
|
|
257
281
|
extendAtSecond: visibilityTimeout / 2,
|
|
258
282
|
payload: this.opt.json ? JSON.parse(message.Body) : message.Body,
|
|
259
283
|
message: message,
|
|
@@ -261,8 +285,20 @@ var JobExecutor = /** @class */ (function () {
|
|
|
261
285
|
qname: qname,
|
|
262
286
|
qrl: qrl
|
|
263
287
|
};
|
|
264
|
-
|
|
288
|
+
oldJob = this.jobsByMessageId[job.message.MessageId];
|
|
289
|
+
if (oldJob) {
|
|
290
|
+
// If we actually see the same job again, we fucked up, probably due to
|
|
291
|
+
// the system being overloaded and us missing our extension call. So
|
|
292
|
+
// we'll celebrate this occasion by throwing a big fat error.
|
|
293
|
+
debug({ oldJob: oldJob });
|
|
294
|
+
e = new Error("Saw job ".concat(oldJob.message.MessageId, " twice"));
|
|
295
|
+
e.job = oldJob;
|
|
296
|
+
// TODO: sentry breadcrumb
|
|
297
|
+
throw e;
|
|
298
|
+
}
|
|
299
|
+
// debug('executeJob', job)
|
|
265
300
|
this.jobs.push(job);
|
|
301
|
+
this.jobsByMessageId[job.message.MessageId] = job;
|
|
266
302
|
this.stats.activeJobs++;
|
|
267
303
|
if (this.opt.verbose) {
|
|
268
304
|
console.error(chalk_1.default.blue('Executing:'), qname, chalk_1.default.blue('-->'), job.payload);
|
|
@@ -271,7 +307,7 @@ var JobExecutor = /** @class */ (function () {
|
|
|
271
307
|
console.log(JSON.stringify({
|
|
272
308
|
event: 'MESSAGE_PROCESSING_START',
|
|
273
309
|
timestamp: new Date(),
|
|
274
|
-
|
|
310
|
+
qrl: qrl,
|
|
275
311
|
messageId: message.MessageId,
|
|
276
312
|
payload: job.payload
|
|
277
313
|
}));
|
|
@@ -304,7 +340,7 @@ var JobExecutor = /** @class */ (function () {
|
|
|
304
340
|
return [3 /*break*/, 4];
|
|
305
341
|
case 3:
|
|
306
342
|
err_1 = _a.sent();
|
|
307
|
-
debug('exec.catch')
|
|
343
|
+
// debug('exec.catch', err)
|
|
308
344
|
// Fail path for job execution
|
|
309
345
|
if (this.opt.verbose) {
|
|
310
346
|
console.error(chalk_1.default.red('FAILED'), message.Body);
|
|
@@ -315,7 +351,7 @@ var JobExecutor = /** @class */ (function () {
|
|
|
315
351
|
console.log(JSON.stringify({
|
|
316
352
|
event: 'MESSAGE_PROCESSING_FAILED',
|
|
317
353
|
reason: 'exception thrown',
|
|
318
|
-
|
|
354
|
+
qrl: qrl,
|
|
319
355
|
timestamp: new Date(),
|
|
320
356
|
messageId: message.MessageId,
|
|
321
357
|
payload: payload,
|