qdone 2.0.29-alpha → 2.0.31-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,256 +2,155 @@
2
2
  /**
3
3
  * Consumer implementation.
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.processMessages = exports.getMessages = exports.requestShutdown = void 0;
46
- var os_1 = require("os");
47
- var client_sqs_1 = require("@aws-sdk/client-sqs");
48
- var chalk_1 = __importDefault(require("chalk"));
49
- var debug_1 = __importDefault(require("debug"));
50
- var systemMonitor_js_1 = require("./scheduler/systemMonitor.js");
51
- var queueManager_js_1 = require("./scheduler/queueManager.js");
52
- var jobExecutor_js_1 = require("./scheduler/jobExecutor.js");
53
- var defaults_js_1 = require("./defaults.js");
54
- var sqs_js_1 = require("./sqs.js");
55
- var debug = (0, debug_1.default)('qdone:consumer');
10
+ const os_1 = require("os");
11
+ const client_sqs_1 = require("@aws-sdk/client-sqs");
12
+ const chalk_1 = __importDefault(require("chalk"));
13
+ const debug_1 = __importDefault(require("debug"));
14
+ const systemMonitor_js_1 = require("./scheduler/systemMonitor.js");
15
+ const queueManager_js_1 = require("./scheduler/queueManager.js");
16
+ const jobExecutor_js_1 = require("./scheduler/jobExecutor.js");
17
+ const defaults_js_1 = require("./defaults.js");
18
+ const sqs_js_1 = require("./sqs.js");
19
+ const debug = (0, debug_1.default)('qdone:consumer');
56
20
  // Global flag for shutdown request
57
- var shutdownRequested = false;
58
- var shutdownCallbacks = [];
59
- function requestShutdown() {
60
- return __awaiter(this, void 0, void 0, function () {
61
- var _i, shutdownCallbacks_1, callback;
62
- return __generator(this, function (_a) {
63
- switch (_a.label) {
64
- case 0:
65
- debug('requestShutdown');
66
- shutdownRequested = true;
67
- _i = 0, shutdownCallbacks_1 = shutdownCallbacks;
68
- _a.label = 1;
69
- case 1:
70
- if (!(_i < shutdownCallbacks_1.length)) return [3 /*break*/, 4];
71
- callback = shutdownCallbacks_1[_i];
72
- debug('callback', callback);
73
- return [4 /*yield*/, callback()
74
- // try { callback() } catch (e) { }
75
- ];
76
- case 2:
77
- _a.sent();
78
- _a.label = 3;
79
- case 3:
80
- _i++;
81
- return [3 /*break*/, 1];
82
- case 4:
83
- debug('requestShutdown done');
84
- return [2 /*return*/];
85
- }
86
- });
87
- });
21
+ let shutdownRequested = false;
22
+ const shutdownCallbacks = [];
23
+ async function requestShutdown() {
24
+ debug('requestShutdown');
25
+ shutdownRequested = true;
26
+ for (const callback of shutdownCallbacks) {
27
+ debug('callback', callback);
28
+ await callback();
29
+ // try { callback() } catch (e) { }
30
+ }
31
+ debug('requestShutdown done');
88
32
  }
89
33
  exports.requestShutdown = requestShutdown;
