qdone 2.2.2 → 2.2.4
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/dedup.js +3 -3
- package/commonjs/src/monitor.js +15 -3
- package/package.json +1 -1
- package/src/cli.js +5 -3
- package/src/dedup.js +3 -3
- package/src/monitor.js +17 -4
package/commonjs/src/dedup.js
CHANGED
|
@@ -125,7 +125,7 @@ async function statMaintenance(opt) {
|
|
|
125
125
|
const duplicateSet = opt.cachePrefix + 'dedup-stats:duplicateSet';
|
|
126
126
|
const expirationSet = opt.cachePrefix + 'dedup-stats:expirationSet';
|
|
127
127
|
const client = (0, cache_js_1.getCacheClient)(opt);
|
|
128
|
-
const now =
|
|
128
|
+
const now = Math.floor(Date.now() / 1000);
|
|
129
129
|
// Grab a batch of expired keys
|
|
130
130
|
debug({ statMaintenance: { aboutToGo: true, expirationSet } });
|
|
131
131
|
const expiredStats = await client.zrange(expirationSet, '-inf', now, 'BYSCORE');
|
|
@@ -150,7 +150,7 @@ async function dedupShouldEnqueue(message, opt) {
|
|
|
150
150
|
const client = (0, cache_js_1.getCacheClient)(opt);
|
|
151
151
|
const dedupId = message?.MessageAttributes?.QdoneDeduplicationId?.StringValue;
|
|
152
152
|
const cacheKey = getCacheKey(dedupId, opt);
|
|
153
|
-
const expireAt =
|
|
153
|
+
const expireAt = Math.floor(Date.now() / 1000) + opt.dedupPeriod;
|
|
154
154
|
const copies = await client.incr(cacheKey);
|
|
155
155
|
debug({ action: 'shouldEnqueue', cacheKey, copies });
|
|
156
156
|
if (copies === 1) {
|
|
@@ -172,7 +172,7 @@ async function dedupShouldEnqueue(message, opt) {
|
|
|
172
172
|
*/
|
|
173
173
|
async function dedupShouldEnqueueMulti(messages, opt) {
|
|
174
174
|
debug({ dedupShouldEnqueueMulti: { messages, opt } });
|
|
175
|
-
const expireAt =
|
|
175
|
+
const expireAt = Math.floor(Date.now() / 1000) + opt.dedupPeriod;
|
|
176
176
|
// Increment all
|
|
177
177
|
const incrPipeline = (0, cache_js_1.getCacheClient)(opt).pipeline();
|
|
178
178
|
for (const message of messages) {
|
package/commonjs/src/monitor.js
CHANGED
|
@@ -26,7 +26,7 @@ async function monitor(queue, save, options) {
|
|
|
26
26
|
const opt = (0, defaults_js_1.getOptionsWithDefaults)(options);
|
|
27
27
|
const queueName = (0, qrlCache_js_1.normalizeQueueName)(queue, opt);
|
|
28
28
|
debug({ options, opt, queue, queueName });
|
|
29
|
-
const data = await getAggregateData(queueName);
|
|
29
|
+
const data = await getAggregateData(queueName, opt);
|
|
30
30
|
console.log(data);
|
|
31
31
|
if (save) {
|
|
32
32
|
if (opt.verbose)
|
|
@@ -85,7 +85,7 @@ async function getQueueAge(queueName) {
|
|
|
85
85
|
* Metrics (from CloudWatch):
|
|
86
86
|
* - ApproximateAgeOfOldestMessage: Max
|
|
87
87
|
*/
|
|
88
|
-
async function getAggregateData(queueName) {
|
|
88
|
+
async function getAggregateData(queueName, opt) {
|
|
89
89
|
const { prefix, suffixRegex } = interpretWildcard(queueName);
|
|
90
90
|
const qrls = await (0, sqs_js_1.getMatchingQueues)(prefix, suffixRegex);
|
|
91
91
|
// debug({ qrls })
|
|
@@ -105,7 +105,19 @@ async function getAggregateData(queueName) {
|
|
|
105
105
|
}
|
|
106
106
|
// Fetch ApproximateAgeOfOldestMessage from CloudWatch (not available via SQS API)
|
|
107
107
|
// Only query queues with messages to minimize CloudWatch API costs
|
|
108
|
-
|
|
108
|
+
// Filter out dead and failed queues for age calculation only — their messages
|
|
109
|
+
// age indefinitely by design, polluting the active age metric.
|
|
110
|
+
// But if the pattern itself targets dead/failed queues, don't filter them out.
|
|
111
|
+
const failSuffix = (opt && opt.failSuffix) || defaults_js_1.defaults.failSuffix;
|
|
112
|
+
const dlqSuffix = (opt && opt.dlqSuffix) || defaults_js_1.defaults.dlqSuffix;
|
|
113
|
+
const strippedPattern = queueName.replace(/\.fifo$/, '');
|
|
114
|
+
const patternTargetsDeadFailed = strippedPattern.endsWith(failSuffix) || strippedPattern.endsWith(dlqSuffix);
|
|
115
|
+
const esc = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
116
|
+
const deadFailedRegex = new RegExp(`(${esc(failSuffix)}|${esc(dlqSuffix)})(\\.fifo)?$`);
|
|
117
|
+
const activeQueueNames = patternTargetsDeadFailed
|
|
118
|
+
? [...total.contributingQueueNames]
|
|
119
|
+
: [...total.contributingQueueNames].filter(q => !deadFailedRegex.test(q));
|
|
120
|
+
const ageResults = await Promise.all(activeQueueNames.map(queue => getQueueAge(queue)));
|
|
109
121
|
total.ApproximateAgeOfOldestMessage = Math.max(0, ...ageResults);
|
|
110
122
|
// debug({ total })
|
|
111
123
|
// convert set to array
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -197,10 +197,11 @@ export async function check (argv, testHook) {
|
|
|
197
197
|
}
|
|
198
198
|
|
|
199
199
|
const monitorOptionDefinitions = [
|
|
200
|
-
{ name: 'save', alias: 's', type: Boolean, description: 'Saves data to CloudWatch' }
|
|
200
|
+
{ name: 'save', alias: 's', type: Boolean, description: 'Saves data to CloudWatch' },
|
|
201
|
+
{ name: 'dlq-suffix', type: String, description: `Suffix to append to each queue to generate DLQ name [default: ${defaults.dlqSuffix}]` }
|
|
201
202
|
]
|
|
202
203
|
|
|
203
|
-
export async function monitor (argv) {
|
|
204
|
+
export async function monitor (argv, testHook) {
|
|
204
205
|
const optionDefinitions = [].concat(monitorOptionDefinitions, globalOptionDefinitions)
|
|
205
206
|
const usageSections = [
|
|
206
207
|
{ content: 'usage: qdone monitor <queuePattern> ', raw: true },
|
|
@@ -234,7 +235,8 @@ export async function monitor (argv) {
|
|
|
234
235
|
|
|
235
236
|
// Load module after AWS global load
|
|
236
237
|
setupAWS(options)
|
|
237
|
-
const { monitor } = await import('./monitor.js')
|
|
238
|
+
const { monitor: monitorOriginal } = await import('./monitor.js')
|
|
239
|
+
const monitor = testHook || monitorOriginal
|
|
238
240
|
return monitor(queue, options.save, options)
|
|
239
241
|
}
|
|
240
242
|
|
package/src/dedup.js
CHANGED
|
@@ -112,7 +112,7 @@ export async function statMaintenance (opt) {
|
|
|
112
112
|
const duplicateSet = opt.cachePrefix + 'dedup-stats:duplicateSet'
|
|
113
113
|
const expirationSet = opt.cachePrefix + 'dedup-stats:expirationSet'
|
|
114
114
|
const client = getCacheClient(opt)
|
|
115
|
-
const now =
|
|
115
|
+
const now = Math.floor(Date.now() / 1000)
|
|
116
116
|
|
|
117
117
|
// Grab a batch of expired keys
|
|
118
118
|
debug({ statMaintenance: { aboutToGo: true, expirationSet } })
|
|
@@ -140,7 +140,7 @@ export async function dedupShouldEnqueue (message, opt) {
|
|
|
140
140
|
const client = getCacheClient(opt)
|
|
141
141
|
const dedupId = message?.MessageAttributes?.QdoneDeduplicationId?.StringValue
|
|
142
142
|
const cacheKey = getCacheKey(dedupId, opt)
|
|
143
|
-
const expireAt =
|
|
143
|
+
const expireAt = Math.floor(Date.now() / 1000) + opt.dedupPeriod
|
|
144
144
|
const copies = await client.incr(cacheKey)
|
|
145
145
|
debug({ action: 'shouldEnqueue', cacheKey, copies })
|
|
146
146
|
if (copies === 1) {
|
|
@@ -163,7 +163,7 @@ export async function dedupShouldEnqueue (message, opt) {
|
|
|
163
163
|
*/
|
|
164
164
|
export async function dedupShouldEnqueueMulti (messages, opt) {
|
|
165
165
|
debug({ dedupShouldEnqueueMulti: { messages, opt } })
|
|
166
|
-
const expireAt =
|
|
166
|
+
const expireAt = Math.floor(Date.now() / 1000) + opt.dedupPeriod
|
|
167
167
|
// Increment all
|
|
168
168
|
const incrPipeline = getCacheClient(opt).pipeline()
|
|
169
169
|
for (const message of messages) {
|
package/src/monitor.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { getMatchingQueues, getQueueAttributes } from './sqs.js'
|
|
6
6
|
import { putAggregateData, getCloudWatchClient } from './cloudWatch.js'
|
|
7
7
|
import { GetMetricStatisticsCommand } from '@aws-sdk/client-cloudwatch'
|
|
8
|
-
import { getOptionsWithDefaults } from './defaults.js'
|
|
8
|
+
import { getOptionsWithDefaults, defaults } from './defaults.js'
|
|
9
9
|
import { normalizeQueueName } from './qrlCache.js'
|
|
10
10
|
import Debug from 'debug'
|
|
11
11
|
const debug = Debug('qdone:monitor')
|
|
@@ -18,7 +18,7 @@ export async function monitor (queue, save, options) {
|
|
|
18
18
|
const opt = getOptionsWithDefaults(options)
|
|
19
19
|
const queueName = normalizeQueueName(queue, opt)
|
|
20
20
|
debug({ options, opt, queue, queueName })
|
|
21
|
-
const data = await getAggregateData(queueName)
|
|
21
|
+
const data = await getAggregateData(queueName, opt)
|
|
22
22
|
console.log(data)
|
|
23
23
|
if (save) {
|
|
24
24
|
if (opt.verbose) process.stderr.write('Saving to CloudWatch...')
|
|
@@ -76,7 +76,7 @@ export async function getQueueAge (queueName) {
|
|
|
76
76
|
* Metrics (from CloudWatch):
|
|
77
77
|
* - ApproximateAgeOfOldestMessage: Max
|
|
78
78
|
*/
|
|
79
|
-
export async function getAggregateData (queueName) {
|
|
79
|
+
export async function getAggregateData (queueName, opt) {
|
|
80
80
|
const { prefix, suffixRegex } = interpretWildcard(queueName)
|
|
81
81
|
const qrls = await getMatchingQueues(prefix, suffixRegex)
|
|
82
82
|
// debug({ qrls })
|
|
@@ -97,8 +97,21 @@ export async function getAggregateData (queueName) {
|
|
|
97
97
|
|
|
98
98
|
// Fetch ApproximateAgeOfOldestMessage from CloudWatch (not available via SQS API)
|
|
99
99
|
// Only query queues with messages to minimize CloudWatch API costs
|
|
100
|
+
// Filter out dead and failed queues for age calculation only — their messages
|
|
101
|
+
// age indefinitely by design, polluting the active age metric.
|
|
102
|
+
// But if the pattern itself targets dead/failed queues, don't filter them out.
|
|
103
|
+
const failSuffix = (opt && opt.failSuffix) || defaults.failSuffix
|
|
104
|
+
const dlqSuffix = (opt && opt.dlqSuffix) || defaults.dlqSuffix
|
|
105
|
+
const strippedPattern = queueName.replace(/\.fifo$/, '')
|
|
106
|
+
const patternTargetsDeadFailed = strippedPattern.endsWith(failSuffix) || strippedPattern.endsWith(dlqSuffix)
|
|
107
|
+
const esc = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
108
|
+
const deadFailedRegex = new RegExp(`(${esc(failSuffix)}|${esc(dlqSuffix)})(\\.fifo)?$`)
|
|
109
|
+
const activeQueueNames = patternTargetsDeadFailed
|
|
110
|
+
? [...total.contributingQueueNames]
|
|
111
|
+
: [...total.contributingQueueNames].filter(q => !deadFailedRegex.test(q))
|
|
112
|
+
|
|
100
113
|
const ageResults = await Promise.all(
|
|
101
|
-
|
|
114
|
+
activeQueueNames.map(queue => getQueueAge(queue))
|
|
102
115
|
)
|
|
103
116
|
total.ApproximateAgeOfOldestMessage = Math.max(0, ...ageResults)
|
|
104
117
|
|