feat: add workspace restart functionality to API (#25757)

This models restart as durable orchestration of existing stop and
start workspace builds instead of adding a new restart transition.
Keeping restart as two existing transitions preserves the current
build/provisioner model.

The child start build is created only after the parent stop build
succeeds, rather than being inserted immediately in a pending
state. That keeps `workspace_builds` aligned with actual
provisioner-ready work and avoids introducing a second
pending-build lifecycle that the provisioner and build acquisition
paths would need to understand.

Refs: https://linear.app/codercom/issue/PLAT-143
This commit is contained in:
George K
2026-07-07 09:18:30 -07:00
committed by GitHub
parent ba094c5706
commit 6af0f4d698
45 changed files with 4412 additions and 144 deletions
+76 -1
View File
@@ -258,7 +258,12 @@ CREATE TYPE api_key_scope AS ENUM (
'ai_gateway_key:create',
'ai_gateway_key:delete',
'ai_gateway_key:read',
'ai_gateway_key:update'
'ai_gateway_key:update',
'workspace_build_orchestration:*',
'workspace_build_orchestration:create',
'workspace_build_orchestration:delete',
'workspace_build_orchestration:read',
'workspace_build_orchestration:update'
);
CREATE TYPE app_sharing_level AS ENUM (
@@ -3868,6 +3873,44 @@ CREATE TABLE workspace_app_statuses (
uri text
);
CREATE TABLE workspace_build_orchestrations (
id uuid NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
workspace_id uuid NOT NULL,
parent_build_id uuid NOT NULL,
child_build_id uuid,
child_transition workspace_transition NOT NULL,
child_template_version_id uuid,
child_template_version_preset_id uuid,
child_rich_parameter_values jsonb DEFAULT '[]'::jsonb NOT NULL,
child_log_level text DEFAULT ''::text NOT NULL,
child_reason build_reason,
attempt_count integer DEFAULT 0 NOT NULL,
next_retry_after timestamp with time zone,
status text DEFAULT 'pending'::text NOT NULL,
error text,
CONSTRAINT workspace_build_orchestrations_attempt_count_check CHECK ((attempt_count >= 0)),
CONSTRAINT workspace_build_orchestrations_child_log_level_check CHECK ((child_log_level = ANY (ARRAY[''::text, 'debug'::text]))),
CONSTRAINT workspace_build_orchestrations_child_parameters_check CHECK ((jsonb_typeof(child_rich_parameter_values) = 'array'::text)),
CONSTRAINT workspace_build_orchestrations_child_preset_version_check CHECK (((child_template_version_preset_id IS NULL) OR (child_template_version_id IS NOT NULL))),
CONSTRAINT workspace_build_orchestrations_completed_child_check CHECK (((status <> 'completed'::text) OR (child_build_id IS NOT NULL))),
CONSTRAINT workspace_build_orchestrations_next_retry_after_check CHECK (((status = 'pending'::text) OR (next_retry_after IS NULL))),
CONSTRAINT workspace_build_orchestrations_status_check CHECK ((status = ANY (ARRAY['pending'::text, 'completed'::text, 'failed'::text, 'canceled'::text])))
);
COMMENT ON TABLE workspace_build_orchestrations IS 'Tracks durable follow-up workspace build operations, such as server-side restart, where one child build is created after a parent build completes successfully.';
COMMENT ON COLUMN workspace_build_orchestrations.workspace_id IS 'Copied from the parent build so the database can enforce that parent and child builds belong to the same workspace.';
COMMENT ON COLUMN workspace_build_orchestrations.parent_build_id IS 'Unique because we only support sequences with one child build per parent build.';
COMMENT ON COLUMN workspace_build_orchestrations.child_build_id IS 'Nullable because the child build is created only after the parent build completes successfully.';
COMMENT ON COLUMN workspace_build_orchestrations.attempt_count IS 'Counts retryable child build creation failures for this orchestration row.';
COMMENT ON COLUMN workspace_build_orchestrations.next_retry_after IS 'When set, the orchestrator skips this pending row until the timestamp has passed.';
CREATE TABLE workspace_build_parameters (
workspace_build_id uuid NOT NULL,
name text NOT NULL,
@@ -4382,6 +4425,9 @@ ALTER TABLE ONLY template_version_preset_parameters
ALTER TABLE ONLY template_version_preset_prebuild_schedules
ADD CONSTRAINT template_version_preset_prebuild_schedules_pkey PRIMARY KEY (id);
ALTER TABLE ONLY template_version_presets
ADD CONSTRAINT template_version_presets_id_template_version_id_key UNIQUE (id, template_version_id);
ALTER TABLE ONLY template_version_presets
ADD CONSTRAINT template_version_presets_pkey PRIMARY KEY (id);
@@ -4499,9 +4545,21 @@ ALTER TABLE ONLY workspace_apps
ALTER TABLE ONLY workspace_apps
ADD CONSTRAINT workspace_apps_pkey PRIMARY KEY (id);
ALTER TABLE ONLY workspace_build_orchestrations
ADD CONSTRAINT workspace_build_orchestrations_child_build_id_key UNIQUE (child_build_id);
ALTER TABLE ONLY workspace_build_orchestrations
ADD CONSTRAINT workspace_build_orchestrations_parent_build_id_key UNIQUE (parent_build_id);
ALTER TABLE ONLY workspace_build_orchestrations
ADD CONSTRAINT workspace_build_orchestrations_pkey PRIMARY KEY (id);
ALTER TABLE ONLY workspace_build_parameters
ADD CONSTRAINT workspace_build_parameters_workspace_build_id_name_key UNIQUE (workspace_build_id, name);
ALTER TABLE ONLY workspace_builds
ADD CONSTRAINT workspace_builds_id_workspace_id_key UNIQUE (id, workspace_id);
ALTER TABLE ONLY workspace_builds
ADD CONSTRAINT workspace_builds_job_id_key UNIQUE (job_id);
@@ -4747,6 +4805,8 @@ CREATE UNIQUE INDEX idx_users_username ON users USING btree (username) WHERE (de
CREATE INDEX idx_workspace_app_statuses_workspace_id_created_at ON workspace_app_statuses USING btree (workspace_id, created_at DESC);
CREATE INDEX idx_workspace_build_orchestrations_pending ON workspace_build_orchestrations USING btree (created_at) WHERE (status = 'pending'::text);
CREATE INDEX idx_workspace_builds_initiator_id ON workspace_builds USING btree (initiator_id);
CREATE UNIQUE INDEX notification_messages_dedupe_hash_idx ON notification_messages USING btree (dedupe_hash);
@@ -5376,6 +5436,21 @@ ALTER TABLE ONLY workspace_app_statuses
ALTER TABLE ONLY workspace_apps
ADD CONSTRAINT workspace_apps_agent_id_fkey FOREIGN KEY (agent_id) REFERENCES workspace_agents(id) ON DELETE CASCADE;
ALTER TABLE ONLY workspace_build_orchestrations
ADD CONSTRAINT workspace_build_orchestrations_child_build_workspace_id_fkey FOREIGN KEY (child_build_id, workspace_id) REFERENCES workspace_builds(id, workspace_id) ON DELETE CASCADE;
ALTER TABLE ONLY workspace_build_orchestrations
ADD CONSTRAINT workspace_build_orchestrations_child_preset_id_fkey FOREIGN KEY (child_template_version_preset_id) REFERENCES template_version_presets(id) ON DELETE SET NULL;
ALTER TABLE ONLY workspace_build_orchestrations
ADD CONSTRAINT workspace_build_orchestrations_child_preset_version_fkey FOREIGN KEY (child_template_version_preset_id, child_template_version_id) REFERENCES template_version_presets(id, template_version_id);
ALTER TABLE ONLY workspace_build_orchestrations
ADD CONSTRAINT workspace_build_orchestrations_child_template_version_id_fkey FOREIGN KEY (child_template_version_id) REFERENCES template_versions(id) ON DELETE CASCADE;
ALTER TABLE ONLY workspace_build_orchestrations
ADD CONSTRAINT workspace_build_orchestrations_parent_build_workspace_id_fkey FOREIGN KEY (parent_build_id, workspace_id) REFERENCES workspace_builds(id, workspace_id) ON DELETE CASCADE;
ALTER TABLE ONLY workspace_build_parameters
ADD CONSTRAINT workspace_build_parameters_workspace_build_id_fkey FOREIGN KEY (workspace_build_id) REFERENCES workspace_builds(id) ON DELETE CASCADE;