-
Notifications
You must be signed in to change notification settings - Fork 980
fix: coderd: decouple ttl and deadline #2282
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
Changes from 9 commits
a19a0e9
3999661
f5ce8a3
987f785
cd4dce3
91e3171
00ac2a2
3faf3c8
a763d2f
bbd8288
feef823
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -85,11 +85,9 @@ func (e *Executor) runOnce(t time.Time) Stats { | |
// is what we compare against when performing autostop operations, rounded down | ||
// to the minute. | ||
// | ||
// NOTE: Currently, if a workspace build is created with a given TTL and then | ||
// the user either changes or unsets the TTL, the deadline for the workspace | ||
// build will not have changed. So, autostop will still happen at the | ||
// original TTL value from when the workspace build was created. | ||
// Whether this is expected behavior from a user's perspective is not yet known. | ||
// NOTE: If a workspace build is created with a given TTL and then the user either | ||
// changes or unsets the TTL, the deadline for the workspace build will not | ||
// have changed. This behavior is as expected per #2229. | ||
Comment on lines
+88
to
+90
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. review: this is now the expected behaviour until we're told otherwise! |
||
eligibleWorkspaces, err := db.GetWorkspacesAutostart(e.ctx) | ||
if err != nil { | ||
return xerrors.Errorf("get eligible workspaces for autostart or autostop: %w", err) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -319,8 +319,8 @@ func (api *API) postWorkspacesByOrganization(rw http.ResponseWriter, r *http.Req | |
} | ||
|
||
if !dbTTL.Valid { | ||
// Default to template maximum when creating a new workspace | ||
dbTTL = sql.NullInt64{Valid: true, Int64: template.MaxTtl} | ||
// Default to min(12 hours, template maximum). Just defaulting to template maximum can be surprising. | ||
dbTTL = sql.NullInt64{Valid: true, Int64: min(template.MaxTtl, int64(12*time.Hour))} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. review: as the default max_ttl for templates is 168 hours, setting the workspace default to something hopefully more reasonable on a day-to-day basis. Folks can still change this if they want! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion (non-blocking): Should we make this a constant somewhere, or perhaps define in the db? It could be hard to determine where this limit is coming from. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Constant sounds good! |
||
} | ||
|
||
workspace, err := api.Database.GetWorkspaceByOwnerIDAndName(r.Context(), database.GetWorkspaceByOwnerIDAndNameParams{ | ||
|
@@ -541,75 +541,41 @@ func (api *API) putWorkspaceTTL(rw http.ResponseWriter, r *http.Request) { | |
return | ||
} | ||
|
||
template, err := api.Database.GetTemplateByID(r.Context(), workspace.TemplateID) | ||
if err != nil { | ||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ | ||
Message: "Error fetching workspace template!", | ||
}) | ||
return | ||
} | ||
var validErrs []httpapi.Error | ||
|
||
dbTTL, err := validWorkspaceTTLMillis(req.TTLMillis, time.Duration(template.MaxTtl)) | ||
if err != nil { | ||
httpapi.Write(rw, http.StatusBadRequest, httpapi.Response{ | ||
Message: "Invalid workspace TTL.", | ||
Detail: err.Error(), | ||
Validations: []httpapi.Error{ | ||
{ | ||
Field: "ttl_ms", | ||
Detail: err.Error(), | ||
}, | ||
}, | ||
}) | ||
return | ||
} | ||
err := api.Database.InTx(func(s database.Store) error { | ||
template, err := s.GetTemplateByID(r.Context(), workspace.TemplateID) | ||
if err != nil { | ||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ | ||
Message: "Error fetching workspace template!", | ||
}) | ||
return xerrors.Errorf("fetch workspace template: %w", err) | ||
} | ||
|
||
err = api.Database.InTx(func(s database.Store) error { | ||
dbTTL, err := validWorkspaceTTLMillis(req.TTLMillis, time.Duration(template.MaxTtl)) | ||
if err != nil { | ||
validErrs = append(validErrs, httpapi.Error{Field: "ttl_ms", Detail: err.Error()}) | ||
return err | ||
} | ||
if err := s.UpdateWorkspaceTTL(r.Context(), database.UpdateWorkspaceTTLParams{ | ||
ID: workspace.ID, | ||
Ttl: dbTTL, | ||
}); err != nil { | ||
return xerrors.Errorf("update workspace TTL: %w", err) | ||
} | ||
|
||
// Also extend the workspace deadline if the workspace is running | ||
latestBuild, err := s.GetLatestWorkspaceBuildByWorkspaceID(r.Context(), workspace.ID) | ||
if err != nil { | ||
return xerrors.Errorf("get latest workspace build: %w", err) | ||
} | ||
|
||
if latestBuild.Transition != database.WorkspaceTransitionStart { | ||
return nil // nothing to do | ||
} | ||
|
||
if latestBuild.UpdatedAt.IsZero() { | ||
// Build in progress; provisionerd should update with the new TTL. | ||
return nil | ||
} | ||
|
||
var newDeadline time.Time | ||
if dbTTL.Valid { | ||
newDeadline = latestBuild.UpdatedAt.Add(time.Duration(dbTTL.Int64)) | ||
} | ||
|
||
if err := s.UpdateWorkspaceBuildByID( | ||
r.Context(), | ||
database.UpdateWorkspaceBuildByIDParams{ | ||
ID: latestBuild.ID, | ||
UpdatedAt: latestBuild.UpdatedAt, | ||
ProvisionerState: latestBuild.ProvisionerState, | ||
Deadline: newDeadline, | ||
}, | ||
); err != nil { | ||
return xerrors.Errorf("update workspace deadline: %w", err) | ||
} | ||
return nil | ||
}) | ||
|
||
if err != nil { | ||
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{ | ||
Message: "Error updating workspace time until shutdown!", | ||
Detail: err.Error(), | ||
code := http.StatusInternalServerError | ||
if len(validErrs) > 0 { | ||
code = http.StatusBadRequest | ||
} | ||
httpapi.Write(rw, code, httpapi.Response{ | ||
Message: "Error updating workspace time until shutdown!", | ||
Validations: validErrs, | ||
Detail: err.Error(), | ||
}) | ||
return | ||
} | ||
|
@@ -974,3 +940,10 @@ func validWorkspaceSchedule(s *string, min time.Duration) (sql.NullString, error | |
String: *s, | ||
}, nil | ||
} | ||
|
||
func min(x, y int64) int64 { | ||
if x < y { | ||
return x | ||
} | ||
return y | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
review: this was causing us to measure deadline extensions incorrectly, resulting in things like
8h (+3m)
if your workspace took 3 minutes to buidl.