Skip to content

Commit 46e1896

Browse files
committed
feat: add activity bumping to template scheduling
1 parent 40f3fc3 commit 46e1896

File tree

20 files changed

+521
-87
lines changed

20 files changed

+521
-87
lines changed

coderd/database/dbauthz/dbauthz.go

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

2355+
func (q *querier) UpdateTemplateWorkspacesLastUsedAt(ctx context.Context, arg database.UpdateTemplateWorkspacesLastUsedAtParams) error {
2356+
fetch := func(ctx context.Context, arg database.UpdateTemplateWorkspacesLastUsedAtParams) (database.Template, error) {
2357+
return q.db.GetTemplateByID(ctx, arg.TemplateID)
2358+
}
2359+
2360+
return fetchAndExec(q.log, q.auth, rbac.ActionUpdate, fetch, q.db.UpdateTemplateWorkspacesLastUsedAt)(ctx, arg)
2361+
}
2362+
23552363
// UpdateUserDeletedByID
23562364
// Deprecated: Delete this function in favor of 'SoftDeleteUserByID'. Deletes are
23572365
// irreversible.
@@ -2630,12 +2638,12 @@ func (q *querier) UpdateWorkspaceTTL(ctx context.Context, arg database.UpdateWor
26302638
return update(q.log, q.auth, fetch, q.db.UpdateWorkspaceTTL)(ctx, arg)
26312639
}
26322640

2633-
func (q *querier) UpdateWorkspacesDeletingAtByTemplateID(ctx context.Context, arg database.UpdateWorkspacesDeletingAtByTemplateIDParams) error {
2634-
fetch := func(ctx context.Context, arg database.UpdateWorkspacesDeletingAtByTemplateIDParams) (database.Template, error) {
2641+
func (q *querier) UpdateWorkspacesLockedDeletingAtByTemplateID(ctx context.Context, arg database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error {
2642+
fetch := func(ctx context.Context, arg database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) (database.Template, error) {
26352643
return q.db.GetTemplateByID(ctx, arg.TemplateID)
26362644
}
26372645

2638-
return fetchAndExec(q.log, q.auth, rbac.ActionUpdate, fetch, q.db.UpdateWorkspacesDeletingAtByTemplateID)(ctx, arg)
2646+
return fetchAndExec(q.log, q.auth, rbac.ActionUpdate, fetch, q.db.UpdateWorkspacesLockedDeletingAtByTemplateID)(ctx, arg)
26392647
}
26402648

26412649
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
@@ -4866,6 +4866,26 @@ func (q *FakeQuerier) UpdateTemplateVersionGitAuthProvidersByJobID(_ context.Con
48664866
return sql.ErrNoRows
48674867
}
48684868

4869+
func (q *FakeQuerier) UpdateTemplateWorkspacesLastUsedAt(_ context.Context, arg database.UpdateTemplateWorkspacesLastUsedAtParams) error {
4870+
err := validateDatabaseType(arg)
4871+
if err != nil {
4872+
return err
4873+
}
4874+
4875+
q.mutex.Lock()
4876+
defer q.mutex.Unlock()
4877+
4878+
for i, ws := range q.workspaces {
4879+
if ws.TemplateID != arg.TemplateID {
4880+
continue
4881+
}
4882+
ws.LastUsedAt = arg.LastUsedAt
4883+
q.workspaces[i] = ws
4884+
}
4885+
4886+
return nil
4887+
}
4888+
48694889
func (q *FakeQuerier) UpdateUserDeletedByID(_ context.Context, params database.UpdateUserDeletedByIDParams) error {
48704890
if err := validateDatabaseType(params); err != nil {
48714891
return err
@@ -5463,7 +5483,7 @@ func (q *FakeQuerier) UpdateWorkspaceTTL(_ context.Context, arg database.UpdateW
54635483
return sql.ErrNoRows
54645484
}
54655485

5466-
func (q *FakeQuerier) UpdateWorkspacesDeletingAtByTemplateID(_ context.Context, arg database.UpdateWorkspacesDeletingAtByTemplateIDParams) error {
5486+
func (q *FakeQuerier) UpdateWorkspacesLockedDeletingAtByTemplateID(_ context.Context, arg database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error {
54675487
q.mutex.Lock()
54685488
defer q.mutex.Unlock()
54695489

@@ -5473,9 +5493,21 @@ func (q *FakeQuerier) UpdateWorkspacesDeletingAtByTemplateID(_ context.Context,
54735493
}
54745494

54755495
for i, ws := range q.workspaces {
5496+
if ws.TemplateID != arg.TemplateID {
5497+
continue
5498+
}
5499+
54765500
if ws.LockedAt.Time.IsZero() {
54775501
continue
54785502
}
5503+
5504+
if !arg.LockedAt.IsZero() {
5505+
ws.LockedAt = sql.NullTime{
5506+
Valid: true,
5507+
Time: arg.LockedAt,
5508+
}
5509+
}
5510+
54795511
deletingAt := sql.NullTime{
54805512
Valid: arg.LockedTtlMs > 0,
54815513
}

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
@@ -501,12 +501,23 @@ AND
501501
workspaces.id = $1
502502
RETURNING workspaces.*;
503503

504-
-- name: UpdateWorkspacesDeletingAtByTemplateID :exec
505-
UPDATE
506-
workspaces
504+
-- name: UpdateWorkspacesLockedDeletingAtByTemplateID :exec
505+
UPDATE workspaces
507506
SET
508-
deleting_at = CASE WHEN @locked_ttl_ms::bigint = 0 THEN NULL ELSE locked_at + interval '1 milliseconds' * @locked_ttl_ms::bigint END
507+
deleting_at = CASE
508+
WHEN @locked_ttl_ms::bigint = 0 THEN NULL
509+
WHEN @locked_at::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN (@locked_at::timestamptz) + interval '1 milliseconds' * @locked_ttl_ms::bigint
510+
ELSE locked_at + interval '1 milliseconds' * @locked_ttl_ms::bigint
511+
END,
512+
locked_at = CASE WHEN @locked_at::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN @locked_at::timestamptz ELSE locked_at END
509513
WHERE
510-
template_id = @template_id
514+
template_id = @template_id
511515
AND
512-
locked_at IS NOT NULL;
516+
locked_at IS NOT NULL;
517+
518+
-- name: UpdateTemplateWorkspacesLastUsedAt :exec
519+
UPDATE workspaces
520+
SET
521+
last_used_at = @last_used_at::timestamptz
522+
WHERE
523+
template_id = @template_id;

coderd/schedule/template.go

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

109121
// 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)