Skip to content

Commit 9a26f93

Browse files
committed
feat: add deleting_at column to workspaces
1 parent 0c73164 commit 9a26f93

22 files changed

+467
-161
lines changed

coderd/autobuild/lifecycle_executor.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ func (e *Executor) runOnce(t time.Time) Stats {
178178
// Lock the workspace if it has breached the template's
179179
// threshold for inactivity.
180180
if reason == database.BuildReasonAutolock {
181-
err = tx.UpdateWorkspaceLockedAt(e.ctx, database.UpdateWorkspaceLockedAtParams{
181+
err = tx.UpdateWorkspaceLockedDeletingAt(e.ctx, database.UpdateWorkspaceLockedDeletingAtParams{
182182
ID: ws.ID,
183183
LockedAt: sql.NullTime{
184184
Time: database.Now(),
@@ -347,11 +347,11 @@ func isEligibleForLockedStop(ws database.Workspace, templateSchedule schedule.Te
347347

348348
func isEligibleForDelete(ws database.Workspace, templateSchedule schedule.TemplateScheduleOptions, currentTick time.Time) bool {
349349
// Only attempt to delete locked workspaces.
350-
return ws.LockedAt.Valid &&
350+
return ws.LockedAt.Valid && ws.DeletingAt.Valid &&
351351
// Locked workspaces should only be deleted if a locked_ttl is specified.
352352
templateSchedule.LockedTTL > 0 &&
353353
// The workspace must breach the locked_ttl.
354-
currentTick.Sub(ws.LockedAt.Time) > templateSchedule.LockedTTL
354+
currentTick.After(ws.DeletingAt.Time)
355355
}
356356

357357
// isEligibleForFailedStop returns true if the workspace is eligible to be stopped

coderd/database/dbauthz/dbauthz.go

+11-3
Original file line numberDiff line numberDiff line change
@@ -2488,11 +2488,11 @@ func (q *querier) UpdateWorkspaceLastUsedAt(ctx context.Context, arg database.Up
24882488
return update(q.log, q.auth, fetch, q.db.UpdateWorkspaceLastUsedAt)(ctx, arg)
24892489
}
24902490

2491-
func (q *querier) UpdateWorkspaceLockedAt(ctx context.Context, arg database.UpdateWorkspaceLockedAtParams) error {
2492-
fetch := func(ctx context.Context, arg database.UpdateWorkspaceLockedAtParams) (database.Workspace, error) {
2491+
func (q *querier) UpdateWorkspaceLockedDeletingAt(ctx context.Context, arg database.UpdateWorkspaceLockedDeletingAtParams) error {
2492+
fetch := func(ctx context.Context, arg database.UpdateWorkspaceLockedDeletingAtParams) (database.Workspace, error) {
24932493
return q.db.GetWorkspaceByID(ctx, arg.ID)
24942494
}
2495-
return update(q.log, q.auth, fetch, q.db.UpdateWorkspaceLockedAt)(ctx, arg)
2495+
return update(q.log, q.auth, fetch, q.db.UpdateWorkspaceLockedDeletingAt)(ctx, arg)
24962496
}
24972497

24982498
func (q *querier) UpdateWorkspaceProxy(ctx context.Context, arg database.UpdateWorkspaceProxyParams) (database.WorkspaceProxy, error) {
@@ -2516,6 +2516,14 @@ func (q *querier) UpdateWorkspaceTTL(ctx context.Context, arg database.UpdateWor
25162516
return update(q.log, q.auth, fetch, q.db.UpdateWorkspaceTTL)(ctx, arg)
25172517
}
25182518

2519+
func (q *querier) UpdateWorkspacesDeletingAtByTemplateID(ctx context.Context, arg database.UpdateWorkspacesDeletingAtByTemplateIDParams) error {
2520+
fetch := func(ctx context.Context, arg database.UpdateWorkspacesDeletingAtByTemplateIDParams) (database.Template, error) {
2521+
return q.db.GetTemplateByID(ctx, arg.TemplateID)
2522+
}
2523+
2524+
return fetchAndExec(q.log, q.auth, rbac.ActionUpdate, fetch, q.db.UpdateWorkspacesDeletingAtByTemplateID)(ctx, arg)
2525+
}
2526+
25192527
func (q *querier) UpsertAppSecurityKey(ctx context.Context, data string) error {
25202528
// No authz checks as this is done during startup
25212529
return q.db.UpsertAppSecurityKey(ctx, data)

coderd/database/dbfake/dbfake.go

+51-6
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,8 @@ func (q *FakeQuerier) convertToWorkspaceRowsNoLock(ctx context.Context, workspac
339339
AutostartSchedule: w.AutostartSchedule,
340340
Ttl: w.Ttl,
341341
LastUsedAt: w.LastUsedAt,
342+
LockedAt: w.LockedAt,
343+
DeletingAt: w.DeletingAt,
342344
Count: count,
343345
}
344346

@@ -4851,24 +4853,42 @@ func (q *FakeQuerier) UpdateWorkspaceLastUsedAt(_ context.Context, arg database.
48514853
return sql.ErrNoRows
48524854
}
48534855

4854-
func (q *FakeQuerier) UpdateWorkspaceLockedAt(_ context.Context, arg database.UpdateWorkspaceLockedAtParams) error {
4856+
func (q *FakeQuerier) UpdateWorkspaceLockedDeletingAt(_ context.Context, arg database.UpdateWorkspaceLockedDeletingAtParams) error {
48554857
if err := validateDatabaseType(arg); err != nil {
48564858
return err
48574859
}
4858-
48594860
q.mutex.Lock()
48604861
defer q.mutex.Unlock()
4861-
48624862
for index, workspace := range q.workspaces {
48634863
if workspace.ID != arg.ID {
48644864
continue
48654865
}
48664866
workspace.LockedAt = arg.LockedAt
4867-
workspace.LastUsedAt = database.Now()
4867+
if workspace.LockedAt.Time.IsZero() {
4868+
workspace.LastUsedAt = database.Now()
4869+
workspace.DeletingAt = sql.NullTime{}
4870+
}
4871+
if !workspace.LockedAt.Time.IsZero() {
4872+
var template database.TemplateTable
4873+
for _, t := range q.templates {
4874+
if t.ID == workspace.TemplateID {
4875+
template = t
4876+
break
4877+
}
4878+
}
4879+
if template.ID == uuid.Nil {
4880+
return xerrors.Errorf("unable to find workspace template")
4881+
}
4882+
if template.LockedTTL > 0 {
4883+
workspace.DeletingAt = sql.NullTime{
4884+
Valid: true,
4885+
Time: workspace.LockedAt.Time.Add(time.Duration(template.LockedTTL)),
4886+
}
4887+
}
4888+
}
48684889
q.workspaces[index] = workspace
48694890
return nil
48704891
}
4871-
48724892
return sql.ErrNoRows
48734893
}
48744894

@@ -4932,6 +4952,32 @@ func (q *FakeQuerier) UpdateWorkspaceTTL(_ context.Context, arg database.UpdateW
49324952
return sql.ErrNoRows
49334953
}
49344954

4955+
func (q *FakeQuerier) UpdateWorkspacesDeletingAtByTemplateID(_ context.Context, arg database.UpdateWorkspacesDeletingAtByTemplateIDParams) error {
4956+
q.mutex.Lock()
4957+
defer q.mutex.Unlock()
4958+
4959+
err := validateDatabaseType(arg)
4960+
if err != nil {
4961+
return err
4962+
}
4963+
4964+
for i, ws := range q.workspaces {
4965+
if ws.LockedAt.Time.IsZero() {
4966+
continue
4967+
}
4968+
deletingAt := sql.NullTime{
4969+
Valid: arg.LockedTtlMs > 0,
4970+
}
4971+
if arg.LockedTtlMs > 0 {
4972+
deletingAt.Time = ws.LockedAt.Time.Add(time.Duration(arg.LockedTtlMs) * time.Millisecond)
4973+
}
4974+
ws.DeletingAt = deletingAt
4975+
q.workspaces[i] = ws
4976+
}
4977+
4978+
return nil
4979+
}
4980+
49354981
func (q *FakeQuerier) UpsertAppSecurityKey(_ context.Context, data string) error {
49364982
q.mutex.Lock()
49374983
defer q.mutex.Unlock()
@@ -4951,7 +4997,6 @@ func (q *FakeQuerier) UpsertLastUpdateCheck(_ context.Context, data string) erro
49514997
defer q.mutex.Unlock()
49524998

49534999
q.lastUpdateCheck = []byte(data)
4954-
return nil
49555000
}
49565001

49575002
func (q *FakeQuerier) UpsertLogoURL(_ context.Context, data string) error {

coderd/database/dbmetrics/dbmetrics.go

+10-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dbmock/dbmock.go

+20-6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/dump.sql

+2-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE workspaces DROP COLUMN deleting_at;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
ALTER TABLE workspaces ADD COLUMN deleting_at timestamptz NULL;

coderd/database/modelmethods.go

+2
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,8 @@ func ConvertWorkspaceRows(rows []GetWorkspacesRow) []Workspace {
354354
AutostartSchedule: r.AutostartSchedule,
355355
Ttl: r.Ttl,
356356
LastUsedAt: r.LastUsedAt,
357+
LockedAt: r.LockedAt,
358+
DeletingAt: r.DeletingAt,
357359
}
358360
}
359361

coderd/database/modelqueries.go

+1
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ func (q *sqlQuerier) GetAuthorizedWorkspaces(ctx context.Context, arg GetWorkspa
240240
&i.Ttl,
241241
&i.LastUsedAt,
242242
&i.LockedAt,
243+
&i.DeletingAt,
243244
&i.TemplateName,
244245
&i.TemplateVersionID,
245246
&i.TemplateVersionName,

coderd/database/models.go

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/querier.go

+2-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)