@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@remit/queue-sidecar",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -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
- queueMessages.set(
65
- { queue: queue.name, role: roles.get(queue.name) ?? "work" },
66
- attributes.approximateNumberOfMessages,
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
- return receive();
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 {