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/cache.js
CHANGED
|
@@ -16,17 +16,18 @@ let client;
|
|
|
16
16
|
* how to connect.
|
|
17
17
|
*/
|
|
18
18
|
function getCacheClient(opt) {
|
|
19
|
+
const RedisClass = opt.Redis || ioredis_1.default;
|
|
19
20
|
if (client) {
|
|
20
21
|
return client;
|
|
21
22
|
}
|
|
22
23
|
else if (opt.cacheUri) {
|
|
23
24
|
const url = new url_1.URL(opt.cacheUri);
|
|
24
25
|
if (url.protocol === 'redis:') {
|
|
25
|
-
client = new
|
|
26
|
+
client = new RedisClass(url.toString());
|
|
26
27
|
}
|
|
27
28
|
else if (url.protocol === 'redis-cluster:') {
|
|
28
29
|
url.protocol = 'redis:';
|
|
29
|
-
client = new
|
|
30
|
+
client = new RedisClass.Cluster([url.toString()], { slotsRefreshInterval: 60 * 1000 });
|
|
30
31
|
}
|
|
31
32
|
else {
|
|
32
33
|
throw new UsageError(`Only redis:// or redis-cluster:// URLs are currently supported. Got: ${url.protocol}`);
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.dedupSuccessfullyProcessedMulti = exports.dedupSuccessfullyProcessed = exports.dedupShouldEnqueueMulti = exports.dedupShouldEnqueue = exports.statMaintenance = exports.updateStats = exports.addDedupParamsToMessage = exports.getCacheKey = exports.getDeduplicationId = void 0;
|
|
7
|
+
const crypto_1 = require("crypto");
|
|
8
|
+
const uuid_1 = require("uuid");
|
|
9
|
+
const cache_js_1 = require("./cache.js");
|
|
10
|
+
const debug_1 = __importDefault(require("debug"));
|
|
11
|
+
const debug = (0, debug_1.default)('qdone:dedup');
|
|
12
|
+
/**
|
|
13
|
+
* Returns a MessageDeduplicationId key appropriate for using with Amazon SQS
|
|
14
|
+
* for the given message. The passed dedupContent will be returned untouched
|
|
15
|
+
* if it meets all the requirements for SQS's MessageDeduplicationId,
|
|
16
|
+
* otherwise disallowed characters will be replaced by `_` and content longer
|
|
17
|
+
* than 128 characters will be truncated and a hash of the content appended.
|
|
18
|
+
* @param {String} dedupContent - Content used to construct the deduplication id.
|
|
19
|
+
* @param {Object} opt - Opt object from getOptionsWithDefaults()
|
|
20
|
+
* @returns {String} the cache key
|
|
21
|
+
*/
|
|
22
|
+
function getDeduplicationId(dedupContent, opt) {
|
|
23
|
+
debug({ getDeduplicationId: { dedupContent } });
|
|
24
|
+
// Don't transmit long keys to redis
|
|
25
|
+
dedupContent = dedupContent.trim().replace(/[^a-zA-Z0-9!"#$%&'()*+,-./:;<=>?@[\\\]^_`{|}~]/g, '_');
|
|
26
|
+
const max = 128;
|
|
27
|
+
const sep = '...sha1:';
|
|
28
|
+
if (dedupContent.length > max) {
|
|
29
|
+
dedupContent = dedupContent.slice(0, max - sep.length - 40) + '...sha1:' + (0, crypto_1.createHash)('sha1').update(dedupContent).digest('hex');
|
|
30
|
+
}
|
|
31
|
+
return dedupContent;
|
|
32
|
+
}
|
|
33
|
+
exports.getDeduplicationId = getDeduplicationId;
|
|
34
|
+
/**
|
|
35
|
+
* Returns the cache key given a deduplication id.
|
|
36
|
+
* @param {String} dedupId - a deduplication id returned from getDeduplicationId
|
|
37
|
+
* @param opt - Opt object from getOptionsWithDefaults()
|
|
38
|
+
* @returns the cache key
|
|
39
|
+
*/
|
|
40
|
+
function getCacheKey(dedupId, opt) {
|
|
41
|
+
const cacheKey = opt.cachePrefix + 'dedup:' + dedupId;
|
|
42
|
+
debug({ getCacheKey: { cacheKey } });
|
|
43
|
+
return cacheKey;
|
|
44
|
+
}
|
|
45
|
+
exports.getCacheKey = getCacheKey;
|
|
46
|
+
/**
|
|
47
|
+
* Modifies a message (parameters to SendMessageCommand) to add the parameters
|
|
48
|
+
* for whatever deduplication options the caller has set.
|
|
49
|
+
* @param {String} message - parameters to SendMessageCommand
|
|
50
|
+
* @param {Object} opt - Opt object from getOptionsWithDefaults()
|
|
51
|
+
* @param {Object} [messageOptions] - optional per message options. We only care about the key deduplicationId.
|
|
52
|
+
* @returns {Object} the modified parameters/message object
|
|
53
|
+
*/
|
|
54
|
+
function addDedupParamsToMessage(message, opt, messageOptions) {
|
|
55
|
+
// Either of these means we need to calculate an id
|
|
56
|
+
if (opt.fifo || opt.externalDedup) {
|
|
57
|
+
const uuidFunction = opt.uuidFunction || uuid_1.v1;
|
|
58
|
+
if (opt.deduplicationId)
|
|
59
|
+
message.MessageDeduplicationId = opt.deduplicationId;
|
|
60
|
+
if (opt.dedupIdPerMessage)
|
|
61
|
+
message.MessageDeduplicationId = uuidFunction();
|
|
62
|
+
if (messageOptions?.deduplicationId)
|
|
63
|
+
message.MessageDeduplicationId = messageOptions.deduplicationId;
|
|
64
|
+
// Fallback to using the message body
|
|
65
|
+
if (!message.MessageDeduplicationId) {
|
|
66
|
+
message.MessageDeduplicationId = getDeduplicationId(message.MessageBody, opt);
|
|
67
|
+
}
|
|
68
|
+
// Track our own dedup id so we can look it up upon ReceiveMessage
|
|
69
|
+
if (opt.externalDedup) {
|
|
70
|
+
message.MessageAttributes = {
|
|
71
|
+
QdoneDeduplicationId: {
|
|
72
|
+
StringValue: message.MessageDeduplicationId,
|
|
73
|
+
DataType: 'String'
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
// If we are using our own dedup, then we must disable the SQS dedup by
|
|
77
|
+
// providing a different unique ID. Otherwise SQS will interact with us.
|
|
78
|
+
if (opt.fifo)
|
|
79
|
+
message.MessageDeduplicationId = uuidFunction();
|
|
80
|
+
}
|
|
81
|
+
// Non fifo can't have this parameter
|
|
82
|
+
if (!opt.fifo)
|
|
83
|
+
delete message.MessageDeduplicationId;
|
|
84
|
+
}
|
|
85
|
+
return message;
|
|
86
|
+
}
|
|
87
|
+
exports.addDedupParamsToMessage = addDedupParamsToMessage;
|
|
88
|
+
/**
|
|
89
|
+
* Updates statistics in redis, of which there are two:
|
|
90
|
+
* 1. duplicateSet - a set who's members are cache keys and scores are the number of duplicate
|
|
91
|
+
* runs prevented by dedup.
|
|
92
|
+
* 2. expirationSet - a set who's members are cache keys and scores are when the cache key expires
|
|
93
|
+
* @param {String} cacheKey
|
|
94
|
+
* @param {Number} duplicates - the number of duplicates, must be at least 1 to gather stats
|
|
95
|
+
* @param {Number} expireAt - timestamp for when this key's dedupPeriod expires
|
|
96
|
+
* @param {Object} opt - Opt object from getOptionsWithDefaults()
|
|
97
|
+
* @param {Object} pipeline - (Optional) redis pipeline you will exec() yourself
|
|
98
|
+
*/
|
|
99
|
+
async function updateStats(cacheKey, duplicates, expireAt, opt, pipeline) {
|
|
100
|
+
if (duplicates >= 1) {
|
|
101
|
+
const duplicateSet = opt.cachePrefix + 'dedup-stats:duplicateSet';
|
|
102
|
+
const expirationSet = opt.cachePrefix + 'dedup-stats:expirationSet';
|
|
103
|
+
const hadPipeline = !!pipeline;
|
|
104
|
+
if (!hadPipeline)
|
|
105
|
+
pipeline = (0, cache_js_1.getCacheClient)(opt).multi();
|
|
106
|
+
pipeline.zadd(duplicateSet, 'GT', duplicates, cacheKey);
|
|
107
|
+
pipeline.zadd(expirationSet, 'GT', expireAt, cacheKey);
|
|
108
|
+
if (!hadPipeline)
|
|
109
|
+
await pipeline.exec();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
exports.updateStats = updateStats;
|
|
113
|
+
/**
|
|
114
|
+
* Removes expired items from stats.
|
|
115
|
+
*/
|
|
116
|
+
async function statMaintenance(opt) {
|
|
117
|
+
const duplicateSet = opt.cachePrefix + 'dedup-stats:duplicateSet';
|
|
118
|
+
const expirationSet = opt.cachePrefix + 'dedup-stats:expirationSet';
|
|
119
|
+
const client = (0, cache_js_1.getCacheClient)(opt);
|
|
120
|
+
const now = new Date().getTime();
|
|
121
|
+
// Grab a batch of expired keys
|
|
122
|
+
debug({ statMaintenance: { aboutToGo: true, expirationSet } });
|
|
123
|
+
const expiredStats = await client.zrange(expirationSet, '-inf', now, 'BYSCORE');
|
|
124
|
+
debug({ statMaintenance: { expiredStats } });
|
|
125
|
+
// And remove them from indexes, main storage
|
|
126
|
+
if (expiredStats.length) {
|
|
127
|
+
const result = await client.multi()
|
|
128
|
+
.zrem(expirationSet, expiredStats)
|
|
129
|
+
.zrem(duplicateSet, expiredStats)
|
|
130
|
+
.exec();
|
|
131
|
+
debug({ statMaintenance: { result } });
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
exports.statMaintenance = statMaintenance;
|
|
135
|
+
/**
|
|
136
|
+
* Determines whether we should enqueue this message or whether it is a duplicate.
|
|
137
|
+
* Returns true if enqueuing the message would not result in a duplicate.
|
|
138
|
+
* @param {Object} message - Parameters to SendMessageCommand
|
|
139
|
+
* @param {Object} opt - Opt object from getOptionsWithDefaults()
|
|
140
|
+
* @returns {Boolean} true if the message can be enqueued without duplicate, else false
|
|
141
|
+
*/
|
|
142
|
+
async function dedupShouldEnqueue(message, opt) {
|
|
143
|
+
const client = (0, cache_js_1.getCacheClient)(opt);
|
|
144
|
+
const dedupId = message?.MessageAttributes?.QdoneDeduplicationId?.StringValue;
|
|
145
|
+
const cacheKey = getCacheKey(dedupId, opt);
|
|
146
|
+
const expireAt = new Date().getTime() + opt.dedupPeriod;
|
|
147
|
+
const copies = await client.incr(cacheKey);
|
|
148
|
+
debug({ action: 'shouldEnqueue', cacheKey, copies });
|
|
149
|
+
if (copies === 1) {
|
|
150
|
+
await client.expireat(cacheKey, expireAt);
|
|
151
|
+
return true;
|
|
152
|
+
}
|
|
153
|
+
if (opt.dedupStats) {
|
|
154
|
+
const duplicates = copies - 1;
|
|
155
|
+
await updateStats(cacheKey, duplicates, expireAt, opt);
|
|
156
|
+
}
|
|
157
|
+
return false;
|
|
158
|
+
}
|
|
159
|
+
exports.dedupShouldEnqueue = dedupShouldEnqueue;
|
|
160
|
+
/**
|
|
161
|
+
* Determines which messages we should enqueue, returning only those that
|
|
162
|
+
* would not be duplicates.
|
|
163
|
+
* @param {Array[Object]} messages - Entries array for the SendMessageBatchCommand
|
|
164
|
+
* @param {Object} opt - Opt object from getOptionsWithDefaults()
|
|
165
|
+
* @returns {Array[Object]} an array of messages that can be safely enqueued. Could be empty.
|
|
166
|
+
*/
|
|
167
|
+
async function dedupShouldEnqueueMulti(messages, opt) {
|
|
168
|
+
debug({ dedupShouldEnqueueMulti: { messages, opt } });
|
|
169
|
+
const expireAt = new Date().getTime() + opt.dedupPeriod;
|
|
170
|
+
// Increment all
|
|
171
|
+
const incrPipeline = (0, cache_js_1.getCacheClient)(opt).pipeline();
|
|
172
|
+
for (const message of messages) {
|
|
173
|
+
const dedupId = message?.MessageAttributes?.QdoneDeduplicationId?.StringValue;
|
|
174
|
+
const cacheKey = getCacheKey(dedupId, opt);
|
|
175
|
+
incrPipeline.incr(cacheKey);
|
|
176
|
+
}
|
|
177
|
+
const responses = await incrPipeline.exec();
|
|
178
|
+
debug({ dedupShouldEnqueueMulti: { messages, responses } });
|
|
179
|
+
// Interpret responses and expire keys for races we won
|
|
180
|
+
const expirePipeline = (0, cache_js_1.getCacheClient)(opt).pipeline();
|
|
181
|
+
const statsPipeline = opt.dedupStats ? (0, cache_js_1.getCacheClient)(opt).pipeline() : undefined;
|
|
182
|
+
const messagesToEnqueue = [];
|
|
183
|
+
for (let i = 0; i < messages.length; i++) {
|
|
184
|
+
const message = messages[i];
|
|
185
|
+
const [, copies] = responses[i];
|
|
186
|
+
const dedupId = message?.MessageAttributes?.QdoneDeduplicationId?.StringValue;
|
|
187
|
+
const cacheKey = getCacheKey(dedupId, opt);
|
|
188
|
+
if (copies === 1) {
|
|
189
|
+
messagesToEnqueue.push(message);
|
|
190
|
+
expirePipeline.expireat(cacheKey, expireAt);
|
|
191
|
+
}
|
|
192
|
+
else if (opt.dedupStats) {
|
|
193
|
+
const duplicates = copies - 1;
|
|
194
|
+
updateStats(cacheKey, duplicates, expireAt, opt, statsPipeline);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
await expirePipeline.exec();
|
|
198
|
+
if (opt.dedupStats)
|
|
199
|
+
await statsPipeline.exec();
|
|
200
|
+
return messagesToEnqueue;
|
|
201
|
+
}
|
|
202
|
+
exports.dedupShouldEnqueueMulti = dedupShouldEnqueueMulti;
|
|
203
|
+
/**
|
|
204
|
+
* Marks a message as processed so that subsequent calls to dedupShouldEnqueue
|
|
205
|
+
* and dedupShouldEnqueueMulti will allow a message to be enqueued again
|
|
206
|
+
* without waiting for dedupPeriod to expire.
|
|
207
|
+
* @param {Object} message - Return value from RecieveMessageCommand
|
|
208
|
+
* @param {Object} opt - Opt object from getOptionsWithDefaults()
|
|
209
|
+
* @returns {Number} 1 if a cache key was deleted, otherwise 0
|
|
210
|
+
*/
|
|
211
|
+
async function dedupSuccessfullyProcessed(message, opt) {
|
|
212
|
+
const client = (0, cache_js_1.getCacheClient)(opt);
|
|
213
|
+
const dedupId = message?.MessageAttributes?.QdoneDeduplicationId?.StringValue;
|
|
214
|
+
if (dedupId) {
|
|
215
|
+
const cacheKey = getCacheKey(dedupId, opt);
|
|
216
|
+
const count = await client.del(cacheKey);
|
|
217
|
+
// Probabalistic stat maintenance
|
|
218
|
+
if (opt.dedupStats) {
|
|
219
|
+
const chance = 1 / 100.0;
|
|
220
|
+
if (Math.random() < chance)
|
|
221
|
+
await statMaintenance(opt);
|
|
222
|
+
}
|
|
223
|
+
return count;
|
|
224
|
+
}
|
|
225
|
+
return 0;
|
|
226
|
+
}
|
|
227
|
+
exports.dedupSuccessfullyProcessed = dedupSuccessfullyProcessed;
|
|
228
|
+
/**
|
|
229
|
+
* Marks an array of messages as processed so that subsequent calls to
|
|
230
|
+
* dedupShouldEnqueue and dedupShouldEnqueueMulti will allow a message to be
|
|
231
|
+
* enqueued again without waiting for dedupPeriod to expire.
|
|
232
|
+
* @param {Array[Object]} messages - Return values from RecieveMessageCommand
|
|
233
|
+
* @param {Object} opt - Opt object from getOptionsWithDefaults()
|
|
234
|
+
* @returns {Number} number of deleted keys
|
|
235
|
+
*/
|
|
236
|
+
async function dedupSuccessfullyProcessedMulti(messages, opt) {
|
|
237
|
+
debug({ messages, dedupSuccessfullyProcessedMulti: { messages, opt } });
|
|
238
|
+
const cacheKeys = [];
|
|
239
|
+
for (const message of messages) {
|
|
240
|
+
const dedupId = message?.MessageAttributes?.QdoneDeduplicationId?.StringValue;
|
|
241
|
+
if (dedupId) {
|
|
242
|
+
const cacheKey = getCacheKey(dedupId, opt);
|
|
243
|
+
cacheKeys.push(cacheKey);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
debug({ dedupSuccessfullyProcessedMulti: { cacheKeys } });
|
|
247
|
+
if (cacheKeys.length) {
|
|
248
|
+
const numDeleted = await (0, cache_js_1.getCacheClient)(opt).del(cacheKeys);
|
|
249
|
+
// const numDeleted = results.map(([, val]) => val).reduce((a, b) => a + b, 0)
|
|
250
|
+
debug({ dedupSuccessfullyProcessedMulti: { cacheKeys, numDeleted } });
|
|
251
|
+
// Probabalistic stat maintenance
|
|
252
|
+
if (opt.dedupStats) {
|
|
253
|
+
const chance = numDeleted / 100.0;
|
|
254
|
+
if (Math.random() < chance)
|
|
255
|
+
await statMaintenance(opt);
|
|
256
|
+
}
|
|
257
|
+
return numDeleted;
|
|
258
|
+
}
|
|
259
|
+
return 0;
|
|
260
|
+
}
|
|
261
|
+
exports.dedupSuccessfullyProcessedMulti = dedupSuccessfullyProcessedMulti;
|
package/commonjs/src/defaults.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.setupVerbose = exports.setupAWS = exports.getOptionsWithDefaults = exports.defaults = void 0;
|
|
3
|
+
exports.setupVerbose = exports.setupAWS = exports.getOptionsWithDefaults = exports.validateMessageOptions = exports.defaults = void 0;
|
|
4
4
|
/**
|
|
5
5
|
* Default options for qdone. Accepts a command line options object and
|
|
6
6
|
* returns nicely-named options.
|
|
@@ -21,10 +21,14 @@ exports.defaults = Object.freeze({
|
|
|
21
21
|
disableLog: false,
|
|
22
22
|
includeFailed: false,
|
|
23
23
|
includeDead: false,
|
|
24
|
+
externalDedup: false,
|
|
25
|
+
dedupPeriod: 60 * 5,
|
|
26
|
+
dedupStats: false,
|
|
24
27
|
// Enqueue
|
|
25
28
|
groupId: (0, uuid_1.v1)(),
|
|
26
29
|
groupIdPerMessage: false,
|
|
27
30
|
deduplicationId: undefined,
|
|
31
|
+
dedupIdPerMessage: false,
|
|
28
32
|
messageRetentionPeriod: 1209600,
|
|
29
33
|
delay: 0,
|
|
30
34
|
sendRetries: 6,
|
|
@@ -42,7 +46,9 @@ exports.defaults = Object.freeze({
|
|
|
42
46
|
// Idle Queues
|
|
43
47
|
idleFor: 60,
|
|
44
48
|
delete: false,
|
|
45
|
-
unpair: false
|
|
49
|
+
unpair: false,
|
|
50
|
+
// Check
|
|
51
|
+
create: false
|
|
46
52
|
});
|
|
47
53
|
function validateInteger(opt, name) {
|
|
48
54
|
const parsed = parseInt(opt[name], 10);
|
|
@@ -50,6 +56,20 @@ function validateInteger(opt, name) {
|
|
|
50
56
|
throw new Error(`${name} needs to be an integer.`);
|
|
51
57
|
return parsed;
|
|
52
58
|
}
|
|
59
|
+
function validateMessageOptions(messageOptions) {
|
|
60
|
+
const validKeys = ['deduplicationId', 'groupId'];
|
|
61
|
+
if (typeof messageOptions === 'object' &&
|
|
62
|
+
!Array.isArray(messageOptions) &&
|
|
63
|
+
messageOptions !== null) {
|
|
64
|
+
for (const key in messageOptions) {
|
|
65
|
+
if (!validKeys.includes(key))
|
|
66
|
+
throw new Error(`Invalid message option ${key}`);
|
|
67
|
+
}
|
|
68
|
+
return messageOptions;
|
|
69
|
+
}
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
exports.validateMessageOptions = validateMessageOptions;
|
|
53
73
|
/**
|
|
54
74
|
* This function should be called by each exposed API entry point on the
|
|
55
75
|
* options passed in from the caller. It supports options named in camelCase
|
|
@@ -63,7 +83,7 @@ function getOptionsWithDefaults(options) {
|
|
|
63
83
|
if (!options)
|
|
64
84
|
options = {};
|
|
65
85
|
// Activate DLQ if any option is set
|
|
66
|
-
const dlq = options.dlq || !!(options['dlq-suffix'] || options['dlq-after'] || options['dlq-name']);
|
|
86
|
+
const dlq = options.dlq || !!(options['dlq-suffix'] || options['dlq-after'] || options['dlq-name'] || options.dlqSuffix || options.dlqAfter || options.dlqName);
|
|
67
87
|
const opt = {
|
|
68
88
|
// Shared
|
|
69
89
|
prefix: options.prefix === '' ? options.prefix : (options.prefix || exports.defaults.prefix),
|
|
@@ -76,6 +96,9 @@ function getOptionsWithDefaults(options) {
|
|
|
76
96
|
disableLog: options.disableLog || options['disable-log'] || exports.defaults.disableLog,
|
|
77
97
|
includeFailed: options.includeFailed || options['include-failed'] || exports.defaults.includeFailed,
|
|
78
98
|
includeDead: options.includeDead || options['include-dead'] || exports.defaults.includeDead,
|
|
99
|
+
externalDedup: options.externalDedup || options['external-dedup'] || exports.defaults.externalDedup,
|
|
100
|
+
dedupPeriod: options.dedupPeriod || options['dedup-period'] || exports.defaults.dedupPeriod,
|
|
101
|
+
dedupStats: options.dedupStats || options['dedup-stats'] || exports.defaults.dedupStats,
|
|
79
102
|
// Cache
|
|
80
103
|
cacheUri: options.cacheUri || options['cache-uri'] || exports.defaults.cacheUri,
|
|
81
104
|
cachePrefix: options.cachePrefix || options['cache-prefix'] || exports.defaults.cachePrefix,
|
|
@@ -84,6 +107,7 @@ function getOptionsWithDefaults(options) {
|
|
|
84
107
|
groupId: options.groupId || options['group-id'] || exports.defaults.groupId,
|
|
85
108
|
groupIdPerMessage: false,
|
|
86
109
|
deduplicationId: options.deduplicationId || options['deduplication-id'] || exports.defaults.deduplicationId,
|
|
110
|
+
dedupIdPerMessage: options.dedupIdPerMessage || options['dedup-id-per-message'] || exports.defaults.dedupIdPerMessage,
|
|
87
111
|
messageRetentionPeriod: options.messageRetentionPeriod || options['message-retention-period'] || exports.defaults.messageRetentionPeriod,
|
|
88
112
|
delay: options.delay || exports.defaults.delay,
|
|
89
113
|
sendRetries: options['send-retries'] || exports.defaults.sendRetries,
|
|
@@ -102,7 +126,10 @@ function getOptionsWithDefaults(options) {
|
|
|
102
126
|
// Idle Queues
|
|
103
127
|
idleFor: options.idleFor || options['idle-for'] || exports.defaults.idleFor,
|
|
104
128
|
delete: options.delete || exports.defaults.delete,
|
|
105
|
-
unpair: options.delete || exports.defaults.unpair
|
|
129
|
+
unpair: options.delete || exports.defaults.unpair,
|
|
130
|
+
// Check
|
|
131
|
+
create: options.create || exports.defaults.create,
|
|
132
|
+
overwrite: options.overwrite || exports.defaults.overwrite
|
|
106
133
|
};
|
|
107
134
|
// Setting this env here means we don't have to in AWS SDK constructors
|
|
108
135
|
process.env.AWS_REGION = opt.region;
|
|
@@ -111,6 +138,7 @@ function getOptionsWithDefaults(options) {
|
|
|
111
138
|
opt.messageRetentionPeriod = validateInteger(opt, 'messageRetentionPeriod');
|
|
112
139
|
opt.delay = validateInteger(opt, 'delay');
|
|
113
140
|
opt.sendRetries = validateInteger(opt, 'sendRetries');
|
|
141
|
+
opt.dedupPeriod = validateInteger(opt, 'dedupPeriod');
|
|
114
142
|
opt.failDelay = validateInteger(opt, 'failDelay');
|
|
115
143
|
opt.dlqAfter = validateInteger(opt, 'dlqAfter');
|
|
116
144
|
opt.waitTime = validateInteger(opt, 'waitTime');
|
|
@@ -118,6 +146,13 @@ function getOptionsWithDefaults(options) {
|
|
|
118
146
|
opt.maxConcurrentJobs = validateInteger(opt, 'maxConcurrentJobs');
|
|
119
147
|
opt.maxMemoryPercent = validateInteger(opt, 'maxMemoryPercent');
|
|
120
148
|
opt.idleFor = validateInteger(opt, 'idleFor');
|
|
149
|
+
// Validate dedup args
|
|
150
|
+
if (opt.externalDedup && !opt.cacheUri)
|
|
151
|
+
throw new Error('--external-dedup requires the --cache-uri argument');
|
|
152
|
+
if (opt.externalDedup && (!opt.dedupPeriod || opt.dedupPeriod < 1))
|
|
153
|
+
throw new Error('--external-dedup of redis requires a --dedup-period > 1 second');
|
|
154
|
+
if (opt.dedupIdPerMessage && opt.deduplicationId)
|
|
155
|
+
throw new Error('Use either --deduplication-id or --dedup-id-per-message but not both');
|
|
121
156
|
return opt;
|
|
122
157
|
}
|
|
123
158
|
exports.getOptionsWithDefaults = getOptionsWithDefaults;
|