@remit/queue-sidecar 0.0.10 → 0.0.11
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/package.json +1 -1
- package/src/metrics.test.ts +18 -0
- package/src/metrics.ts +14 -5
- package/src/store.ts +11 -1
package/package.json
CHANGED
package/src/metrics.test.ts
CHANGED
|
@@ -141,6 +141,23 @@ describe("the sidecar /metrics endpoint", () => {
|
|
|
141
141
|
assert.equal(post.status, 400);
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
+
it("exports how long each queue has gone without handing out a message", async () => {
|
|
145
|
+
store.purgeQueue("work");
|
|
146
|
+
store.sendMessage({ queueName: "work", body: "waiting" });
|
|
147
|
+
const idle = await get("/metrics");
|
|
148
|
+
assert.match(
|
|
149
|
+
idle.body,
|
|
150
|
+
/^remit_queue_last_receive_age_seconds\{queue="work",role="work"\} \d+$/m,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const quiet = store.lastReceiveAgeSeconds("work", Date.now() + 901_000);
|
|
154
|
+
assert.ok(quiet >= 900);
|
|
155
|
+
|
|
156
|
+
store.receiveMessages({ queueName: "work", maxMessages: 1 });
|
|
157
|
+
assert.equal(store.lastReceiveAgeSeconds("work"), 0);
|
|
158
|
+
store.purgeQueue("work");
|
|
159
|
+
});
|
|
160
|
+
|
|
144
161
|
it("counts only visible messages as depth", () => {
|
|
145
162
|
store.sendMessage({ queueName: "work", body: "c" });
|
|
146
163
|
store.receiveMessages({ queueName: "work", maxMessages: 1 });
|
|
@@ -165,6 +182,7 @@ describe("a store that cannot answer for any queue", () => {
|
|
|
165
182
|
getQueueAttributes: () => {
|
|
166
183
|
throw new Error("never reached");
|
|
167
184
|
},
|
|
185
|
+
lastReceiveAgeSeconds: () => 0,
|
|
168
186
|
}),
|
|
169
187
|
/holds no queues/,
|
|
170
188
|
);
|
package/src/metrics.ts
CHANGED
|
@@ -17,9 +17,16 @@ const queueMessages = new Gauge({
|
|
|
17
17
|
registers: [registry],
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
+
const queueLastReceiveAge = new Gauge({
|
|
21
|
+
name: "remit_queue_last_receive_age_seconds",
|
|
22
|
+
help: "Seconds since a queue last handed a message to a consumer, or since the sidecar started if it has not.",
|
|
23
|
+
labelNames: ["queue", "role"],
|
|
24
|
+
registers: [registry],
|
|
25
|
+
});
|
|
26
|
+
|
|
20
27
|
export type QueueDepthSource = Pick<
|
|
21
28
|
QueueStore,
|
|
22
|
-
"listQueues" | "getQueueAttributes"
|
|
29
|
+
"listQueues" | "getQueueAttributes" | "lastReceiveAgeSeconds"
|
|
23
30
|
>;
|
|
24
31
|
|
|
25
32
|
export const queueRoles = (
|
|
@@ -59,11 +66,13 @@ export const collectQueueDepths = (store: QueueDepthSource): void => {
|
|
|
59
66
|
}
|
|
60
67
|
const roles = queueRoles(queues);
|
|
61
68
|
queueMessages.reset();
|
|
69
|
+
queueLastReceiveAge.reset();
|
|
62
70
|
for (const queue of queues) {
|
|
63
71
|
const attributes = store.getQueueAttributes(queue.name);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
72
|
+
const labels = { queue: queue.name, role: roles.get(queue.name) ?? "work" };
|
|
73
|
+
queueMessages.set(labels, attributes.approximateNumberOfMessages);
|
|
74
|
+
if (labels.role === "work") {
|
|
75
|
+
queueLastReceiveAge.set(labels, store.lastReceiveAgeSeconds(queue.name));
|
|
76
|
+
}
|
|
68
77
|
}
|
|
69
78
|
};
|
package/src/store.ts
CHANGED
|
@@ -97,6 +97,8 @@ const toQueueRecord = (row: QueueRow): QueueRecord => ({
|
|
|
97
97
|
export class QueueStore {
|
|
98
98
|
private readonly db: Database.Database;
|
|
99
99
|
private sequenceCounter: bigint;
|
|
100
|
+
private readonly startedAt = Date.now();
|
|
101
|
+
private readonly lastReceiveAt = new Map<string, number>();
|
|
100
102
|
|
|
101
103
|
constructor(filename: string) {
|
|
102
104
|
this.db = new Database(filename);
|
|
@@ -357,7 +359,15 @@ export class QueueStore {
|
|
|
357
359
|
return picked;
|
|
358
360
|
});
|
|
359
361
|
|
|
360
|
-
|
|
362
|
+
const received = receive();
|
|
363
|
+
if (received.length > 0) this.lastReceiveAt.set(input.queueName, now);
|
|
364
|
+
return received;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
lastReceiveAgeSeconds(queueName: string, now = Date.now()): number {
|
|
368
|
+
this.requireQueue(queueName);
|
|
369
|
+
const since = this.lastReceiveAt.get(queueName) ?? this.startedAt;
|
|
370
|
+
return Math.max(0, Math.floor((now - since) / 1000));
|
|
361
371
|
}
|
|
362
372
|
|
|
363
373
|
deleteMessage(queueName: string, receiptHandle: string): void {
|