Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion packages/trpc/src/router/cloud-workspace/meter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -365,12 +365,13 @@ describe("settleBoot", () => {
expect(autumnCalls.some((call) => call.method === "track")).toBe(false);
});

test("a settle whose next lock fails keeps the billed window, so a retry does not confirm it again", async () => {
test("a settle whose next lock fails keeps the billed window and moves to a new lock id for the retry", async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The title promises a retry with a new lock id, but the test never runs a retry or checks the lock id. Either add a second settleBoot call that asserts the check call's lock.lockId differs from the first attempt, or rename the test to match what it asserts (the persisted lockSeq: 1).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/trpc/src/router/cloud-workspace/meter.test.ts, line 368:

<comment>The title promises a retry with a new lock id, but the test never runs a retry or checks the lock id. Either add a second `settleBoot` call that asserts the `check` call's `lock.lockId` differs from the first attempt, or rename the test to match what it asserts (the persisted `lockSeq: 1`).</comment>

<file context>
@@ -365,12 +365,13 @@ describe("settleBoot", () => {
 	});
 
-	test("a settle whose next lock fails keeps the billed window, so a retry does not confirm it again", async () => {
+	test("a settle whose next lock fails keeps the billed window and moves to a new lock id for the retry", async () => {
 		renewalError = new Error("autumn down");
 		await expect(settleBoot("boot_1")).rejects.toThrow("autumn down");
</file context>
Suggested change
test("a settle whose next lock fails keeps the billed window and moves to a new lock id for the retry", async () => {
test("a settle whose next lock fails keeps the billed window and persists the advanced lock seq", async () => {

renewalError = new Error("autumn down");
await expect(settleBoot("boot_1")).rejects.toThrow("autumn down");
expect(writes).toContainEqual(
expect.objectContaining({
lockOpen: false,
lockSeq: 1,
finalizedThrough: expect.any(Date),
}),
);
Expand Down
9 changes: 7 additions & 2 deletions packages/trpc/src/router/cloud-workspace/meter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,9 +415,15 @@ export async function settleBoot(
amount: gbMinutes({ ms: end - from, memoryMb: session.memoryMb }),
user,
});
const nextSeq = boot.lockSeq + 1;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: nextSeq also advances for ended sessions, which never request another lock. If recordActiveCpu fails, retries advance lockSeq again and send different lock-sequence metadata under the same bootId:active-cpu idempotency key; only advance it for a running session.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At packages/trpc/src/router/cloud-workspace/meter.ts, line 418:

<comment>`nextSeq` also advances for ended sessions, which never request another lock. If `recordActiveCpu` fails, retries advance `lockSeq` again and send different lock-sequence metadata under the same `bootId:active-cpu` idempotency key; only advance it for a running session.</comment>

<file context>
@@ -415,9 +415,15 @@ export async function settleBoot(
 			amount: gbMinutes({ ms: end - from, memoryMb: session.memoryMb }),
 			user,
 		});
+		const nextSeq = boot.lockSeq + 1;
+		// Autumn answers a repeated lock id with 409, so a retry after a failed renewal needs a fresh one.
 		await db
</file context>
Suggested change
const nextSeq = boot.lockSeq + 1;
const nextSeq = boot.lockSeq + (session.endedAt === null ? 1 : 0);

// Autumn answers a repeated lock id with 409, so a retry after a failed renewal needs a fresh one.
await db
.update(cloudWorkspaceBoots)
.set({ finalizedThrough: new Date(end), lockOpen: false })
.set({
finalizedThrough: new Date(end),
lockOpen: false,
lockSeq: nextSeq,
})
.where(eq(cloudWorkspaceBoots.id, bootId));

if (session.endedAt !== null) {
Expand All @@ -434,7 +440,6 @@ export async function settleBoot(
return "closed";
}

const nextSeq = boot.lockSeq + 1;
const renewal = await autumnClient.check({
customerId: boot.organizationId,
featureId: BOX_GB_MINUTES_FEATURE_ID,
Expand Down
Loading