- Notifications
You must be signed in to change notification settings - Fork1.1k
fix: make GetWorkspacesEligibleForTransition return even less false positives#15594
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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
78d9befdea0c20d0f1851077439c3b419851a66a149725ec75e9b806b2a037b997c902dd055588bddfdf66a01a3b7434c0100f54cd201025eac63b71b31cbbeaf32ee1599b2a142e3353f17e46e993643d349c56a48fb99cc9307545350d1c1648ae03d0b7f23b2ec918f3c8a8b1b6d92d239a62e2f13adf74e6fdec3d6cFile filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -65,6 +65,7 @@ | ||
| }, | ||
| "automatic_updates": "never", | ||
| "allow_renames": false, | ||
| "favorite": false, | ||
| "next_start_at": "[timestamp]" | ||
| } | ||
| ] | ||
Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.
Uh oh!
There was an error while loading.Please reload this page.
Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.
Uh oh!
There was an error while loading.Please reload this page.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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,currentTick) | ||
DanielleMaywood marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| 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,err := schedule.NextAllowedAutostart(build.CreatedAt, ws.AutostartSchedule.String, templateSchedule) | ||
| iferr != nil { | ||
| return false | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 && | ||
| // 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. | ||
Member 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. We have a new known case, reset by db trigger. Should we implement the trigger in memory db as well? (I know it's probably going away soon, but still, might cause flakes?) ContributorAuthor 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. I don'tthink it'd cause flakes, but instead cause dbmem tests to fail when postgres tests do not. Happy to add it though ContributorAuthor 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. On second thought, I've checked Member 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. There is no good way to do it, just ad hoc code in various functions 😢 ContributorAuthor 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. Ah that's unfortunate. I think attempting to emulate this trigger isn't worth it then. If someone writes a test that is dependent on the trigger (which they ideally shouldn't) then they'll have to disable it when running under | ||
| (workspace.NextStartAt.Time.IsZero() || | ||
DanielleMaywood marked this conversation as resolved. Show resolvedHide resolvedUh oh!There was an error while loading.Please reload this page. | ||
| !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) { | ||
| 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() | ||
Some generated files are not rendered by default. Learn more abouthow customized files appear on GitHub.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.