fix: make GetWorkspacesEligibleForTransition return even less false positives (#15594)

Relates to https://github.com/coder/coder/issues/15082

Further to https://github.com/coder/coder/pull/15429, this reduces the
amount of false-positives returned by the 'is eligible for autostart'
part of the query. We achieve this by calculating the 'next start at'
time of the workspace, storing it in the database, and using it in our
`GetWorkspacesEligibleForTransition` query.

The prior implementation of the 'is eligible for autostart' query would
return _all_ workspaces that at some point in the future _might_ be
eligible for autostart. This now ensures we only return workspaces that
_should_ be eligible for autostart.

We also now pass `currentTick` instead of `t` to the
`GetWorkspacesEligibleForTransition` query as otherwise we'll have one
round of workspaces that are skipped by `isEligibleForTransition` due to
`currentTick` being a truncated version of `t`.
This commit is contained in:
Danielle Maywood
2024-12-02 21:02:36 +00:00
committed by GitHub
parent 2b57dcc68c
commit e21a301682
35 changed files with 1012 additions and 75 deletions

View File

@ -142,7 +142,7 @@ func (e *Executor) runOnce(t time.Time) Stats {
// 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.
workspaces, err := e.db.GetWorkspacesEligibleForTransition(e.ctx, t)
workspaces, err := e.db.GetWorkspacesEligibleForTransition(e.ctx, currentTick)
if err != nil {
e.log.Error(e.ctx, "get workspaces for autostart or autostop", slog.Error(err))
return stats
@ -205,6 +205,23 @@ func (e *Executor) runOnce(t time.Time) Stats {
return xerrors.Errorf("get template scheduling options: %w", err)
}
// If next start at is not valid we need to re-compute it
if !ws.NextStartAt.Valid && ws.AutostartSchedule.Valid {
next, err := schedule.NextAllowedAutostart(currentTick, ws.AutostartSchedule.String, templateSchedule)
if err == nil {
nextStartAt := sql.NullTime{Valid: true, Time: dbtime.Time(next.UTC())}
if err = tx.UpdateWorkspaceNextStartAt(e.ctx, database.UpdateWorkspaceNextStartAtParams{
ID: wsID,
NextStartAt: nextStartAt,
}); err != nil {
return xerrors.Errorf("update workspace next start at: %w", err)
}
// Save re-fetching the workspace
ws.NextStartAt = nextStartAt
}
}
tmpl, err = tx.GetTemplateByID(e.ctx, ws.TemplateID)
if err != nil {
return xerrors.Errorf("get template by ID: %w", err)
@ -463,8 +480,8 @@ func isEligibleForAutostart(user database.User, ws database.Workspace, build dat
return false
}
nextTransition, allowed := schedule.NextAutostart(build.CreatedAt, ws.AutostartSchedule.String, templateSchedule)
if !allowed {
nextTransition, err := schedule.NextAllowedAutostart(build.CreatedAt, ws.AutostartSchedule.String, templateSchedule)
if err != nil {
return false
}