mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
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:
@@ -475,6 +475,7 @@ func (q *FakeQuerier) convertToWorkspaceRowsNoLock(ctx context.Context, workspac
|
||||
DeletingAt: w.DeletingAt,
|
||||
AutomaticUpdates: w.AutomaticUpdates,
|
||||
Favorite: w.Favorite,
|
||||
NextStartAt: w.NextStartAt,
|
||||
|
||||
OwnerAvatarUrl: extended.OwnerAvatarUrl,
|
||||
OwnerUsername: extended.OwnerUsername,
|
||||
@@ -1431,6 +1432,35 @@ func (q *FakeQuerier) BatchUpdateWorkspaceLastUsedAt(_ context.Context, arg data
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) BatchUpdateWorkspaceNextStartAt(_ context.Context, arg database.BatchUpdateWorkspaceNextStartAtParams) error {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for i, workspace := range q.workspaces {
|
||||
for j, workspaceID := range arg.IDs {
|
||||
if workspace.ID != workspaceID {
|
||||
continue
|
||||
}
|
||||
|
||||
nextStartAt := arg.NextStartAts[j]
|
||||
if nextStartAt.IsZero() {
|
||||
q.workspaces[i].NextStartAt = sql.NullTime{}
|
||||
} else {
|
||||
q.workspaces[i].NextStartAt = sql.NullTime{Valid: true, Time: nextStartAt}
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*FakeQuerier) BulkMarkNotificationMessagesFailed(_ context.Context, arg database.BulkMarkNotificationMessagesFailedParams) (int64, error) {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
@@ -6908,6 +6938,20 @@ func (q *FakeQuerier) GetWorkspacesAndAgentsByOwnerID(ctx context.Context, owner
|
||||
return q.GetAuthorizedWorkspacesAndAgentsByOwnerID(ctx, ownerID, nil)
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspacesByTemplateID(_ context.Context, templateID uuid.UUID) ([]database.WorkspaceTable, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
workspaces := []database.WorkspaceTable{}
|
||||
for _, workspace := range q.workspaces {
|
||||
if workspace.TemplateID == templateID {
|
||||
workspaces = append(workspaces, workspace)
|
||||
}
|
||||
}
|
||||
|
||||
return workspaces, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetWorkspacesEligibleForTransition(ctx context.Context, now time.Time) ([]database.GetWorkspacesEligibleForTransitionRow, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
@@ -6952,7 +6996,13 @@ func (q *FakeQuerier) GetWorkspacesEligibleForTransition(ctx context.Context, no
|
||||
if user.Status == database.UserStatusActive &&
|
||||
job.JobStatus != database.ProvisionerJobStatusFailed &&
|
||||
build.Transition == database.WorkspaceTransitionStop &&
|
||||
workspace.AutostartSchedule.Valid {
|
||||
workspace.AutostartSchedule.Valid &&
|
||||
// We do not know if workspace with a zero next start is eligible
|
||||
// for autostart, so we accept this false-positive. This can occur
|
||||
// when a coder version is upgraded and next_start_at has yet to
|
||||
// be set.
|
||||
(workspace.NextStartAt.Time.IsZero() ||
|
||||
!now.Before(workspace.NextStartAt.Time)) {
|
||||
workspaces = append(workspaces, database.GetWorkspacesEligibleForTransitionRow{
|
||||
ID: workspace.ID,
|
||||
Name: workspace.Name,
|
||||
@@ -6962,7 +7012,7 @@ func (q *FakeQuerier) GetWorkspacesEligibleForTransition(ctx context.Context, no
|
||||
|
||||
if !workspace.DormantAt.Valid &&
|
||||
template.TimeTilDormant > 0 &&
|
||||
now.Sub(workspace.LastUsedAt) > time.Duration(template.TimeTilDormant) {
|
||||
now.Sub(workspace.LastUsedAt) >= time.Duration(template.TimeTilDormant) {
|
||||
workspaces = append(workspaces, database.GetWorkspacesEligibleForTransitionRow{
|
||||
ID: workspace.ID,
|
||||
Name: workspace.Name,
|
||||
@@ -7927,6 +7977,7 @@ func (q *FakeQuerier) InsertWorkspace(_ context.Context, arg database.InsertWork
|
||||
Ttl: arg.Ttl,
|
||||
LastUsedAt: arg.LastUsedAt,
|
||||
AutomaticUpdates: arg.AutomaticUpdates,
|
||||
NextStartAt: arg.NextStartAt,
|
||||
}
|
||||
q.workspaces = append(q.workspaces, workspace)
|
||||
return workspace, nil
|
||||
@@ -9868,6 +9919,7 @@ func (q *FakeQuerier) UpdateWorkspaceAutostart(_ context.Context, arg database.U
|
||||
continue
|
||||
}
|
||||
workspace.AutostartSchedule = arg.AutostartSchedule
|
||||
workspace.NextStartAt = arg.NextStartAt
|
||||
q.workspaces[index] = workspace
|
||||
return nil
|
||||
}
|
||||
@@ -10017,6 +10069,29 @@ func (q *FakeQuerier) UpdateWorkspaceLastUsedAt(_ context.Context, arg database.
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpdateWorkspaceNextStartAt(_ context.Context, arg database.UpdateWorkspaceNextStartAtParams) error {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for index, workspace := range q.workspaces {
|
||||
if workspace.ID != arg.ID {
|
||||
continue
|
||||
}
|
||||
|
||||
workspace.NextStartAt = arg.NextStartAt
|
||||
q.workspaces[index] = workspace
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpdateWorkspaceProxy(_ context.Context, arg database.UpdateWorkspaceProxyParams) (database.WorkspaceProxy, error) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
Reference in New Issue
Block a user