forked from wrenn/wrenn
Replace synchronous RPC-based CP-host communication for sandbox lifecycle operations (Create, Pause, Resume, Destroy) with an async pattern. CP handlers now return 202 Accepted immediately, fire agent RPCs in background goroutines, and publish state events to a Redis Stream. A background consumer processes events as a fallback writer. Agent-side auto-pause events are pushed to the CP via HTTP callback (POST /v1/hosts/sandbox-events), keeping Redis internal to the CP. All DB status transitions use conditional updates (UpdateSandboxStatusIf, UpdateSandboxRunningIf) to prevent race conditions between concurrent operations and background goroutines. The HostMonitor reconciler is kept at 60s as a safety net, extended to handle transient statuses (starting, pausing, resuming, stopping). Frontend updated to handle 202 responses with empty bodies and render transient statuses with blue indicators.
114 lines
3.4 KiB
SQL
114 lines
3.4 KiB
SQL
-- name: InsertSandbox :one
|
|
INSERT INTO sandboxes (id, team_id, host_id, template, status, vcpus, memory_mb, timeout_sec, disk_size_mb, template_id, template_team_id, metadata)
|
|
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
|
|
RETURNING *;
|
|
|
|
-- name: GetSandbox :one
|
|
SELECT * FROM sandboxes WHERE id = $1;
|
|
|
|
-- name: GetSandboxByTeam :one
|
|
SELECT * FROM sandboxes WHERE id = $1 AND team_id = $2;
|
|
|
|
-- name: GetSandboxProxyTarget :one
|
|
-- Returns the sandbox status and its host's address in one query.
|
|
-- Used by SandboxProxyWrapper to avoid two round-trips.
|
|
SELECT s.status, h.address AS host_address
|
|
FROM sandboxes s
|
|
JOIN hosts h ON h.id = s.host_id
|
|
WHERE s.id = $1;
|
|
|
|
-- name: ListSandboxes :many
|
|
SELECT * FROM sandboxes ORDER BY created_at DESC;
|
|
|
|
-- name: ListSandboxesByTeam :many
|
|
SELECT * FROM sandboxes
|
|
WHERE team_id = $1 AND status NOT IN ('stopped', 'error')
|
|
ORDER BY created_at DESC;
|
|
|
|
-- name: ListSandboxesByHostAndStatus :many
|
|
SELECT * FROM sandboxes
|
|
WHERE host_id = $1 AND status = ANY($2::text[])
|
|
ORDER BY created_at DESC;
|
|
|
|
-- name: UpdateSandboxRunning :one
|
|
UPDATE sandboxes
|
|
SET status = 'running',
|
|
host_ip = $2,
|
|
guest_ip = $3,
|
|
started_at = $4,
|
|
last_active_at = $4,
|
|
last_updated = NOW()
|
|
WHERE id = $1
|
|
RETURNING *;
|
|
|
|
-- name: UpdateSandboxStatus :one
|
|
UPDATE sandboxes
|
|
SET status = $2,
|
|
last_updated = NOW()
|
|
WHERE id = $1
|
|
RETURNING *;
|
|
|
|
-- name: UpdateLastActive :exec
|
|
UPDATE sandboxes
|
|
SET last_active_at = $2,
|
|
last_updated = NOW()
|
|
WHERE id = $1;
|
|
|
|
-- name: BulkUpdateStatusByIDs :exec
|
|
UPDATE sandboxes
|
|
SET status = $2,
|
|
last_updated = NOW()
|
|
WHERE id = ANY($1::uuid[]);
|
|
|
|
-- name: ListActiveSandboxesByTeam :many
|
|
SELECT * FROM sandboxes
|
|
WHERE team_id = $1 AND status IN ('running', 'paused', 'starting', 'hibernated')
|
|
ORDER BY created_at DESC;
|
|
|
|
-- name: MarkSandboxesMissingByHost :exec
|
|
-- Called when the host monitor marks a host unreachable.
|
|
-- Marks running/starting/pending sandboxes on that host as 'missing' so users see
|
|
-- the sandbox is not currently reachable, without permanently losing the record.
|
|
UPDATE sandboxes
|
|
SET status = 'missing',
|
|
last_updated = NOW()
|
|
WHERE host_id = $1 AND status IN ('running', 'starting', 'pending', 'pausing', 'resuming', 'stopping');
|
|
|
|
-- name: UpdateSandboxMetadata :exec
|
|
UPDATE sandboxes
|
|
SET metadata = $2,
|
|
last_updated = NOW()
|
|
WHERE id = $1;
|
|
|
|
-- name: UpdateSandboxRunningIf :one
|
|
-- Conditionally transition a sandbox to running only if the current status
|
|
-- matches the expected value. Prevents races where a user destroys a sandbox
|
|
-- while the create/resume goroutine is still in-flight.
|
|
UPDATE sandboxes
|
|
SET status = 'running',
|
|
host_ip = $3,
|
|
guest_ip = $4,
|
|
started_at = $5,
|
|
last_active_at = $5,
|
|
last_updated = NOW()
|
|
WHERE id = $1 AND status = $2
|
|
RETURNING *;
|
|
|
|
-- name: UpdateSandboxStatusIf :one
|
|
-- Atomically update status only when the current status matches the expected value.
|
|
-- Prevents background goroutines from overwriting a status that has since changed
|
|
-- (e.g. user destroyed a sandbox while Create was in-flight).
|
|
UPDATE sandboxes
|
|
SET status = $3,
|
|
last_updated = NOW()
|
|
WHERE id = $1 AND status = $2
|
|
RETURNING *;
|
|
|
|
-- name: BulkRestoreRunning :exec
|
|
-- Called by the reconciler when a host comes back online and its sandboxes are
|
|
-- confirmed alive. Restores only sandboxes that are in 'missing' state.
|
|
UPDATE sandboxes
|
|
SET status = 'running',
|
|
last_updated = NOW()
|
|
WHERE id = ANY($1::uuid[]) AND status = 'missing';
|