Skip to content

Commit f3f6f6f

Browse files
committed
fix(awareness-service): stop re-claiming exhausted deliveries
A dead-lettered delivery was left with status 'failed', which claimBatch selects - so the engine re-claimed it every tick, re-attempted it, inflated its attempt count without bound and inserted a fresh dead-letter row each time. Mark exhausted deliveries with a terminal 'dead' status and exclude any delivery at/over the attempt cap from claiming.
1 parent 7dced88 commit f3f6f6f

3 files changed

Lines changed: 18 additions & 4 deletions

File tree

services/awareness-service/api/src/database/entities/Delivery.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ export type DeliveryStatus =
1111
| "pending"
1212
| "delivering"
1313
| "delivered"
14-
| "failed";
14+
| "failed"
15+
| "dead";
1516

1617
/**
1718
* A queued webhook delivery of one packet to one subscription. The unique

services/awareness-service/api/src/openapi.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,13 @@ export const openApiDocument = {
107107
packetId: { type: "string" },
108108
status: {
109109
type: "string",
110-
enum: ["pending", "delivering", "delivered", "failed"],
110+
enum: [
111+
"pending",
112+
"delivering",
113+
"delivered",
114+
"failed",
115+
"dead",
116+
],
111117
},
112118
attempts: { type: "integer" },
113119
nextAttemptAt: { type: "string", format: "date-time" },

services/awareness-service/api/src/services/DeliveryEngine.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,15 +92,19 @@ export class DeliveryEngine {
9292
.update(Delivery)
9393
.set({ status: "delivering" })
9494
.where(
95+
// Only pending/failed deliveries still under the attempt
96+
// limit are claimable. `dead` deliveries (and any that
97+
// already hit the cap) are terminal and never re-claimed.
9598
`id IN (
9699
SELECT id FROM deliveries
97100
WHERE status IN ('pending', 'failed')
101+
AND attempts < :maxAttempts
98102
AND "nextAttemptAt" <= now()
99103
ORDER BY "nextAttemptAt"
100104
LIMIT :limit
101105
FOR UPDATE SKIP LOCKED
102106
)`,
103-
{ limit: BATCH_SIZE },
107+
{ limit: BATCH_SIZE, maxAttempts: config.maxAttempts },
104108
)
105109
.returning("*")
106110
.execute();
@@ -181,8 +185,11 @@ export class DeliveryEngine {
181185
const deliveryRepo = AppDataSource.getRepository(Delivery);
182186

183187
if (attempts >= config.maxAttempts) {
188+
// Terminal: mark `dead` so the engine never re-claims it. (Using
189+
// `failed` here let exhausted deliveries be picked up again every
190+
// tick, inflating attempts and spawning duplicate dead letters.)
184191
await deliveryRepo.update(delivery.id, {
185-
status: "failed",
192+
status: "dead",
186193
attempts,
187194
lastError: message,
188195
lastResponseStatus: responseStatus,

0 commit comments

Comments
 (0)