Skip to content

Commit 75c1790

Browse files
849261680clawsweeper[bot]Takhoffman
authored
fix(outbound): preserve retries for budget-deferred deliveries (#91241)
Summary: - The branch removes the budget-deferred `failDelivery` path from outbound recovery and updates the `maxRecoveryMs` regression expectation so unattempted deliveries keep retry counts at zero. - PR surface: Source -11, Tests -1. Total -12 across 2 files. - Reproducibility: yes. at source level: current main reaches `failDelivery` from the exhausted recovery-budge ... in this read-only review, but the PR body also supplies terminal output showing the after-fix queue state. Automerge notes: - PR branch already contained follow-up commit before automerge: fix(outbound): preserve retries for budget-deferred deliveries Validation: - ClawSweeper review passed for head aff2b9d. - Required merge gates passed before the squash merge. Prepared head SHA: aff2b9d Review: #91241 (comment) Co-authored-by: 宇宙熊Yzx <53250620+849261680@users.noreply.github.com> Co-authored-by: clawsweeper <274271284+clawsweeper[bot]@users.noreply.github.com> Co-authored-by: clawsweeper[bot] <274271284+clawsweeper[bot]@users.noreply.github.com> Approved-by: takhoffman Co-authored-by: takhoffman <781889+takhoffman@users.noreply.github.com>
1 parent b16a435 commit 75c1790

2 files changed

Lines changed: 5 additions & 17 deletions

File tree

src/infra/outbound/delivery-queue-recovery.ts

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -326,17 +326,6 @@ async function moveEntryToFailedWithLogging(
326326
}
327327
}
328328

329-
async function deferRemainingEntriesForBudget(
330-
entries: readonly QueuedDelivery[],
331-
stateDir: string | undefined,
332-
): Promise<void> {
333-
// Increment retryCount so entries that are repeatedly deferred by the
334-
// recovery budget eventually hit MAX_RETRIES and get pruned.
335-
await Promise.allSettled(
336-
entries.map((entry) => failDelivery(entry.id, "recovery time budget exceeded", stateDir)),
337-
);
338-
}
339-
340329
/** Compute the backoff delay in ms for a given retry count. */
341330
export function computeBackoffMs(retryCount: number): number {
342331
if (retryCount <= 0) {
@@ -624,12 +613,12 @@ export async function recoverPendingDeliveries(opts: {
624613
const deadline = resolveRecoveryDeadlineMs(opts.maxRecoveryMs);
625614
const summary = createEmptyRecoverySummary();
626615

627-
for (let i = 0; i < pending.length; i++) {
628-
const entry = pending[i];
616+
for (const entry of pending) {
629617
const now = Date.now();
630618
if (now >= deadline) {
631619
opts.log.warn(`Recovery time budget exceeded — remaining entries deferred to next startup`);
632-
await deferRemainingEntriesForBudget(pending.slice(i), opts.stateDir);
620+
// Budget deferral is not a delivery attempt. Keep entries pending without
621+
// consuming retry budget; attempted failures still flow through failDelivery.
633622
break;
634623
}
635624

src/infra/outbound/delivery-queue.recovery.test.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -615,7 +615,7 @@ describe("delivery-queue recovery", () => {
615615
});
616616
});
617617

618-
it("respects maxRecoveryMs time budget and bumps deferred retries", async () => {
618+
it("respects maxRecoveryMs time budget without bumping deferred retries", async () => {
619619
await enqueueCrashRecoveryEntries();
620620
await enqueueDelivery(
621621
{ channel: "demo-channel-c", to: "#c", payloads: [{ text: "c" }] },
@@ -638,8 +638,7 @@ describe("delivery-queue recovery", () => {
638638

639639
const remaining = await loadPendingDeliveries(tmpDir());
640640
expect(remaining).toHaveLength(3);
641-
const entriesWithUnexpectedRetryCount = remaining.filter((entry) => entry.retryCount !== 1);
642-
expect(entriesWithUnexpectedRetryCount).toStrictEqual([]);
641+
expect(remaining.map((entry) => entry.retryCount)).toStrictEqual([0, 0, 0]);
643642
expectMockMessageContaining(log.warn, "deferred to next startup");
644643
});
645644

0 commit comments

Comments
 (0)