90
- function getMessages(qrl, opt, maxMessages) {
91
- return __awaiter(this, void 0, void 0, function () {
92
- var params, response;
93
- return __generator(this, function (_a) {
94
- switch (_a.label) {
95
- case 0:
96
- params = {
97
- AttributeNames: ['All'],
98
- MaxNumberOfMessages: maxMessages,
99
- MessageAttributeNames: ['All'],
100
- QueueUrl: qrl,
101
- VisibilityTimeout: 60,
102
- WaitTimeSeconds: opt.waitTime
103
- };
104
- return [4 /*yield*/, (0, sqs_js_1.getSQSClient)().send(new client_sqs_1.ReceiveMessageCommand(params))
105
- // debug('ReceiveMessage response', response)
106
- ];
107
- case 1:
108
- response = _a.sent();
109
- // debug('ReceiveMessage response', response)
110
- return [2 /*return*/, response.Messages || []];
111
- }
112
- });
113
- });
34
+ async function getMessages(qrl, opt, maxMessages) {
35
+ const params = {
36
+ AttributeNames: ['All'],
37
+ MaxNumberOfMessages: maxMessages,
38
+ MessageAttributeNames: ['All'],
39
+ QueueUrl: qrl,
40
+ VisibilityTimeout: 60,
41
+ WaitTimeSeconds: opt.waitTime
42
+ };
43
+ const response = await (0, sqs_js_1.getSQSClient)().send(new client_sqs_1.ReceiveMessageCommand(params));
44
+ // debug('ReceiveMessage response', response)
45
+ return response.Messages || [];
114
46
  }
115
47
  exports.getMessages = getMessages;
116
48
  //
117
49
  // Consumer
118
50
  //
119
- function processMessages(queues, callback, options) {
120
- return __awaiter(this, void 0, void 0, function () {
121
- var opt, lastLatency, systemMonitor, jobExecutor, queueManager, cores, delayTimeout, delay, activeQrls, maxReturnCount, listen, allowedJobs, maxLatency, latency, latencyFactor, freeMemory, totalMemory, memoryThreshold, freememThreshold, remainingMemory, freememFactor, oneMinuteLoad, loadPerCore, loadFactor, overallFactor, targetJobs, jobsLeft, _i, _a, _b, qname, qrl, maxMessages;
122
- var _this = this;
123
- return __generator(this, function (_c) {
124
- switch (_c.label) {
125
- case 0:
126
- debug({ options: options });
127
- opt = (0, defaults_js_1.getOptionsWithDefaults)(options);
128
- debug('processMessages', { queues: queues, callback: callback, options: options, opt: opt, argv: process.argv });
129
- lastLatency = 10;
130
- systemMonitor = new systemMonitor_js_1.SystemMonitor(function (latency) {
131
- var percentDifference = 100 * Math.abs(lastLatency - latency) / lastLatency;
132
- if (percentDifference > 10 && opt.verbose) {
133
- console.error(chalk_1.default.blue('Latency:', Math.round(latency), 'ms'));
134
- }
135
- lastLatency = latency;
136
- });
137
- jobExecutor = new jobExecutor_js_1.JobExecutor(opt);
138
- queueManager = new queueManager_js_1.QueueManager(opt, queues, 60);
139
- cores = (0, os_1.cpus)().length;
140
- delay = function (ms) { return new Promise(function (resolve) {
141
- delayTimeout = setTimeout(resolve, ms);
142
- }); };
143
- shutdownCallbacks.push(function () { return __awaiter(_this, void 0, void 0, function () {
144
- return __generator(this, function (_a) {
145
- switch (_a.label) {
146
- case 0:
147
- clearTimeout(delayTimeout);
148
- return [4 /*yield*/, queueManager.shutdown()];
149
- case 1:
150
- _a.sent();
151
- debug({ queueManager: 'done' });
152
- return [4 /*yield*/, jobExecutor.shutdown()];
153
- case 2:
154
- _a.sent();
155
- debug({ jobExecutor: 'done' });
156
- return [4 /*yield*/, systemMonitor.shutdown()];
157
- case 3:
158
- _a.sent();
159
- debug({ systemMonitor: 'done' });
160
- return [2 /*return*/];
161
- }
162
- });
163
- }); });
164
- activeQrls = new Set();
165
- maxReturnCount = 0;
166
- listen = function (qname, qrl, maxMessages) { return __awaiter(_this, void 0, void 0, function () {
167
- var messages, _i, messages_1, message, e_1;
168
- return __generator(this, function (_a) {
169
- switch (_a.label) {
170
- case 0:
171
- if (opt.verbose) {
172
- console.error(chalk_1.default.blue('Listening on: '), qname);
173
- }
174
- activeQrls.add(qrl);
175
- maxReturnCount += maxMessages;
176
- _a.label = 1;
177
- case 1:
178
- _a.trys.push([1, 3, , 4]);
179
- return [4 /*yield*/, getMessages(qrl, opt, maxMessages)];
180
- case 2:
181
- messages = _a.sent();
182
- if (!shutdownRequested) {
183
- if (messages.length) {
184
- for (_i = 0, messages_1 = messages; _i < messages_1.length; _i++) {
185
- message = messages_1[_i];
186
- jobExecutor.executeJob(message, callback, qname, qrl);
187
- }
188
- queueManager.updateIcehouse(qrl, false);
189
- }
190
- else {
191
- // If we didn't get any, update the icehouse so we can back off
192
- queueManager.updateIcehouse(qrl, true);
193
- }
194
- }
195
- // Max job accounting
196
- maxReturnCount -= maxMessages;
197
- activeQrls.delete(qrl);
198
- return [3 /*break*/, 4];
199
- case 3:
200
- e_1 = _a.sent();
201
- // If the queue has been cleaned up, we should back off anyway
202
- if (e_1 instanceof client_sqs_1.QueueDoesNotExist) {
203
- queueManager.updateIcehouse(qrl, true);
204
- }
205
- else {
206
- throw e_1;
207
- }
208
- return [3 /*break*/, 4];
209
- case 4: return [2 /*return*/];
210
- }
211
- });
212
- }); };
213
- _c.label = 1;
214
- case 1:
215
- if (!!shutdownRequested) return [3 /*break*/, 3];
216
- allowedJobs = Math.max(0, opt.maxConcurrentJobs - jobExecutor.activeJobCount() - maxReturnCount);
217
- maxLatency = 100;
218
- latency = systemMonitor.getLatency() || 10;
219
- latencyFactor = 1 - Math.abs(Math.min(latency / maxLatency, 1)) // 0 if latency is at max, 1 if latency 0
220
- ;
221
- freeMemory = (0, os_1.freemem)();
222
- totalMemory = (0, os_1.totalmem)();
223
- memoryThreshold = totalMemory * opt.maxMemoryPercent / 100;
224
- freememThreshold = totalMemory - memoryThreshold;
225
- remainingMemory = Math.max(0, freeMemory - freememThreshold);
226
- freememFactor = Math.min(1, Math.max(0, remainingMemory / memoryThreshold));
227
- oneMinuteLoad = (0, os_1.loadavg)()[0];
228
- loadPerCore = oneMinuteLoad / cores;
229
- loadFactor = 1 - Math.min(1, Math.max(0, loadPerCore / 3));
230
- overallFactor = Math.min(latencyFactor, freememFactor, loadFactor);
231
- targetJobs = Math.round(allowedJobs * overallFactor);
232
- jobsLeft = targetJobs;
233
- if (opt.verbose) {
234
- console.error({ maxConcurrentJobs: opt.maxConcurrentJobs, jobCount: jobExecutor.activeJobCount(), allowedJobs: allowedJobs, maxLatency: maxLatency, latencyFactor: latencyFactor, freememFactor: freememFactor, loadFactor: loadFactor, overallFactor: overallFactor, targetJobs: targetJobs, activeQrls: activeQrls });
235
- }
236
- for (_i = 0, _a = queueManager.getPairs(); _i < _a.length; _i++) {
237
- _b = _a[_i], qname = _b.qname, qrl = _b.qrl;
238
- // debug({ evaluating: { qname, qrl, jobsLeft, activeQrlsHasQrl: activeQrls.has(qrl) } })
239
- if (jobsLeft <= 0 || activeQrls.has(qrl))
240
- continue;
241
- maxMessages = Math.min(10, jobsLeft);
242
- listen(qname, qrl, maxMessages);
243
- jobsLeft -= maxMessages;
244
- // debug({ listenedTo: { qname, maxMessages, jobsLeft } })
245
- }
246
- return [4 /*yield*/, delay(1000)];
247
- case 2:
248
- _c.sent();
249
- return [3 /*break*/, 1];
250
- case 3:
251
- debug('after all');
252
- return [2 /*return*/];
253
- }
254
- });
51
+ async function processMessages(queues, callback, options) {
52
+ debug({ options });
53
+ const opt = (0, defaults_js_1.getOptionsWithDefaults)(options);
54
+ debug('processMessages', { queues, callback, options, opt, argv: process.argv });
55
+ let lastLatency = 10;
56
+ const systemMonitor = new systemMonitor_js_1.SystemMonitor(latency => {
57
+ const percentDifference = 100 * Math.abs(lastLatency - latency) / lastLatency;
58
+ if (percentDifference > 10 && opt.verbose) {
59
+ console.error(chalk_1.default.blue('Latency:', Math.round(latency), 'ms'));
60
+ }
61
+ lastLatency = latency;
62
+ });
63
+ const jobExecutor = new jobExecutor_js_1.JobExecutor(opt);
64
+ const queueManager = new queueManager_js_1.QueueManager(opt, queues, 60);
65
+ const cores = (0, os_1.cpus)().length;
66
+ // debug({ systemMonitor, jobExecutor, queueManager })
67
+ // This delay function keeps a timeout reference around so it can be
68
+ // cancelled at shutdown
69
+ let delayTimeout;
70
+ const delay = (ms) => new Promise(resolve => {
71
+ delayTimeout = setTimeout(resolve, ms);
72
+ });
73
+ shutdownCallbacks.push(async () => {
74
+ clearTimeout(delayTimeout);
75
+ await queueManager.shutdown();
76
+ debug({ queueManager: 'done' });
77
+ await jobExecutor.shutdown();
78
+ debug({ jobExecutor: 'done' });
79
+ await systemMonitor.shutdown();
80
+ debug({ systemMonitor: 'done' });
255
81
  });
82
+ // Keep track of how many messages could be returned from each queue
83
+ const activeQrls = new Set();
84
+ let maxReturnCount = 0;
85
+ const listen = async (qname, qrl, maxMessages) => {
86
+ if (opt.verbose) {
87
+ console.error(chalk_1.default.blue('Listening on: '), qname);
88
+ }
89
+ activeQrls.add(qrl);
90
+ maxReturnCount += maxMessages;
91
+ try {
92
+ const messages = await getMessages(qrl, opt, maxMessages);
93
+ if (!shutdownRequested) {
94
+ if (messages.length) {
95
+ jobExecutor.executeJobs(messages, callback, qname, qrl);
96
+ queueManager.updateIcehouse(qrl, false);
97
+ }
98
+ else {
99
+ // If we didn't get any, update the icehouse so we can back off
100
+ queueManager.updateIcehouse(qrl, true);
101
+ }
102
+ }
103
+ // Max job accounting
104
+ if (messages.length)
105
+ maxReturnCount -= messages.length;
106
+ activeQrls.delete(qrl);
107
+ }
108
+ catch (e) {
109
+ // If the queue has been cleaned up, we should back off anyway
110
+ if (e instanceof client_sqs_1.QueueDoesNotExist) {
111
+ queueManager.updateIcehouse(qrl, true);
112
+ }
113
+ else {
114
+ throw e;
115
+ }
116
+ }
117
+ };
118
+ while (!shutdownRequested) { // eslint-disable-line
119
+ // Figure out how we are running
120
+ const allowedJobs = Math.max(0, opt.maxConcurrentJobs - jobExecutor.runningJobCount() - maxReturnCount);
121
+ // Latency
122
+ const maxLatency = 100;
123
+ const latency = systemMonitor.getLatency() || 10;
124
+ const latencyFactor = 1 - Math.abs(Math.min(latency / maxLatency, 1)); // 0 if latency is at max, 1 if latency 0
125
+ // Memory
126
+ const freeMemory = (0, os_1.freemem)();
127
+ const totalMemory = (0, os_1.totalmem)();
128
+ const memoryThreshold = totalMemory * opt.maxMemoryPercent / 100;
129
+ const freememThreshold = totalMemory - memoryThreshold;
130
+ const remainingMemory = Math.max(0, freeMemory - freememThreshold);
131
+ const freememFactor = Math.min(1, Math.max(0, remainingMemory / memoryThreshold));
132
+ // Load
133
+ const oneMinuteLoad = (0, os_1.loadavg)()[0];
134
+ const loadPerCore = oneMinuteLoad / cores;
135
+ const loadFactor = 1 - Math.min(1, Math.max(0, loadPerCore / 3));
136
+ const overallFactor = Math.min(latencyFactor, freememFactor, loadFactor);
137
+ const targetJobs = Math.round(allowedJobs * overallFactor);
138
+ let jobsLeft = targetJobs;
139
+ if (opt.verbose) {
140
+ // console.error({ maxConcurrentJobs: opt.maxConcurrentJobs, jobCount: jobExecutor.activeJobCount(), allowedJobs, maxLatency, latencyFactor, freememFactor, loadFactor, overallFactor, targetJobs, activeQrls })
141
+ }
142
+ for (const { qname, qrl } of queueManager.getPairs()) {
143
+ // const qcount = jobExecutor.runningJobCountForQueue(qname)
144
+ // console.log({ evaluating: { qname, qrl, qcount, jobsLeft, activeQrlsHasQrl: activeQrls.has(qrl) } })
145
+ if (jobsLeft <= 0 || activeQrls.has(qrl))
146
+ continue;
147
+ const maxMessages = Math.min(10, jobsLeft);
148
+ listen(qname, qrl, maxMessages);
149
+ jobsLeft -= maxMessages;
150
+ // debug({ listenedTo: { qname, maxMessages, jobsLeft } })
151
+ }
152
+ await delay(1000);
153
+ }
154
+ debug('after all');
256
155
  }
257
156
  exports.processMessages = processMessages;
@@ -5,7 +5,7 @@ exports.setupVerbose = exports.setupAWS = exports.getOptionsWithDefaults = expor
5
5
  * Default options for qdone. Accepts a command line options object and
6
6
  * returns nicely-named options.
7
7
  */
8
- var uuid_1 = require("uuid");
8
+ const uuid_1 = require("uuid");
9
9
  exports.defaults = Object.freeze({
10
10
  // Shared
11
11
  prefix: 'qdone_',
@@ -27,8 +27,8 @@ exports.defaults = Object.freeze({
27
27
  messageRetentionPeriod: 1209600,
28
28
  delay: 0,
29
29
  sendRetries: 6,
30
- failDelay: 0,
31
- dlq: false,
30
+ failDelay: 120,
31
+ dlq: true,
32
32
  dlqSuffix: '_dead',
33
33
  dlqAfter: 3,
34
34
  // Worker
@@ -44,9 +44,9 @@ exports.defaults = Object.freeze({
44
44
  unpair: false
45
45
  });
46
46
  function validateInteger(opt, name) {
47
- var parsed = parseInt(opt[name], 10);
47
+ const parsed = parseInt(opt[name], 10);
48
48
  if (isNaN(parsed))
49
- throw new Error("".concat(name, " needs to be an integer."));
49
+ throw new Error(`${name} needs to be an integer.`);
50
50
  return parsed;
51
51
  }
52
52
  /**
@@ -62,8 +62,8 @@ function getOptionsWithDefaults(options) {
62
62
  if (!options)
63
63
  options = {};
64
64
  // Activate DLQ if any option is set
65
- var dlq = options.dlq || !!(options['dlq-suffix'] || options['dlq-after'] || options['dlq-name']);
66
- var opt = {
65
+ const dlq = options.dlq || !!(options['dlq-suffix'] || options['dlq-after'] || options['dlq-name']);
66
+ const opt = {
67
67
  // Shared
68
68
  prefix: options.prefix === '' ? options.prefix : (options.prefix || exports.defaults.prefix),
69
69
  failSuffix: options.failSuffix || options['fail-suffix'] || exports.defaults.failSuffix,
@@ -85,7 +85,7 @@ function getOptionsWithDefaults(options) {
85
85
  delay: options.delay || exports.defaults.delay,
86
86
  sendRetries: options['send-retries'] || exports.defaults.sendRetries,
87
87
  failDelay: options.failDelay || options['fail-delay'] || exports.defaults.failDelay,
88
- dlq: dlq || exports.defaults.dlq,
88
+ dlq: dlq === false ? false : (dlq || exports.defaults.dlq),
89
89
  dlqSuffix: options.dlqSuffix || options['dlq-suffix'] || exports.defaults.dlqSuffix,
90
90
  dlqAfter: options.dlqAfter || options['dlq-after'] || exports.defaults.dlqAfter,
91
91
  tags: options.tags || undefined,
@@ -120,13 +120,13 @@ function getOptionsWithDefaults(options) {
120
120
  }
121
121
  exports.getOptionsWithDefaults = getOptionsWithDefaults;
122
122
  function setupAWS(options) {
123
- var opt = getOptionsWithDefaults(options);
123
+ const opt = getOptionsWithDefaults(options);
124
124
  process.env.AWS_REGION = opt.region;
125
125
  }
126
126
  exports.setupAWS = setupAWS;
127
127
  function setupVerbose(options) {
128
- var verbose = options.verbose || (process.stderr.isTTY && !options.quiet);
129
- var quiet = options.quiet || (!process.stderr.isTTY && !options.verbose);
128
+ const verbose = options.verbose || (process.stderr.isTTY && !options.quiet);
129
+ const quiet = options.quiet || (!process.stderr.isTTY && !options.verbose);
130
130
  options.verbose = verbose;
131
131
  options.quiet = quiet;
132
132
  }