mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
feat: notify users before workspace autostop (#26676)
This commit is contained in:
Generated
+63
-6
@@ -36200,6 +36200,31 @@ func (q *sqlQuerier) UpdateWorkspaceBuildFlagsByID(ctx context.Context, arg Upda
|
||||
return err
|
||||
}
|
||||
|
||||
const updateWorkspaceBuildNotifiedAutostopDeadline = `-- name: UpdateWorkspaceBuildNotifiedAutostopDeadline :exec
|
||||
UPDATE
|
||||
workspace_builds
|
||||
SET
|
||||
notified_autostop_deadline = $1::timestamptz,
|
||||
updated_at = $2::timestamptz
|
||||
WHERE id = $3::uuid
|
||||
`
|
||||
|
||||
type UpdateWorkspaceBuildNotifiedAutostopDeadlineParams struct {
|
||||
NotifiedAutostopDeadline time.Time `db:"notified_autostop_deadline" json:"notified_autostop_deadline"`
|
||||
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
}
|
||||
|
||||
// Stamps the deadline value that an autostop reminder was last sent for. Once
|
||||
// this equals the build's deadline the reminder is considered handled and the
|
||||
// lifecycle executor will not send another for this deadline, which makes the
|
||||
// reminder idempotent and HA-safe. It re-arms automatically when the deadline
|
||||
// changes (e.g. an activity bump).
|
||||
func (q *sqlQuerier) UpdateWorkspaceBuildNotifiedAutostopDeadline(ctx context.Context, arg UpdateWorkspaceBuildNotifiedAutostopDeadlineParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateWorkspaceBuildNotifiedAutostopDeadline, arg.NotifiedAutostopDeadline, arg.UpdatedAt, arg.ID)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateWorkspaceBuildProvisionerStateByID = `-- name: UpdateWorkspaceBuildProvisionerStateByID :exec
|
||||
UPDATE
|
||||
workspace_builds
|
||||
@@ -38083,7 +38108,7 @@ func (q *sqlQuerier) GetWorkspacesByTemplateID(ctx context.Context, templateID u
|
||||
return items, nil
|
||||
}
|
||||
|
||||
const getWorkspacesEligibleForTransition = `-- name: GetWorkspacesEligibleForTransition :many
|
||||
const getWorkspacesEligibleForLifecycleAction = `-- name: GetWorkspacesEligibleForLifecycleAction :many
|
||||
SELECT
|
||||
workspaces.id,
|
||||
workspaces.name,
|
||||
@@ -38206,6 +38231,34 @@ WHERE
|
||||
provisioner_jobs.job_status = 'failed'::provisioner_job_status AND
|
||||
provisioner_jobs.completed_at IS NOT NULL AND
|
||||
($1 :: 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 > $1::timestamptz AND
|
||||
templates.time_til_autostop_notify > 0 AND
|
||||
workspace_builds.deadline <= ($1::timestamptz) + (INTERVAL '1 millisecond' * (templates.time_til_autostop_notify / 1000000)) AND
|
||||
workspace_builds.notified_autostop_deadline != workspace_builds.deadline
|
||||
)
|
||||
)
|
||||
AND workspaces.deleted = 'false'
|
||||
@@ -38215,21 +38268,25 @@ WHERE
|
||||
AND workspaces.owner_id != 'c42fdf75-3097-471c-8c33-fb52454d81c0'::UUID
|
||||
`
|
||||
|
||||
type GetWorkspacesEligibleForTransitionRow struct {
|
||||
type GetWorkspacesEligibleForLifecycleActionRow struct {
|
||||
ID uuid.UUID `db:"id" json:"id"`
|
||||
Name string `db:"name" json:"name"`
|
||||
BuildTemplateVersionID uuid.NullUUID `db:"build_template_version_id" json:"build_template_version_id"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) GetWorkspacesEligibleForTransition(ctx context.Context, now time.Time) ([]GetWorkspacesEligibleForTransitionRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getWorkspacesEligibleForTransition, now)
|
||||
// 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).
|
||||
func (q *sqlQuerier) GetWorkspacesEligibleForLifecycleAction(ctx context.Context, now time.Time) ([]GetWorkspacesEligibleForLifecycleActionRow, error) {
|
||||
rows, err := q.db.QueryContext(ctx, getWorkspacesEligibleForLifecycleAction, now)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer rows.Close()
|
||||
var items []GetWorkspacesEligibleForTransitionRow
|
||||
var items []GetWorkspacesEligibleForLifecycleActionRow
|
||||
for rows.Next() {
|
||||
var i GetWorkspacesEligibleForTransitionRow
|
||||
var i GetWorkspacesEligibleForLifecycleActionRow
|
||||
if err := rows.Scan(&i.ID, &i.Name, &i.BuildTemplateVersionID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user