Add auto-pause TTL and ping endpoint for sandbox inactivity management
Replace the existing auto-destroy TTL behavior with auto-pause: when a
sandbox exceeds its timeout_sec of inactivity, the TTL reaper now pauses
it (snapshot + teardown) instead of destroying it, preserving the ability
to resume later.
Key changes:
- TTL reaper calls Pause instead of Destroy, with fallback to Destroy if
pause fails (e.g. Firecracker process already gone)
- New PingSandbox RPC resets the in-memory LastActiveAt timer
- New POST /v1/sandboxes/{id}/ping REST endpoint resets both agent memory
and DB last_active_at
- ListSandboxes RPC now includes auto_paused_sandbox_ids so the reconciler
can distinguish auto-paused sandboxes from crashed ones in a single call
- Reconciler polls every 5s (was 30s) and marks auto-paused as "paused"
vs orphaned as "stopped"
- Resume RPC accepts timeout_sec from DB so TTL survives pause/resume cycles
- Reaper checks every 2s (was 10s) and uses a detached context to avoid
incomplete pauses on app shutdown
- Default timeout_sec changed from 300 to 0 (no auto-pause unless requested)
This commit is contained in:
@ -175,8 +175,8 @@ const testUIHTML = `<!DOCTYPE html>
|
||||
<input type="number" id="create-vcpus" value="1" min="1" max="8">
|
||||
<label>Memory (MB)</label>
|
||||
<input type="number" id="create-memory" value="512" min="128" max="8192">
|
||||
<label>Timeout (sec)</label>
|
||||
<input type="number" id="create-timeout" value="300" min="30">
|
||||
<label>Timeout (sec, 0 = no auto-pause)</label>
|
||||
<input type="number" id="create-timeout" value="0" min="0">
|
||||
<div class="btn-row">
|
||||
<button class="btn-green" onclick="createSandbox()">Create</button>
|
||||
</div>
|
||||
@ -417,7 +417,7 @@ function renderSandboxes(sandboxes) {
|
||||
document.getElementById('sandboxes-table').innerHTML = '<p style="color:#5f5c57;margin-top:8px">No sandboxes</p>';
|
||||
return;
|
||||
}
|
||||
let html = '<table><thead><tr><th>ID</th><th>Status</th><th>Template</th><th>vCPUs</th><th>Mem</th><th>Host IP</th><th>Created</th><th>Actions</th></tr></thead><tbody>';
|
||||
let html = '<table><thead><tr><th>ID</th><th>Status</th><th>Template</th><th>vCPUs</th><th>Mem</th><th>TTL</th><th>Host IP</th><th>Created</th><th>Actions</th></tr></thead><tbody>';
|
||||
for (const sb of sandboxes) {
|
||||
html += '<tr>';
|
||||
html += '<td class="clickable" onclick="useSandbox(\'' + sb.id + '\')">' + sb.id + '</td>';
|
||||
@ -425,10 +425,12 @@ function renderSandboxes(sandboxes) {
|
||||
html += '<td>' + esc(sb.template) + '</td>';
|
||||
html += '<td>' + sb.vcpus + '</td>';
|
||||
html += '<td>' + sb.memory_mb + 'MB</td>';
|
||||
html += '<td>' + (sb.timeout_sec ? sb.timeout_sec + 's' : '-') + '</td>';
|
||||
html += '<td>' + (sb.host_ip || '-') + '</td>';
|
||||
html += '<td>' + new Date(sb.created_at).toLocaleTimeString() + '</td>';
|
||||
html += '<td><div class="btn-row">';
|
||||
if (sb.status === 'running') {
|
||||
html += '<button class="btn-blue" onclick="pingSandbox(\'' + sb.id + '\')">Ping</button>';
|
||||
html += '<button class="btn-amber" onclick="pauseSandbox(\'' + sb.id + '\')">Pause</button>';
|
||||
html += '<button class="btn-red" onclick="destroySandbox(\'' + sb.id + '\')">Destroy</button>';
|
||||
} else if (sb.status === 'paused') {
|
||||
@ -497,6 +499,16 @@ async function destroySandbox(id) {
|
||||
}
|
||||
}
|
||||
|
||||
async function pingSandbox(id) {
|
||||
log('Pinging ' + id + '...', 'info');
|
||||
try {
|
||||
await api('POST', '/v1/sandboxes/' + id + '/ping', null, 'apikey');
|
||||
log('Pinged ' + id + ' — inactivity timer reset', 'ok');
|
||||
} catch (e) {
|
||||
log('Ping failed: ' + e.message, 'err');
|
||||
}
|
||||
}
|
||||
|
||||
// --- Exec ---
|
||||
|
||||
async function execCmd() {
|
||||
|
||||
Reference in New Issue
Block a user