qdone 2.0.35-alpha → 2.0.37-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 +261 -0
- package/commonjs/src/defaults.js +39 -4
- package/commonjs/src/enqueue.js +194 -114
- package/commonjs/src/qrlCache.js +2 -1
- package/commonjs/src/scheduler/jobExecutor.js +3 -0
- package/package.json +3 -2
- package/src/cache.js +3 -2
- package/src/check.js +205 -0
- package/src/cli.js +81 -3
- package/src/dedup.js +252 -0
- package/src/defaults.js +37 -3
- package/src/enqueue.js +186 -114
- package/src/qrlCache.js +1 -1
- package/src/scheduler/jobExecutor.js +5 -0
- package/src/worker.js +6 -0
- package/npm-shrinkwrap.json +0 -15999
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,18 @@ 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
|
+
const promises = params.Entries.map(async (m) => ({ m, shouldEnqueue: await (0, dedup_js_1.dedupShouldEnqueue)(m, opt) }));
|
|
278
|
+
const results = await Promise.all(promises);
|
|
279
|
+
params.Entries = results.filter(({ shouldEnqueue }) => shouldEnqueue);
|
|
280
|
+
if (!params.Entries.length)
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
222
283
|
// Send them
|
|
223
284
|
const client = (0, sqs_js_1.getSQSClient)();
|
|
224
285
|
const cmd = new client_sqs_1.SendMessageBatchCommand(params);
|
|
@@ -318,8 +379,8 @@ exports.flushMessages = flushMessages;
|
|
|
318
379
|
// Automaticaly flushes if queue has >= 10 messages.
|
|
319
380
|
// Returns number of messages flushed.
|
|
320
381
|
//
|
|
321
|
-
async function addMessage(qrl, command, messageIndex, opt, sendBuffer) {
|
|
322
|
-
const message = formatMessage(command, messageIndex);
|
|
382
|
+
async function addMessage(qrl, command, messageIndex, opt, sendBuffer, messageOptions) {
|
|
383
|
+
const message = formatMessage(command, messageIndex, opt, messageOptions);
|
|
323
384
|
sendBuffer[qrl] = sendBuffer[qrl] || [];
|
|
324
385
|
sendBuffer[qrl].push(message);
|
|
325
386
|
debug({ location: 'addMessage', sendBuffer });
|
|
@@ -336,8 +397,17 @@ exports.addMessage = addMessage;
|
|
|
336
397
|
async function enqueue(queue, command, options) {
|
|
337
398
|
debug('enqueue(', { queue, command }, ')');
|
|
338
399
|
const opt = (0, defaults_js_1.getOptionsWithDefaults)(options);
|
|
339
|
-
|
|
340
|
-
|
|
400
|
+
if (opt.sentryDsn) {
|
|
401
|
+
(0, node_1.setExtra)({ qdoneOperation: 'enqueue', args: { queue, command, opt } });
|
|
402
|
+
}
|
|
403
|
+
try {
|
|
404
|
+
const qrl = await getOrCreateQueue(queue, opt);
|
|
405
|
+
return sendMessage(qrl, command, opt);
|
|
406
|
+
}
|
|
407
|
+
catch (e) {
|
|
408
|
+
console.log(e);
|
|
409
|
+
throw e;
|
|
410
|
+
}
|
|
341
411
|
}
|
|
342
412
|
exports.enqueue = enqueue;
|
|
343
413
|
//
|
|
@@ -347,39 +417,49 @@ exports.enqueue = enqueue;
|
|
|
347
417
|
async function enqueueBatch(pairs, options) {
|
|
348
418
|
debug('enqueueBatch(', pairs, ')');
|
|
349
419
|
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));
|
|
420
|
+
if (opt.sentryDsn) {
|
|
421
|
+
(0, node_1.setExtra)({ qdoneOperation: 'enqueueBatch', args: { pairs, opt } });
|
|
361
422
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const
|
|
371
|
-
|
|
423
|
+
try {
|
|
424
|
+
// Find unique queues so we can pre-fetch qrls. We do this so that all
|
|
425
|
+
// queues are created prior to going through our flush logic
|
|
426
|
+
const normalizedPairs = pairs.map(({ queue, command, messageOptions }) => ({
|
|
427
|
+
qname: (0, qrlCache_js_1.normalizeQueueName)(queue, opt),
|
|
428
|
+
command,
|
|
429
|
+
messageOptions: (0, defaults_js_1.validateMessageOptions)(messageOptions)
|
|
430
|
+
}));
|
|
431
|
+
const uniqueQnames = new Set(normalizedPairs.map(p => p.qname));
|
|
432
|
+
// Prefetch qrls / create queues in parallel
|
|
433
|
+
const createPromises = [];
|
|
434
|
+
for (const qname of uniqueQnames) {
|
|
435
|
+
createPromises.push(getOrCreateQueue(qname, opt));
|
|
436
|
+
}
|
|
437
|
+
await Promise.all(createPromises);
|
|
438
|
+
// After we've prefetched, all qrls are in cache
|
|
439
|
+
// so go back through the list of pairs and fire off messages
|
|
440
|
+
requestCount = 0;
|
|
441
|
+
const sendBuffer = {};
|
|
442
|
+
let messageIndex = 0;
|
|
443
|
+
let initialFlushTotal = 0;
|
|
444
|
+
for (const { qname, command, messageOptions } of normalizedPairs) {
|
|
445
|
+
const qrl = await getOrCreateQueue(qname, opt);
|
|
446
|
+
initialFlushTotal += await addMessage(qrl, command, messageIndex++, opt, sendBuffer, messageOptions);
|
|
447
|
+
}
|
|
448
|
+
// And flush any remaining messages
|
|
449
|
+
const extraFlushPromises = [];
|
|
450
|
+
for (const qrl in sendBuffer) {
|
|
451
|
+
extraFlushPromises.push(flushMessages(qrl, opt, sendBuffer));
|
|
452
|
+
}
|
|
453
|
+
const extraFlushCounts = await Promise.all(extraFlushPromises);
|
|
454
|
+
const extraFlushTotal = extraFlushCounts.reduce((a, b) => a + b, 0);
|
|
455
|
+
const totalFlushed = initialFlushTotal + extraFlushTotal;
|
|
456
|
+
debug({ initialFlushTotal, extraFlushTotal, totalFlushed });
|
|
457
|
+
return totalFlushed;
|
|
372
458
|
}
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
extraFlushPromises.push(flushMessages(qrl, opt, sendBuffer));
|
|
459
|
+
catch (e) {
|
|
460
|
+
console.log(e);
|
|
461
|
+
throw e;
|
|
377
462
|
}
|
|
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
463
|
}
|
|
384
464
|
exports.enqueueBatch = enqueueBatch;
|
|
385
465
|
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 Promise.all(entries.map(e => (0, dedup_js_1.dedupSuccessfullyProcessed)(this.jobsByMessageId[e.Id], this.opt)));
|
|
220
223
|
// TODO Sentry
|
|
221
224
|
}
|
|
222
225
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "qdone",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.37-alpha",
|
|
4
4
|
"description": "A distributed scheduler for SQS",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./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
|
}
|