qdone 2.0.34-alpha → 2.0.36-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/cache.js +3 -2
- package/commonjs/src/dedup.js +264 -0
- package/commonjs/src/defaults.js +42 -5
- package/commonjs/src/enqueue.js +192 -114
- package/commonjs/src/qrlCache.js +2 -1
- package/commonjs/src/scheduler/jobExecutor.js +3 -0
- package/commonjs/src/scheduler/queueManager.js +1 -1
- package/npm-shrinkwrap.json +3 -2
- package/package.json +4 -3
- package/src/cache.js +3 -2
- package/src/check.js +205 -0
- package/src/cli.js +81 -3
- package/src/dedup.js +256 -0
- package/src/defaults.js +40 -4
- package/src/enqueue.js +185 -114
- package/src/qrlCache.js +1 -1
- package/src/scheduler/jobExecutor.js +5 -0
- package/src/scheduler/queueManager.js +1 -1
- package/src/worker.js +6 -0
package/commonjs/src/enqueue.js
CHANGED
|
@@ -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.enqueueBatch = exports.enqueue = exports.addMessage = exports.flushMessages = exports.sendMessageBatch = exports.sendMessage = exports.formatMessage = exports.getQueueAttributes = exports.getOrCreateQueue = exports.getOrCreateFailQueue = exports.getOrCreateDLQ = void 0;
|
|
6
|
+
exports.enqueueBatch = exports.enqueue = exports.addMessage = exports.flushMessages = exports.sendMessageBatch = exports.sendMessage = exports.formatMessage = exports.getQueueAttributes = exports.getOrCreateQueue = exports.getQueueParams = exports.getOrCreateFailQueue = exports.getFailParams = exports.getOrCreateDLQ = exports.getDLQParams = void 0;
|
|
7
7
|
const node_1 = require("@sentry/node");
|
|
8
8
|
const uuid_1 = require("uuid");
|
|
9
9
|
const chalk_1 = __importDefault(require("chalk"));
|
|
@@ -11,12 +11,26 @@ const debug_1 = __importDefault(require("debug"));
|
|
|
11
11
|
const client_sqs_1 = require("@aws-sdk/client-sqs");
|
|
12
12
|
const qrlCache_js_1 = require("./qrlCache.js");
|
|
13
13
|
const sqs_js_1 = require("./sqs.js");
|
|
14
|
+
const dedup_js_1 = require("./dedup.js");
|
|
14
15
|
const defaults_js_1 = require("./defaults.js");
|
|
15
16
|
const exponentialBackoff_js_1 = require("./exponentialBackoff.js");
|
|
16
17
|
const debug = (0, debug_1.default)('qdone:enqueue');
|
|
18
|
+
function getDLQParams(queue, opt) {
|
|
19
|
+
const dqname = (0, qrlCache_js_1.normalizeDLQName)(queue, opt);
|
|
20
|
+
const params = {
|
|
21
|
+
Attributes: { MessageRetentionPeriod: opt.messageRetentionPeriod + '' },
|
|
22
|
+
QueueName: dqname
|
|
23
|
+
};
|
|
24
|
+
if (opt.tags)
|
|
25
|
+
params.tags = opt.tags;
|
|
26
|
+
if (opt.fifo)
|
|
27
|
+
params.Attributes.FifoQueue = 'true';
|
|
28
|
+
return { dqname, params };
|
|
29
|
+
}
|
|
30
|
+
exports.getDLQParams = getDLQParams;
|
|
17
31
|
async function getOrCreateDLQ(queue, opt) {
|
|
18
32
|
debug('getOrCreateDLQ(', queue, ')');
|
|
19
|
-
const dqname = (
|
|
33
|
+
const { dqname, params } = getDLQParams(queue, opt);
|
|
20
34
|
try {
|
|
21
35
|
const dqrl = await (0, qrlCache_js_1.qrlCacheGet)(dqname);
|
|
22
36
|
return dqrl;
|
|
@@ -27,14 +41,6 @@ async function getOrCreateDLQ(queue, opt) {
|
|
|
27
41
|
throw err;
|
|
28
42
|
// Create our DLQ
|
|
29
43
|
const client = (0, sqs_js_1.getSQSClient)();
|
|
30
|
-
const params = {
|
|
31
|
-
Attributes: { MessageRetentionPeriod: opt.messageRetentionPeriod + '' },
|
|
32
|
-
QueueName: dqname
|
|
33
|
-
};
|
|
34
|
-
if (opt.tags)
|
|
35
|
-
params.tags = opt.tags;
|
|
36
|
-
if (opt.fifo)
|
|
37
|
-
params.Attributes.FifoQueue = 'true';
|
|
38
44
|
const cmd = new client_sqs_1.CreateQueueCommand(params);
|
|
39
45
|
if (opt.verbose)
|
|
40
46
|
console.error(chalk_1.default.blue('Creating dead letter queue ') + dqname);
|
|
@@ -46,10 +52,47 @@ async function getOrCreateDLQ(queue, opt) {
|
|
|
46
52
|
}
|
|
47
53
|
}
|
|
48
54
|
exports.getOrCreateDLQ = getOrCreateDLQ;
|
|
49
|
-
|
|
55
|
+
/**
|
|
56
|
+
* Returns the parameters needed for creating a failed queue. If DLQ options
|
|
57
|
+
* are set, it makes an API call to get this DLQ's ARN.
|
|
58
|
+
*/
|
|
59
|
+
async function getFailParams(queue, opt) {
|
|
60
|
+
const fqname = (0, qrlCache_js_1.normalizeFailQueueName)(queue, opt);
|
|
61
|
+
const params = {
|
|
62
|
+
Attributes: { MessageRetentionPeriod: opt.messageRetentionPeriod + '' },
|
|
63
|
+
QueueName: fqname
|
|
64
|
+
};
|
|
65
|
+
// If we have a dlq, we grab it and set a redrive policy
|
|
66
|
+
if (opt.dlq) {
|
|
67
|
+
const dqname = (0, qrlCache_js_1.normalizeDLQName)(queue, opt);
|
|
68
|
+
const dqrl = await (0, qrlCache_js_1.qrlCacheGet)(dqname);
|
|
69
|
+
const dqa = await getQueueAttributes(dqrl);
|
|
70
|
+
debug('dqa', dqa);
|
|
71
|
+
params.Attributes.RedrivePolicy = JSON.stringify({
|
|
72
|
+
deadLetterTargetArn: dqa.Attributes.QueueArn,
|
|
73
|
+
maxReceiveCount: opt.dlqAfter
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
if (opt.failDelay)
|
|
77
|
+
params.Attributes.DelaySeconds = opt.failDelay + '';
|
|
78
|
+
if (opt.tags)
|
|
79
|
+
params.tags = opt.tags;
|
|
80
|
+
if (opt.fifo)
|
|
81
|
+
params.Attributes.FifoQueue = 'true';
|
|
82
|
+
return params;
|
|
83
|
+
}
|
|
84
|
+
exports.getFailParams = getFailParams;
|
|
85
|
+
/**
|
|
86
|
+
* Returns the qrl for the failed queue for the given queue. Creates the queue
|
|
87
|
+
* if it does not exist.
|
|
88
|
+
*/
|
|
89
|
+
async function getOrCreateFailQueue(queue, opt, doesNotExist) {
|
|
50
90
|
debug('getOrCreateFailQueue(', queue, ')');
|
|
51
91
|
const fqname = (0, qrlCache_js_1.normalizeFailQueueName)(queue, opt);
|
|
52
92
|
try {
|
|
93
|
+
// Bail early if the caller knew we didn't have a queue
|
|
94
|
+
if (doesNotExist)
|
|
95
|
+
throw new client_sqs_1.QueueDoesNotExist(fqname);
|
|
53
96
|
const fqrl = await (0, qrlCache_js_1.qrlCacheGet)(fqname);
|
|
54
97
|
return fqrl;
|
|
55
98
|
}
|
|
@@ -57,28 +100,20 @@ async function getOrCreateFailQueue(queue, opt) {
|
|
|
57
100
|
// Anything other than queue doesn't exist gets re-thrown
|
|
58
101
|
if (!(err instanceof client_sqs_1.QueueDoesNotExist))
|
|
59
102
|
throw err;
|
|
60
|
-
//
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
QueueName: fqname
|
|
65
|
-
};
|
|
66
|
-
// If we have a dlq, we grab it and set a redrive policy
|
|
67
|
-
if (opt.dlq) {
|
|
68
|
-
const dqrl = await getOrCreateDLQ(queue, opt);
|
|
69
|
-
const dqa = await getQueueAttributes(dqrl);
|
|
70
|
-
debug('dqa', dqa);
|
|
71
|
-
params.Attributes.RedrivePolicy = JSON.stringify({
|
|
72
|
-
deadLetterTargetArn: dqa.Attributes.QueueArn,
|
|
73
|
-
maxReceiveCount: opt.dlqAfter + ''
|
|
74
|
-
});
|
|
103
|
+
// Grab params, creating DLQ if needed
|
|
104
|
+
let params;
|
|
105
|
+
try {
|
|
106
|
+
params = await getFailParams(queue, opt);
|
|
75
107
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
params
|
|
108
|
+
catch (e) {
|
|
109
|
+
// If DLQ doesn't exist, create it
|
|
110
|
+
if (!(opt.dlq && e instanceof client_sqs_1.QueueDoesNotExist))
|
|
111
|
+
throw e;
|
|
112
|
+
await getOrCreateDLQ(queue, opt);
|
|
113
|
+
params = await getFailParams(queue, opt);
|
|
114
|
+
}
|
|
115
|
+
// Create our fail queue
|
|
116
|
+
const client = (0, sqs_js_1.getSQSClient)();
|
|
82
117
|
const cmd = new client_sqs_1.CreateQueueCommand(params);
|
|
83
118
|
if (opt.verbose)
|
|
84
119
|
console.error(chalk_1.default.blue('Creating fail queue ') + fqname);
|
|
@@ -90,6 +125,32 @@ async function getOrCreateFailQueue(queue, opt) {
|
|
|
90
125
|
}
|
|
91
126
|
}
|
|
92
127
|
exports.getOrCreateFailQueue = getOrCreateFailQueue;
|
|
128
|
+
/**
|
|
129
|
+
* Returns the parameters needed for creating a queue. If fail options
|
|
130
|
+
* are set, it makes an API call to get the fail queue's ARN.
|
|
131
|
+
*/
|
|
132
|
+
async function getQueueParams(queue, opt) {
|
|
133
|
+
const qname = (0, qrlCache_js_1.normalizeQueueName)(queue, opt);
|
|
134
|
+
const fqname = (0, qrlCache_js_1.normalizeFailQueueName)(queue, opt);
|
|
135
|
+
const fqrl = await (0, qrlCache_js_1.qrlCacheGet)(fqname, opt);
|
|
136
|
+
const fqa = await getQueueAttributes(fqrl);
|
|
137
|
+
const params = {
|
|
138
|
+
Attributes: {
|
|
139
|
+
MessageRetentionPeriod: opt.messageRetentionPeriod + '',
|
|
140
|
+
RedrivePolicy: JSON.stringify({
|
|
141
|
+
deadLetterTargetArn: fqa.Attributes.QueueArn,
|
|
142
|
+
maxReceiveCount: 1
|
|
143
|
+
})
|
|
144
|
+
},
|
|
145
|
+
QueueName: qname
|
|
146
|
+
};
|
|
147
|
+
if (opt.tags)
|
|
148
|
+
params.tags = opt.tags;
|
|
149
|
+
if (opt.fifo)
|
|
150
|
+
params.Attributes.FifoQueue = 'true';
|
|
151
|
+
return params;
|
|
152
|
+
}
|
|
153
|
+
exports.getQueueParams = getQueueParams;
|
|
93
154
|
/**
|
|
94
155
|
* Returns a qrl for a queue that either exists or does not
|
|
95
156
|
*/
|
|
@@ -104,31 +165,26 @@ async function getOrCreateQueue(queue, opt) {
|
|
|
104
165
|
// Anything other than queue doesn't exist gets re-thrown
|
|
105
166
|
if (!(err instanceof client_sqs_1.QueueDoesNotExist))
|
|
106
167
|
throw err;
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
168
|
+
// Grab params, creating fail queue if needed
|
|
169
|
+
let params;
|
|
170
|
+
try {
|
|
171
|
+
params = await getQueueParams(qname, opt);
|
|
172
|
+
}
|
|
173
|
+
catch (e) {
|
|
174
|
+
// If fail queue doesn't exist, create it
|
|
175
|
+
if (!(e instanceof client_sqs_1.QueueDoesNotExist))
|
|
176
|
+
throw e;
|
|
177
|
+
await getOrCreateFailQueue(qname, opt, true);
|
|
178
|
+
params = await getQueueParams(qname, opt);
|
|
179
|
+
}
|
|
180
|
+
debug({ getOrCreateQueue: { qname, params } });
|
|
110
181
|
// Create our queue
|
|
111
182
|
const client = (0, sqs_js_1.getSQSClient)();
|
|
112
|
-
const params = {
|
|
113
|
-
Attributes: {
|
|
114
|
-
MessageRetentionPeriod: opt.messageRetentionPeriod + '',
|
|
115
|
-
RedrivePolicy: JSON.stringify({
|
|
116
|
-
deadLetterTargetArn: fqa.Attributes.QueueArn,
|
|
117
|
-
maxReceiveCount: '1'
|
|
118
|
-
})
|
|
119
|
-
},
|
|
120
|
-
QueueName: qname
|
|
121
|
-
};
|
|
122
|
-
if (opt.tags)
|
|
123
|
-
params.tags = opt.tags;
|
|
124
|
-
if (opt.fifo)
|
|
125
|
-
params.Attributes.FifoQueue = 'true';
|
|
126
183
|
const cmd = new client_sqs_1.CreateQueueCommand(params);
|
|
127
|
-
debug({ params });
|
|
128
184
|
if (opt.verbose)
|
|
129
|
-
console.error(chalk_1.default.blue('Creating queue ') + qname);
|
|
185
|
+
console.error(chalk_1.default.blue('Creating fail queue ') + qname);
|
|
130
186
|
const data = await client.send(cmd);
|
|
131
|
-
debug('createQueue returned', data);
|
|
187
|
+
debug('AWS createQueue returned', data);
|
|
132
188
|
const qrl = data.QueueUrl;
|
|
133
189
|
(0, qrlCache_js_1.qrlCacheSet)(qname, qrl);
|
|
134
190
|
return qrl;
|
|
@@ -146,18 +202,18 @@ async function getQueueAttributes(qrl) {
|
|
|
146
202
|
return data;
|
|
147
203
|
}
|
|
148
204
|
exports.getQueueAttributes = getQueueAttributes;
|
|
149
|
-
function formatMessage(
|
|
150
|
-
const message = {
|
|
151
|
-
/*
|
|
152
|
-
MessageAttributes: {
|
|
153
|
-
City: { DataType: 'String', StringValue: 'Any City' },
|
|
154
|
-
Population: { DataType: 'Number', StringValue: '1250800' }
|
|
155
|
-
},
|
|
156
|
-
*/
|
|
157
|
-
MessageBody: command
|
|
158
|
-
};
|
|
205
|
+
function formatMessage(body, id, opt, messageOptions) {
|
|
206
|
+
const message = { MessageBody: body };
|
|
159
207
|
if (typeof id !== 'undefined')
|
|
160
208
|
message.Id = '' + id;
|
|
209
|
+
if (opt.fifo) {
|
|
210
|
+
message.MessageGroupId = messageOptions?.groupId || opt?.groupId;
|
|
211
|
+
}
|
|
212
|
+
(0, dedup_js_1.addDedupParamsToMessage)(message, opt, messageOptions);
|
|
213
|
+
if (opt.delay)
|
|
214
|
+
message.DelaySeconds = opt.delay;
|
|
215
|
+
if (messageOptions?.delay)
|
|
216
|
+
message.DelaySeconds = messageOptions.delay;
|
|
161
217
|
return message;
|
|
162
218
|
}
|
|
163
219
|
exports.formatMessage = formatMessage;
|
|
@@ -167,15 +223,19 @@ const retryableExceptions = [
|
|
|
167
223
|
client_sqs_1.KmsThrottled,
|
|
168
224
|
client_sqs_1.QueueDoesNotExist // Queue could temporarily not exist due to eventual consistency, let it retry
|
|
169
225
|
];
|
|
170
|
-
async function sendMessage(qrl, command, opt) {
|
|
226
|
+
async function sendMessage(qrl, command, opt, messageOptions) {
|
|
171
227
|
debug('sendMessage(', qrl, command, ')');
|
|
172
|
-
const
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
228
|
+
const uuidFunction = opt.uuidFunction || uuid_1.v1;
|
|
229
|
+
const params = {
|
|
230
|
+
QueueUrl: qrl,
|
|
231
|
+
...formatMessage(command, null, opt, messageOptions)
|
|
232
|
+
};
|
|
233
|
+
// See if we even have to send it
|
|
234
|
+
if (opt.externalDedup) {
|
|
235
|
+
const shouldEnqueue = await (0, dedup_js_1.dedupShouldEnqueue)(params, opt);
|
|
236
|
+
if (!shouldEnqueue)
|
|
237
|
+
return { MessageId: uuidFunction() };
|
|
176
238
|
}
|
|
177
|
-
if (opt.delay)
|
|
178
|
-
params.DelaySeconds = opt.delay;
|
|
179
239
|
// Send it
|
|
180
240
|
const client = (0, sqs_js_1.getSQSClient)();
|
|
181
241
|
const cmd = new client_sqs_1.SendMessageCommand(params);
|
|
@@ -188,12 +248,16 @@ async function sendMessage(qrl, command, opt) {
|
|
|
188
248
|
return data;
|
|
189
249
|
};
|
|
190
250
|
const shouldRetry = async (result, error) => {
|
|
251
|
+
if (!error)
|
|
252
|
+
return false;
|
|
191
253
|
for (const exceptionClass of retryableExceptions) {
|
|
192
254
|
if (error instanceof exceptionClass) {
|
|
193
255
|
debug({ sendMessageRetryingBecause: { error, result } });
|
|
194
256
|
return true;
|
|
195
257
|
}
|
|
196
258
|
}
|
|
259
|
+
// If we could not send it, we also need to remove our dedup flag
|
|
260
|
+
await (0, dedup_js_1.dedupSuccessfullyProcessed)(params, opt);
|
|
197
261
|
return false;
|
|
198
262
|
};
|
|
199
263
|
const result = await backoff.run(send, shouldRetry);
|
|
@@ -204,21 +268,16 @@ exports.sendMessage = sendMessage;
|
|
|
204
268
|
async function sendMessageBatch(qrl, messages, opt) {
|
|
205
269
|
debug('sendMessageBatch(', qrl, messages.map(e => Object.assign(Object.assign({}, e), { MessageBody: e.MessageBody.slice(0, 10) + '...' })), ')');
|
|
206
270
|
const params = { Entries: messages, QueueUrl: qrl };
|
|
207
|
-
const uuidFunction = opt.uuidFunction || uuid_1.v1;
|
|
208
|
-
// Add in group id if we're using fifo
|
|
209
|
-
if (opt.fifo) {
|
|
210
|
-
params.Entries = params.Entries.map(message => Object.assign({
|
|
211
|
-
MessageGroupId: opt.groupIdPerMessage ? uuidFunction() : opt.groupId,
|
|
212
|
-
MessageDeduplicationId: opt.deduplicationId || uuidFunction()
|
|
213
|
-
}, message));
|
|
214
|
-
}
|
|
215
|
-
if (opt.delay) {
|
|
216
|
-
params.Entries = params.Entries.map(message => Object.assign({ DelaySeconds: opt.delay }, message));
|
|
217
|
-
}
|
|
218
271
|
if (opt.sentryDsn) {
|
|
219
272
|
(0, node_1.addBreadcrumb)({ category: 'sendMessageBatch', message: JSON.stringify({ params }), level: 'debug' });
|
|
220
273
|
}
|
|
221
274
|
debug({ params });
|
|
275
|
+
// See which messages we even have to send
|
|
276
|
+
if (opt.externalDedup) {
|
|
277
|
+
params.Entries = await (0, dedup_js_1.dedupShouldEnqueueMulti)(params.Entries, opt);
|
|
278
|
+
if (!params.Entries.length)
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
222
281
|
// Send them
|
|
223
282
|
const client = (0, sqs_js_1.getSQSClient)();
|
|
224
283
|
const cmd = new client_sqs_1.SendMessageBatchCommand(params);
|
|
@@ -318,8 +377,8 @@ exports.flushMessages = flushMessages;
|
|
|
318
377
|
// Automaticaly flushes if queue has >= 10 messages.
|
|
319
378
|
// Returns number of messages flushed.
|
|
320
379
|
//
|
|
321
|
-
async function addMessage(qrl, command, messageIndex, opt, sendBuffer) {
|
|
322
|
-
const message = formatMessage(command, messageIndex);
|
|
380
|
+
async function addMessage(qrl, command, messageIndex, opt, sendBuffer, messageOptions) {
|
|
381
|
+
const message = formatMessage(command, messageIndex, opt, messageOptions);
|
|
323
382
|
sendBuffer[qrl] = sendBuffer[qrl] || [];
|
|
324
383
|
sendBuffer[qrl].push(message);
|
|
325
384
|
debug({ location: 'addMessage', sendBuffer });
|
|
@@ -336,8 +395,17 @@ exports.addMessage = addMessage;
|
|
|
336
395
|
async function enqueue(queue, command, options) {
|
|
337
396
|
debug('enqueue(', { queue, command }, ')');
|
|
338
397
|
const opt = (0, defaults_js_1.getOptionsWithDefaults)(options);
|
|
339
|
-
|
|
340
|
-
|
|
398
|
+
if (opt.sentryDsn) {
|
|
399
|
+
(0, node_1.setExtra)({ qdoneOperation: 'enqueue', args: { queue, command, opt } });
|
|
400
|
+
}
|
|
401
|
+
try {
|
|
402
|
+
const qrl = await getOrCreateQueue(queue, opt);
|
|
403
|
+
return sendMessage(qrl, command, opt);
|
|
404
|
+
}
|
|
405
|
+
catch (e) {
|
|
406
|
+
console.log(e);
|
|
407
|
+
throw e;
|
|
408
|
+
}
|
|
341
409
|
}
|
|
342
410
|
exports.enqueue = enqueue;
|
|
343
411
|
//
|
|
@@ -347,39 +415,49 @@ exports.enqueue = enqueue;
|
|
|
347
415
|
async function enqueueBatch(pairs, options) {
|
|
348
416
|
debug('enqueueBatch(', pairs, ')');
|
|
349
417
|
const opt = (0, defaults_js_1.getOptionsWithDefaults)(options);
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
const normalizedPairs = pairs.map(({ queue, command }) => ({
|
|
353
|
-
qname: (0, qrlCache_js_1.normalizeQueueName)(queue, opt),
|
|
354
|
-
command
|
|
355
|
-
}));
|
|
356
|
-
const uniqueQnames = new Set(normalizedPairs.map(p => p.qname));
|
|
357
|
-
// Prefetch qrls / create queues in parallel
|
|
358
|
-
const createPromises = [];
|
|
359
|
-
for (const qname of uniqueQnames) {
|
|
360
|
-
createPromises.push(getOrCreateQueue(qname, opt));
|
|
418
|
+
if (opt.sentryDsn) {
|
|
419
|
+
(0, node_1.setExtra)({ qdoneOperation: 'enqueueBatch', args: { pairs, opt } });
|
|
361
420
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const
|
|
371
|
-
|
|
421
|
+
try {
|
|
422
|
+
// Find unique queues so we can pre-fetch qrls. We do this so that all
|
|
423
|
+
// queues are created prior to going through our flush logic
|
|
424
|
+
const normalizedPairs = pairs.map(({ queue, command, messageOptions }) => ({
|
|
425
|
+
qname: (0, qrlCache_js_1.normalizeQueueName)(queue, opt),
|
|
426
|
+
command,
|
|
427
|
+
messageOptions: (0, defaults_js_1.validateMessageOptions)(messageOptions)
|
|
428
|
+
}));
|
|
429
|
+
const uniqueQnames = new Set(normalizedPairs.map(p => p.qname));
|
|
430
|
+
// Prefetch qrls / create queues in parallel
|
|
431
|
+
const createPromises = [];
|
|
432
|
+
for (const qname of uniqueQnames) {
|
|
433
|
+
createPromises.push(getOrCreateQueue(qname, opt));
|
|
434
|
+
}
|
|
435
|
+
await Promise.all(createPromises);
|
|
436
|
+
// After we've prefetched, all qrls are in cache
|
|
437
|
+
// so go back through the list of pairs and fire off messages
|
|
438
|
+
requestCount = 0;
|
|
439
|
+
const sendBuffer = {};
|
|
440
|
+
let messageIndex = 0;
|
|
441
|
+
let initialFlushTotal = 0;
|
|
442
|
+
for (const { qname, command, messageOptions } of normalizedPairs) {
|
|
443
|
+
const qrl = await getOrCreateQueue(qname, opt);
|
|
444
|
+
initialFlushTotal += await addMessage(qrl, command, messageIndex++, opt, sendBuffer, messageOptions);
|
|
445
|
+
}
|
|
446
|
+
// And flush any remaining messages
|
|
447
|
+
const extraFlushPromises = [];
|
|
448
|
+
for (const qrl in sendBuffer) {
|
|
449
|
+
extraFlushPromises.push(flushMessages(qrl, opt, sendBuffer));
|
|
450
|
+
}
|
|
451
|
+
const extraFlushCounts = await Promise.all(extraFlushPromises);
|
|
452
|
+
const extraFlushTotal = extraFlushCounts.reduce((a, b) => a + b, 0);
|
|
453
|
+
const totalFlushed = initialFlushTotal + extraFlushTotal;
|
|
454
|
+
debug({ initialFlushTotal, extraFlushTotal, totalFlushed });
|
|
455
|
+
return totalFlushed;
|
|
372
456
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
extraFlushPromises.push(flushMessages(qrl, opt, sendBuffer));
|
|
457
|
+
catch (e) {
|
|
458
|
+
console.log(e);
|
|
459
|
+
throw e;
|
|
377
460
|
}
|
|
378
|
-
const extraFlushCounts = await Promise.all(extraFlushPromises);
|
|
379
|
-
const extraFlushTotal = extraFlushCounts.reduce((a, b) => a + b, 0);
|
|
380
|
-
const totalFlushed = initialFlushTotal + extraFlushTotal;
|
|
381
|
-
debug({ initialFlushTotal, extraFlushTotal, totalFlushed });
|
|
382
|
-
return totalFlushed;
|
|
383
461
|
}
|
|
384
462
|
exports.enqueueBatch = enqueueBatch;
|
|
385
463
|
debug('loaded');
|
package/commonjs/src/qrlCache.js
CHANGED
|
@@ -70,7 +70,8 @@ async function qrlCacheGet(qname) {
|
|
|
70
70
|
// debug({ cmd })
|
|
71
71
|
const result = await client.send(cmd);
|
|
72
72
|
// debug('result', result)
|
|
73
|
-
|
|
73
|
+
if (!result)
|
|
74
|
+
throw new client_sqs_1.QueueDoesNotExist(qname);
|
|
74
75
|
const { QueueUrl: qrl } = result;
|
|
75
76
|
// debug('getQueueUrl returned', data)
|
|
76
77
|
qcache.set(qname, qrl);
|
|
@@ -11,6 +11,7 @@ exports.JobExecutor = void 0;
|
|
|
11
11
|
const client_sqs_1 = require("@aws-sdk/client-sqs");
|
|
12
12
|
const chalk_1 = __importDefault(require("chalk"));
|
|
13
13
|
const debug_1 = __importDefault(require("debug"));
|
|
14
|
+
const dedup_js_1 = require("../dedup.js");
|
|
14
15
|
const sqs_js_1 = require("../sqs.js");
|
|
15
16
|
const debug = (0, debug_1.default)('qdone:jobExecutor');
|
|
16
17
|
const maxJobSeconds = 12 * 60 * 60;
|
|
@@ -217,6 +218,8 @@ class JobExecutor {
|
|
|
217
218
|
}
|
|
218
219
|
}
|
|
219
220
|
debug('DeleteMessageBatch returned', result);
|
|
221
|
+
// Mark batch as processed for dedup
|
|
222
|
+
await (0, dedup_js_1.dedupSuccessfullyProcessedMulti)(entries.map(e => this.jobsByMessageId[e.Id]), this.opt);
|
|
220
223
|
// TODO Sentry
|
|
221
224
|
}
|
|
222
225
|
}
|
|
@@ -105,7 +105,7 @@ class QueueManager {
|
|
|
105
105
|
.filter(({ qname, qrl }) => {
|
|
106
106
|
const isFifo = qname.endsWith('.fifo');
|
|
107
107
|
const isDead = isFifo ? qname.endsWith('_dead.fifo') : qname.endsWith('_dead');
|
|
108
|
-
return !isDead;
|
|
108
|
+
return this.opt.includeDead ? true : !isDead;
|
|
109
109
|
})
|
|
110
110
|
// then icehouse
|
|
111
111
|
.filter(({ qname, qrl }) => !this.keepInIcehouse(qrl, now));
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qdone",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.36-alpha",
|
|
4
4
|
"lockfileVersion": 2,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "qdone",
|
|
9
|
-
"version": "2.0.
|
|
9
|
+
"version": "2.0.36-alpha",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@aws-sdk/client-cloudwatch": "3.465.0",
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
"command-line-usage": "^7.0.1",
|
|
19
19
|
"debug": "^4.3.4",
|
|
20
20
|
"ioredis": "^5.3.2",
|
|
21
|
+
"ioredis-mock": "^8.9.0",
|
|
21
22
|
"standard": "^17.1.0",
|
|
22
23
|
"tree-kill": "^1.2.2",
|
|
23
24
|
"uuid": "^9.0.1"
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qdone",
|
|
3
|
-
"version": "2.0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "2.0.36-alpha",
|
|
4
|
+
"description": "A distributed scheduler for SQS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
7
7
|
"module": "./index.js",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"command-line-usage": "^7.0.1",
|
|
22
22
|
"debug": "^4.3.4",
|
|
23
23
|
"ioredis": "^5.3.2",
|
|
24
|
+
"ioredis-mock": "^8.9.0",
|
|
24
25
|
"standard": "^17.1.0",
|
|
25
26
|
"tree-kill": "^1.2.2",
|
|
26
27
|
"uuid": "^9.0.1"
|
|
@@ -50,7 +51,7 @@
|
|
|
50
51
|
"build": "tsc --allowJs index.js --outdir commonjs --esModuleInterop --module commonjs --target es2021 --strict --skipLibCheck --forceConsistentCasingInFileNames",
|
|
51
52
|
"clean": "rm -rf commonjs/src commonjs/*.js coverage",
|
|
52
53
|
"lint": "standard",
|
|
53
|
-
"coverage": "
|
|
54
|
+
"coverage": "NODE_OPTIONS='--experimental-json-modules --experimental-vm-modules --no-warnings' jest --coverage | coveralls",
|
|
54
55
|
"standard": "standard",
|
|
55
56
|
"prep-for-publish": "echo YOU MUST USE NPM TO PREP FOR PUBLISH && pnpm run clean && pnpm run build && npm shrinkwrap --production && echo now commit shrinkwrap and use npm run publish-{next,latest}",
|
|
56
57
|
"publish-latest": "npm publish --tag latest",
|
package/src/cache.js
CHANGED
|
@@ -12,15 +12,16 @@ let client
|
|
|
12
12
|
* how to connect.
|
|
13
13
|
*/
|
|
14
14
|
export function getCacheClient (opt) {
|
|
15
|
+
const RedisClass = opt.Redis || Redis
|
|
15
16
|
if (client) {
|
|
16
17
|
return client
|
|
17
18
|
} else if (opt.cacheUri) {
|
|
18
19
|
const url = new URL(opt.cacheUri)
|
|
19
20
|
if (url.protocol === 'redis:') {
|
|
20
|
-
client = new
|
|
21
|
+
client = new RedisClass(url.toString())
|
|
21
22
|
} else if (url.protocol === 'redis-cluster:') {
|
|
22
23
|
url.protocol = 'redis:'
|
|
23
|
-
client = new
|
|
24
|
+
client = new RedisClass.Cluster([url.toString()], { slotsRefreshInterval: 60 * 1000 })
|
|
24
25
|
} else {
|
|
25
26
|
throw new UsageError(`Only redis:// or redis-cluster:// URLs are currently supported. Got: ${url.protocol}`)
|
|
26
27
|
}
|