Skip to content

Commit 6e41cd1

Browse files
authored
feat: add activity bumping to template scheduling (#9040)
1 parent 6214117 commit 6e41cd1

File tree

30 files changed

+669
-190
lines changed

30 files changed

+669
-190
lines changed

coderd/database/dbauthz/dbauthz.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2385,6 +2385,14 @@ func (q *querier) UpdateTemplateVersionGitAuthProvidersByJobID(ctx context.Conte
23852385
return q.db.UpdateTemplateVersionGitAuthProvidersByJobID(ctx, arg)
23862386
}
23872387

2388+
func (q *querier) UpdateTemplateWorkspacesLastUsedAt(ctx context.Context, arg database.UpdateTemplateWorkspacesLastUsedAtParams) error {
2389+
fetch := func(ctx context.Context, arg database.UpdateTemplateWorkspacesLastUsedAtParams) (database.Template, error) {
2390+
return q.db.GetTemplateByID(ctx, arg.TemplateID)
2391+
}
2392+
2393+
return fetchAndExec(q.log, q.auth, rbac.ActionUpdate, fetch, q.db.UpdateTemplateWorkspacesLastUsedAt)(ctx, arg)
2394+
}
2395+
23882396
// UpdateUserDeletedByID
23892397
// Deprecated: Delete this function in favor of 'SoftDeleteUserByID'. Deletes are
23902398
// irreversible.
@@ -2663,12 +2671,12 @@ func (q *querier) UpdateWorkspaceTTL(ctx context.Context, arg database.UpdateWor
26632671
return update(q.log, q.auth, fetch, q.db.UpdateWorkspaceTTL)(ctx, arg)
26642672
}
26652673

2666-
func (q *querier) UpdateWorkspacesDeletingAtByTemplateID(ctx context.Context, arg database.UpdateWorkspacesDeletingAtByTemplateIDParams) error {
2667-
fetch := func(ctx context.Context, arg database.UpdateWorkspacesDeletingAtByTemplateIDParams) (database.Template, error) {
2674+
func (q *querier) UpdateWorkspacesLockedDeletingAtByTemplateID(ctx context.Context, arg database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error {
2675+
fetch := func(ctx context.Context, arg database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) (database.Template, error) {
26682676
return q.db.GetTemplateByID(ctx, arg.TemplateID)
26692677
}
26702678

2671-
return fetchAndExec(q.log, q.auth, rbac.ActionUpdate, fetch, q.db.UpdateWorkspacesDeletingAtByTemplateID)(ctx, arg)
2679+
return fetchAndExec(q.log, q.auth, rbac.ActionUpdate, fetch, q.db.UpdateWorkspacesLockedDeletingAtByTemplateID)(ctx, arg)
26722680
}
26732681

26742682
func (q *querier) UpsertAppSecurityKey(ctx context.Context, data string) error {

coderd/database/dbfake/dbfake.go

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5199,6 +5199,26 @@ func (q *FakeQuerier) UpdateTemplateVersionGitAuthProvidersByJobID(_ context.Con
51995199
return sql.ErrNoRows
52005200
}
52015201

5202+
func (q *FakeQuerier) UpdateTemplateWorkspacesLastUsedAt(_ context.Context, arg database.UpdateTemplateWorkspacesLastUsedAtParams) error {
5203+
err := validateDatabaseType(arg)
5204+
if err != nil {
5205+
return err
5206+
}
5207+
5208+
q.mutex.Lock()
5209+
defer q.mutex.Unlock()
5210+
5211+
for i, ws := range q.workspaces {
5212+
if ws.TemplateID != arg.TemplateID {
5213+
continue
5214+
}
5215+
ws.LastUsedAt = arg.LastUsedAt
5216+
q.workspaces[i] = ws
5217+
}
5218+
5219+
return nil
5220+
}
5221+
52025222
func (q *FakeQuerier) UpdateUserDeletedByID(_ context.Context, params database.UpdateUserDeletedByIDParams) error {
52035223
if err := validateDatabaseType(params); err != nil {
52045224
return err
@@ -5796,7 +5816,7 @@ func (q *FakeQuerier) UpdateWorkspaceTTL(_ context.Context, arg database.UpdateW
57965816
return sql.ErrNoRows
57975817
}
57985818

5799-
func (q *FakeQuerier) UpdateWorkspacesDeletingAtByTemplateID(_ context.Context, arg database.UpdateWorkspacesDeletingAtByTemplateIDParams) error {
5819+
func (q *FakeQuerier) UpdateWorkspacesLockedDeletingAtByTemplateID(_ context.Context, arg database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error {
58005820
q.mutex.Lock()
58015821
defer q.mutex.Unlock()
58025822

@@ -5806,9 +5826,21 @@ func (q *FakeQuerier) UpdateWorkspacesDeletingAtByTemplateID(_ context.Context,
58065826
}
58075827

58085828
for i, ws := range q.workspaces {
5829+
if ws.TemplateID != arg.TemplateID {
5830+
continue
5831+
}
5832+
58095833
if ws.LockedAt.Time.IsZero() {
58105834
continue
58115835
}
5836+
5837+
if !arg.LockedAt.IsZero() {
5838+
ws.LockedAt = sql.NullTime{
5839+
Valid: true,
5840+
Time: arg.LockedAt,
5841+
}
5842+
}
5843+
58125844
deletingAt := sql.NullTime{
58135845
Valid: arg.LockedTtlMs > 0,
58145846
}

coderd/database/dbmetrics/dbmetrics.go

Lines changed: 10 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dbmock/dbmock.go

Lines changed: 20 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/querier.go

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries.sql.go

Lines changed: 32 additions & 9 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/workspaces.sql

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -512,12 +512,23 @@ AND
512512
workspaces.id = $1
513513
RETURNING workspaces.*;
514514

515-
-- name: UpdateWorkspacesDeletingAtByTemplateID :exec
516-
UPDATE
517-
workspaces
515+
-- name: UpdateWorkspacesLockedDeletingAtByTemplateID :exec
516+
UPDATE workspaces
518517
SET
519-
deleting_at = CASE WHEN @locked_ttl_ms::bigint = 0 THEN NULL ELSE locked_at + interval '1 milliseconds' * @locked_ttl_ms::bigint END
518+
deleting_at = CASE
519+
WHEN @locked_ttl_ms::bigint = 0 THEN NULL
520+
WHEN @locked_at::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN (@locked_at::timestamptz) + interval '1 milliseconds' * @locked_ttl_ms::bigint
521+
ELSE locked_at + interval '1 milliseconds' * @locked_ttl_ms::bigint
522+
END,
523+
locked_at = CASE WHEN @locked_at::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN @locked_at::timestamptz ELSE locked_at END
520524
WHERE
521-
template_id = @template_id
525+
template_id = @template_id
522526
AND
523-
locked_at IS NOT NULL;
527+
locked_at IS NOT NULL;
528+
529+
-- name: UpdateTemplateWorkspacesLastUsedAt :exec
530+
UPDATE workspaces
531+
SET
532+
last_used_at = @last_used_at::timestamptz
533+
WHERE
534+
template_id = @template_id;

coderd/schedule/template.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,18 @@ type TemplateScheduleOptions struct {
105105
// LockedTTL dictates the duration after which locked workspaces will be
106106
// permanently deleted.
107107
LockedTTL time.Duration `json:"locked_ttl"`
108+
// UpdateWorkspaceLastUsedAt updates the template's workspaces'
109+
// last_used_at field. This is useful for preventing updates to the
110+
// templates inactivity_ttl immediately triggering a lock action against
111+
// workspaces whose last_used_at field violates the new template
112+
// inactivity_ttl threshold.
113+
UpdateWorkspaceLastUsedAt bool `json:"update_workspace_last_used_at"`
114+
// UpdateWorkspaceLockedAt updates the template's workspaces'
115+
// locked_at field. This is useful for preventing updates to the
116+
// templates locked_ttl immediately triggering a delete action against
117+
// workspaces whose locked_at field violates the new template locked_ttl
118+
// threshold.
119+
UpdateWorkspaceLockedAt bool `json:"update_workspace_locked_at"`
108120
}
109121

110122
// TemplateScheduleStore provides an interface for retrieving template

coderd/templates.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -622,9 +622,11 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
622622
DaysOfWeek: restartRequirementDaysOfWeekParsed,
623623
Weeks: req.RestartRequirement.Weeks,
624624
},
625-
FailureTTL: failureTTL,
626-
InactivityTTL: inactivityTTL,
627-
LockedTTL: lockedTTL,
625+
FailureTTL: failureTTL,
626+
InactivityTTL: inactivityTTL,
627+
LockedTTL: lockedTTL,
628+
UpdateWorkspaceLastUsedAt: req.UpdateWorkspaceLastUsedAt,
629+
UpdateWorkspaceLockedAt: req.UpdateWorkspaceLockedAt,
628630
})
629631
if err != nil {
630632
return xerrors.Errorf("set template schedule options: %w", err)

codersdk/templates.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,15 @@ type UpdateTemplateMeta struct {
192192
FailureTTLMillis int64 `json:"failure_ttl_ms,omitempty"`
193193
InactivityTTLMillis int64 `json:"inactivity_ttl_ms,omitempty"`
194194
LockedTTLMillis int64 `json:"locked_ttl_ms,omitempty"`
195+
// UpdateWorkspaceLastUsedAt updates the last_used_at field of workspaces
196+
// spawned from the template. This is useful for preventing workspaces being
197+
// immediately locked when updating the inactivity_ttl field to a new, shorter
198+
// value.
199+
UpdateWorkspaceLastUsedAt bool `json:"update_workspace_last_used_at"`
200+
// UpdateWorkspaceLockedAt updates the locked_at field of workspaces spawned
201+
// from the template. This is useful for preventing locked workspaces being immediately
202+
// deleted when updating the locked_ttl field to a new, shorter value.
203+
UpdateWorkspaceLockedAt bool `json:"update_workspace_locked_at"`
195204
}
196205

197206
type TemplateExample struct {

0 commit comments

Comments
 (0)