Skip to content

feat: add customizable ttl_bump interval #10566

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion cli/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ type templateTableRow struct {
ActiveVersionID uuid.UUID `json:"-" table:"active version id"`
UsedBy string `json:"-" table:"used by"`
DefaultTTL time.Duration `json:"-" table:"default ttl"`
DefaultTTLBump time.Duration `json:"-" table:"default ttl bump"`
}

// templateToRows converts a list of templates to a list of templateTableRow for
Expand All @@ -111,7 +112,8 @@ func templatesToRows(templates ...codersdk.Template) []templateTableRow {
Provisioner: template.Provisioner,
ActiveVersionID: template.ActiveVersionID,
UsedBy: pretty.Sprint(cliui.DefaultStyles.Fuchsia, formatActiveDevelopers(template.ActiveUserCount)),
DefaultTTL: (time.Duration(template.DefaultTTLMillis) * time.Millisecond),
DefaultTTL: time.Duration(template.DefaultTTLMillis) * time.Millisecond,
DefaultTTLBump: time.Duration(template.DefaultTTLBumpMillis) * time.Millisecond,
}
}

Expand Down
42 changes: 40 additions & 2 deletions coderd/activitybump_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,9 @@ func Test_ActivityBumpWorkspace(t *testing.T) {
buildDeadlineOffset *time.Duration
maxDeadlineOffset *time.Duration
workspaceTTL time.Duration
workspaceBumpTTL time.Duration
templateTTL time.Duration
templateBumpTTL time.Duration
templateDisallowsUserAutostop bool
expectedBump time.Duration
}{
Expand Down Expand Up @@ -83,6 +85,16 @@ func Test_ActivityBumpWorkspace(t *testing.T) {
workspaceTTL: 8 * time.Hour,
expectedBump: 1 * time.Hour,
},
{
name: "MaxDeadlineCustomBump",
transition: database.WorkspaceTransitionStart,
jobCompletedAt: sql.NullTime{Valid: true, Time: dbtime.Now().Add(-24 * time.Minute)},
buildDeadlineOffset: ptr.Ref(time.Minute), // last chance to bump!
maxDeadlineOffset: ptr.Ref(4 * time.Hour),
workspaceTTL: 8 * time.Hour,
workspaceBumpTTL: 5 * time.Hour,
expectedBump: 5 * time.Hour,
},
{
// A workspace that is still running, has passed its deadline, but has not
// yet been auto-stopped should still bump the deadline.
Expand All @@ -101,6 +113,17 @@ func Test_ActivityBumpWorkspace(t *testing.T) {
buildDeadlineOffset: ptr.Ref(-time.Minute),
workspaceTTL: 8 * time.Hour,
},
{
// Deadline bump TTL can be less than the time left in the
// deadline. It should be a no-op.
name: "DeadlineNotDecreased",
transition: database.WorkspaceTransitionStart,
jobCompletedAt: sql.NullTime{Valid: true, Time: dbtime.Now().Add(-24 * time.Hour)},
buildDeadlineOffset: ptr.Ref(time.Hour * 5),
workspaceTTL: 8 * time.Hour,
workspaceBumpTTL: time.Hour,
expectedBump: 0,
},
{
// A workspace built from a template that disallows user autostop should bump
// by the template TTL instead.
Expand All @@ -113,6 +136,18 @@ func Test_ActivityBumpWorkspace(t *testing.T) {
templateDisallowsUserAutostop: true,
expectedBump: 8 * time.Hour,
},
{
name: "TemplateDisallowsUserAutostopCustomBump",
transition: database.WorkspaceTransitionStart,
templateDisallowsUserAutostop: true,
jobCompletedAt: sql.NullTime{Valid: true, Time: dbtime.Now().Add(-7 * time.Hour)},
buildDeadlineOffset: ptr.Ref(time.Minute),
templateTTL: time.Hour * 8,
templateBumpTTL: time.Hour,
workspaceTTL: 24 * time.Hour,
workspaceBumpTTL: 10 * time.Hour,
expectedBump: time.Hour, // Expect smaller bump
},
} {
tt := tt
for _, tz := range timezones {
Expand Down Expand Up @@ -147,6 +182,7 @@ func Test_ActivityBumpWorkspace(t *testing.T) {
OrganizationID: org.ID,
TemplateID: template.ID,
Ttl: sql.NullInt64{Valid: true, Int64: int64(tt.workspaceTTL)},
TtlBump: int64(tt.workspaceBumpTTL),
})
job = dbgen.ProvisionerJob(t, db, nil, database.ProvisionerJob{
OrganizationID: org.ID,
Expand All @@ -163,6 +199,7 @@ func Test_ActivityBumpWorkspace(t *testing.T) {
UpdatedAt: dbtime.Now(),
AllowUserAutostop: !tt.templateDisallowsUserAutostop,
DefaultTTL: int64(tt.templateTTL),
DefaultTTLBump: int64(tt.templateBumpTTL),
}), "unexpected error updating template schedule")

var buildNumber int32 = 1
Expand Down Expand Up @@ -207,6 +244,7 @@ func Test_ActivityBumpWorkspace(t *testing.T) {
require.Equal(t, buildDeadline.UTC(), bld.Deadline.UTC(), "unexpected build deadline before bump")
require.Equal(t, maxDeadline.UTC(), bld.MaxDeadline.UTC(), "unexpected max deadline before bump")
require.Equal(t, tt.workspaceTTL, time.Duration(ws.Ttl.Int64), "unexpected workspace TTL before bump")
require.Equal(t, tt.workspaceBumpTTL, time.Duration(ws.TtlBump), "unexpected workspace TTL bump")

// Wait a bit before bumping as dbtime is rounded to the nearest millisecond.
// This should also hopefully be enough for Windows time resolution to register
Expand Down Expand Up @@ -236,8 +274,8 @@ func Test_ActivityBumpWorkspace(t *testing.T) {
// Assert that the bump occurred between start and end.
expectedDeadlineStart := start.Add(tt.expectedBump)
expectedDeadlineEnd := end.Add(tt.expectedBump)
require.GreaterOrEqual(t, updatedBuild.Deadline, expectedDeadlineStart, "new deadline should be greater than or equal to start")
require.LessOrEqual(t, updatedBuild.Deadline, expectedDeadlineEnd, "new deadline should be lesser than or equal to end")
require.GreaterOrEqualf(t, updatedBuild.Deadline, expectedDeadlineStart, "new deadline should be greater than or equal to start, pre-bump: %s, diff_from_last_deadline: %s", buildDeadline.String(), updatedBuild.Deadline.Sub(buildDeadline))
require.LessOrEqualf(t, updatedBuild.Deadline, expectedDeadlineEnd, "new deadline should be lesser than or equal to end, pre-bump: %s, diff_from_last_deadline: %s", buildDeadline.String(), updatedBuild.Deadline.Sub(buildDeadline))
})
}
}
Expand Down
1 change: 1 addition & 0 deletions coderd/database/dbgen/dbgen.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,7 @@ func Workspace(t testing.TB, db database.Store, orig database.Workspace) databas
Name: takeFirst(orig.Name, namesgenerator.GetRandomName(1)),
AutostartSchedule: orig.AutostartSchedule,
Ttl: orig.Ttl,
TtlBump: orig.TtlBump,
AutomaticUpdates: takeFirst(orig.AutomaticUpdates, database.AutomaticUpdatesNever),
})
require.NoError(t, err, "insert workspace")
Expand Down
14 changes: 14 additions & 0 deletions coderd/database/dbmem/dbmem.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ func (q *FakeQuerier) convertToWorkspaceRowsNoLock(ctx context.Context, workspac
Name: w.Name,
AutostartSchedule: w.AutostartSchedule,
Ttl: w.Ttl,
TtlBump: w.TtlBump,
LastUsedAt: w.LastUsedAt,
DormantAt: w.DormantAt,
DeletingAt: w.DeletingAt,
Expand Down Expand Up @@ -824,9 +825,15 @@ func (q *FakeQuerier) ActivityBumpWorkspace(ctx context.Context, workspaceID uui
var ttlDur time.Duration
if workspace.Ttl.Valid {
ttlDur = time.Duration(workspace.Ttl.Int64)
if workspace.TtlBump > 0 {
ttlDur = time.Duration(workspace.TtlBump)
}
}
if !template.AllowUserAutostop {
ttlDur = time.Duration(template.DefaultTTL)
if template.DefaultTTLBump > 0 {
ttlDur = time.Duration(template.DefaultTTLBump)
}
}
if ttlDur <= 0 {
// There's no TTL set anymore, so we don't know the bump duration.
Expand All @@ -843,6 +850,10 @@ func (q *FakeQuerier) ActivityBumpWorkspace(ctx context.Context, workspaceID uui
// Bump.
newDeadline := now.Add(ttlDur)
q.workspaceBuilds[i].UpdatedAt = now
if newDeadline.Before(q.workspaceBuilds[i].Deadline) {
// Never shorten a deadline
newDeadline = q.workspaceBuilds[i].Deadline
}
if !q.workspaceBuilds[i].MaxDeadline.IsZero() {
q.workspaceBuilds[i].Deadline = minTime(newDeadline, q.workspaceBuilds[i].MaxDeadline)
} else {
Expand Down Expand Up @@ -5070,6 +5081,7 @@ func (q *FakeQuerier) InsertWorkspace(_ context.Context, arg database.InsertWork
Name: arg.Name,
AutostartSchedule: arg.AutostartSchedule,
Ttl: arg.Ttl,
TtlBump: arg.TtlBump,
LastUsedAt: arg.LastUsedAt,
AutomaticUpdates: arg.AutomaticUpdates,
}
Expand Down Expand Up @@ -5941,6 +5953,7 @@ func (q *FakeQuerier) UpdateTemplateScheduleByID(_ context.Context, arg database
tpl.AllowUserAutostop = arg.AllowUserAutostop
tpl.UpdatedAt = dbtime.Now()
tpl.DefaultTTL = arg.DefaultTTL
tpl.DefaultTTLBump = arg.DefaultTTLBump
tpl.MaxTTL = arg.MaxTTL
tpl.AutostopRequirementDaysOfWeek = arg.AutostopRequirementDaysOfWeek
tpl.AutostopRequirementWeeks = arg.AutostopRequirementWeeks
Expand Down Expand Up @@ -6671,6 +6684,7 @@ func (q *FakeQuerier) UpdateWorkspaceTTL(_ context.Context, arg database.UpdateW
continue
}
workspace.Ttl = arg.Ttl
workspace.TtlBump = arg.TtlBump
q.workspaces[index] = workspace
return nil
}
Expand Down
11 changes: 9 additions & 2 deletions coderd/database/dump.sql

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 26 additions & 0 deletions coderd/database/migrations/000169_ttl_bump.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
BEGIN;

-- Update the template_with_users view;
DROP VIEW template_with_users;

ALTER TABLE templates DROP COLUMN default_ttl_bump;
ALTER TABLE workspaces DROP COLUMN ttl_bump;

-- If you need to update this view, put 'DROP VIEW template_with_users;' before this.
CREATE VIEW
template_with_users
AS
SELECT
templates.*,
coalesce(visible_users.avatar_url, '') AS created_by_avatar_url,
coalesce(visible_users.username, '') AS created_by_username
FROM
templates
LEFT JOIN
visible_users
ON
templates.created_by = visible_users.id;

COMMENT ON VIEW template_with_users IS 'Joins in the username + avatar url of the created by user.';

COMMIT;
29 changes: 29 additions & 0 deletions coderd/database/migrations/000169_ttl_bump.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
BEGIN;

DROP VIEW template_with_users;

ALTER TABLE templates ADD COLUMN default_ttl_bump bigint DEFAULT '0'::bigint NOT NULL;
COMMENT ON COLUMN templates.default_ttl_bump IS 'Amount of time to bump workspace ttl from activity. 0 will default to the "default_ttl" as the bump interval.';


ALTER TABLE workspaces ADD COLUMN ttl_bump bigint DEFAULT '0'::bigint NOT NULL;
COMMENT ON COLUMN workspaces.ttl_bump IS 'Amount of time to bump workspace ttl from activity. 0 will default to the "ttl" as the bump interval.';
Comment on lines +5 to +10
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

0 means defer to ttl for the bump interval.


CREATE VIEW
template_with_users
AS
SELECT
templates.*,
coalesce(visible_users.avatar_url, '') AS created_by_avatar_url,
coalesce(visible_users.username, '') AS created_by_username
FROM
templates
LEFT JOIN
visible_users
ON
templates.created_by = visible_users.id;

COMMENT ON VIEW template_with_users IS 'Joins in the username + avatar url of the created by user.';


COMMIT;
5 changes: 5 additions & 0 deletions coderd/database/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading