Skip to content

Commit 4944839

Browse files
committed
fix(sandbox): bound the session sandbox sweep by a deadline, not a batch size
1 parent bd119ea commit 4944839

1 file changed

Lines changed: 21 additions & 12 deletions

File tree

‎apps/sim/lib/execution/remote-sandbox/session.ts‎

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,12 @@ const SESSION_SANDBOX_PAUSED_RETENTION_MS = 15 * 60_000
3131

3232
const REAP_LOCK_TIMEOUT_MS = 10_000
3333
const REAP_CONCURRENCY = 8
34-
/** Bounds one sweep's runtime; a backlog drains over the following runs. */
35-
const REAP_BATCH_LIMIT = 200
34+
/**
35+
* A sweep starts no kill after this. One kill takes at most the lock wait plus two 5-second
36+
* provider requests, so the sweep reports within the cron route's 60-second limit; the rest waits
37+
* for the next run.
38+
*/
39+
const REAP_DEADLINE_MS = 30_000
3640

3741
interface SessionAcquisition {
3842
created: CreatedSandbox
@@ -104,26 +108,31 @@ export async function ensureSessionSandbox(args: {
104108
export async function reapPausedSessionSandboxes(): Promise<{
105109
killed: number
106110
skipped: number
111+
deferred: number
107112
failed: number
108113
}> {
109114
const provider = resolveProvider()
110-
const result = { killed: 0, skipped: 0, failed: 0 }
115+
const result = { killed: 0, skipped: 0, deferred: 0, failed: 0 }
111116
if (!provider.listPausedSessionSandboxes || !provider.killPausedSessionSandbox) return result
112117
const killPaused = provider.killPausedSessionSandbox.bind(provider)
113118

119+
const deadline = AbortSignal.timeout(REAP_DEADLINE_MS)
114120
const pausedBeforeMs = Date.now() - SESSION_SANDBOX_PAUSED_RETENTION_MS
115-
const expired = (await provider.listPausedSessionSandboxes())
116-
.filter((sandbox) => sandbox.pausedAtMs <= pausedBeforeMs)
117-
.slice(0, REAP_BATCH_LIMIT)
118-
const outcomes = await mapSettledWithConcurrency(expired, REAP_CONCURRENCY, (sandbox) =>
119-
withSandboxSessionLock(sandbox.sessionKey, AbortSignal.timeout(REAP_LOCK_TIMEOUT_MS), () =>
120-
killPaused(sandbox, pausedBeforeMs)
121-
)
121+
const expired = (await provider.listPausedSessionSandboxes()).filter(
122+
(sandbox) => sandbox.pausedAtMs <= pausedBeforeMs
122123
)
124+
const outcomes = await mapSettledWithConcurrency(expired, REAP_CONCURRENCY, async (sandbox) => {
125+
if (deadline.aborted) return 'deferred' as const
126+
const killed = await withSandboxSessionLock(
127+
sandbox.sessionKey,
128+
AbortSignal.timeout(REAP_LOCK_TIMEOUT_MS),
129+
() => killPaused(sandbox, pausedBeforeMs)
130+
)
131+
return killed ? ('killed' as const) : ('skipped' as const)
132+
})
123133
outcomes.forEach((outcome, index) => {
124134
if (outcome.status === 'fulfilled') {
125-
if (outcome.value) result.killed++
126-
else result.skipped++
135+
result[outcome.value]++
127136
return
128137
}
129138
result.failed++

0 commit comments

Comments
 (0)