feat: notify users before workspace autostop (#26676)

This commit is contained in:
Jon Ayers
2026-06-26 01:25:23 -05:00
committed by GitHub
parent a163d43981
commit 637a801a41
11 changed files with 693 additions and 36 deletions
+33 -1
View File
@@ -739,7 +739,11 @@ SELECT
stopped_workspaces.count AS stopped_workspaces
FROM pending_workspaces, building_workspaces, running_workspaces, failed_workspaces, stopped_workspaces;
-- name: GetWorkspacesEligibleForTransition :many
-- name: GetWorkspacesEligibleForLifecycleAction :many
-- Returns workspaces the lifecycle executor must act on this tick. An
-- "action" is a state transition (autostart/autostop/dormancy/delete), a
-- dormancy mark (which has no build transition), or a one-time autostop
-- reminder notification (which only stamps a marker, no transition).
SELECT
workspaces.id,
workspaces.name,
@@ -862,6 +866,34 @@ WHERE
provisioner_jobs.job_status = 'failed'::provisioner_job_status AND
provisioner_jobs.completed_at IS NOT NULL AND
(@now :: timestamptz) - provisioner_jobs.completed_at > (INTERVAL '1 millisecond' * (templates.failure_ttl / 1000000))
) OR
-- A workspace may be eligible for an autostop reminder if the following are true:
-- * The latest build is a successfully provisioned start build.
-- * The workspace is not dormant and its owner is not suspended.
-- * The build has a deadline in the future (we never remind about a stop already due).
-- * The template opts in (time_til_autostop_notify > 0) and now is within the lead window.
-- * A reminder has not yet been sent for THIS deadline.
--
-- NOTE: time_til_autostop_notify has no upper bound. If it exceeds a
-- workspace's remaining lifetime, the notify window already includes "now"
-- at build creation. This arm intentionally still only matches builds whose
-- deadline is in the future (deadline > now) and whose marker has not yet
-- been stamped (notified_autostop_deadline != deadline), so at most ONE
-- reminder is ever produced for a given deadline regardless of how large the
-- field is. The field is stored in nanoseconds, so convert to an interval
-- the same way the dormancy arm does: nanoseconds / 1000000 yields
-- milliseconds.
(
provisioner_jobs.job_status = 'succeeded'::provisioner_job_status AND
workspace_builds.transition = 'start'::workspace_transition AND
workspaces.dormant_at IS NULL AND
users.status != 'suspended'::user_status AND
workspace_builds.deadline != '0001-01-01 00:00:00+00'::timestamptz AND
workspace_builds.deadline > @now::timestamptz AND
templates.time_til_autostop_notify > 0 AND
workspace_builds.deadline <= (@now::timestamptz) + (INTERVAL '1 millisecond' * (templates.time_til_autostop_notify / 1000000)) AND
workspace_builds.notified_autostop_deadline != workspace_builds.deadline
)
)
AND workspaces.deleted = 'false'