mirror of
https://github.com/coder/coder.git
synced 2025-07-08 11:39:50 +00:00
feat: add activity bumping to template scheduling (#9040)
This commit is contained in:
@ -2385,6 +2385,14 @@ func (q *querier) UpdateTemplateVersionGitAuthProvidersByJobID(ctx context.Conte
|
||||
return q.db.UpdateTemplateVersionGitAuthProvidersByJobID(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateTemplateWorkspacesLastUsedAt(ctx context.Context, arg database.UpdateTemplateWorkspacesLastUsedAtParams) error {
|
||||
fetch := func(ctx context.Context, arg database.UpdateTemplateWorkspacesLastUsedAtParams) (database.Template, error) {
|
||||
return q.db.GetTemplateByID(ctx, arg.TemplateID)
|
||||
}
|
||||
|
||||
return fetchAndExec(q.log, q.auth, rbac.ActionUpdate, fetch, q.db.UpdateTemplateWorkspacesLastUsedAt)(ctx, arg)
|
||||
}
|
||||
|
||||
// UpdateUserDeletedByID
|
||||
// Deprecated: Delete this function in favor of 'SoftDeleteUserByID'. Deletes are
|
||||
// irreversible.
|
||||
@ -2663,12 +2671,12 @@ func (q *querier) UpdateWorkspaceTTL(ctx context.Context, arg database.UpdateWor
|
||||
return update(q.log, q.auth, fetch, q.db.UpdateWorkspaceTTL)(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) UpdateWorkspacesDeletingAtByTemplateID(ctx context.Context, arg database.UpdateWorkspacesDeletingAtByTemplateIDParams) error {
|
||||
fetch := func(ctx context.Context, arg database.UpdateWorkspacesDeletingAtByTemplateIDParams) (database.Template, error) {
|
||||
func (q *querier) UpdateWorkspacesLockedDeletingAtByTemplateID(ctx context.Context, arg database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error {
|
||||
fetch := func(ctx context.Context, arg database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) (database.Template, error) {
|
||||
return q.db.GetTemplateByID(ctx, arg.TemplateID)
|
||||
}
|
||||
|
||||
return fetchAndExec(q.log, q.auth, rbac.ActionUpdate, fetch, q.db.UpdateWorkspacesDeletingAtByTemplateID)(ctx, arg)
|
||||
return fetchAndExec(q.log, q.auth, rbac.ActionUpdate, fetch, q.db.UpdateWorkspacesLockedDeletingAtByTemplateID)(ctx, arg)
|
||||
}
|
||||
|
||||
func (q *querier) UpsertAppSecurityKey(ctx context.Context, data string) error {
|
||||
|
@ -5199,6 +5199,26 @@ func (q *FakeQuerier) UpdateTemplateVersionGitAuthProvidersByJobID(_ context.Con
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpdateTemplateWorkspacesLastUsedAt(_ context.Context, arg database.UpdateTemplateWorkspacesLastUsedAtParams) error {
|
||||
err := validateDatabaseType(arg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
for i, ws := range q.workspaces {
|
||||
if ws.TemplateID != arg.TemplateID {
|
||||
continue
|
||||
}
|
||||
ws.LastUsedAt = arg.LastUsedAt
|
||||
q.workspaces[i] = ws
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpdateUserDeletedByID(_ context.Context, params database.UpdateUserDeletedByIDParams) error {
|
||||
if err := validateDatabaseType(params); err != nil {
|
||||
return err
|
||||
@ -5796,7 +5816,7 @@ func (q *FakeQuerier) UpdateWorkspaceTTL(_ context.Context, arg database.UpdateW
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpdateWorkspacesDeletingAtByTemplateID(_ context.Context, arg database.UpdateWorkspacesDeletingAtByTemplateIDParams) error {
|
||||
func (q *FakeQuerier) UpdateWorkspacesLockedDeletingAtByTemplateID(_ context.Context, arg database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
@ -5806,9 +5826,21 @@ func (q *FakeQuerier) UpdateWorkspacesDeletingAtByTemplateID(_ context.Context,
|
||||
}
|
||||
|
||||
for i, ws := range q.workspaces {
|
||||
if ws.TemplateID != arg.TemplateID {
|
||||
continue
|
||||
}
|
||||
|
||||
if ws.LockedAt.Time.IsZero() {
|
||||
continue
|
||||
}
|
||||
|
||||
if !arg.LockedAt.IsZero() {
|
||||
ws.LockedAt = sql.NullTime{
|
||||
Valid: true,
|
||||
Time: arg.LockedAt,
|
||||
}
|
||||
}
|
||||
|
||||
deletingAt := sql.NullTime{
|
||||
Valid: arg.LockedTtlMs > 0,
|
||||
}
|
||||
|
@ -1453,6 +1453,13 @@ func (m metricsStore) UpdateTemplateVersionGitAuthProvidersByJobID(ctx context.C
|
||||
return err
|
||||
}
|
||||
|
||||
func (m metricsStore) UpdateTemplateWorkspacesLastUsedAt(ctx context.Context, arg database.UpdateTemplateWorkspacesLastUsedAtParams) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.UpdateTemplateWorkspacesLastUsedAt(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("UpdateTemplateWorkspacesLastUsedAt").Observe(time.Since(start).Seconds())
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) UpdateUserDeletedByID(ctx context.Context, arg database.UpdateUserDeletedByIDParams) error {
|
||||
start := time.Now()
|
||||
err := m.s.UpdateUserDeletedByID(ctx, arg)
|
||||
@ -1635,10 +1642,10 @@ func (m metricsStore) UpdateWorkspaceTTL(ctx context.Context, arg database.Updat
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m metricsStore) UpdateWorkspacesDeletingAtByTemplateID(ctx context.Context, arg database.UpdateWorkspacesDeletingAtByTemplateIDParams) error {
|
||||
func (m metricsStore) UpdateWorkspacesLockedDeletingAtByTemplateID(ctx context.Context, arg database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.UpdateWorkspacesDeletingAtByTemplateID(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("UpdateWorkspacesDeletingAtByTemplateID").Observe(time.Since(start).Seconds())
|
||||
r0 := m.s.UpdateWorkspacesLockedDeletingAtByTemplateID(ctx, arg)
|
||||
m.queryLatencies.WithLabelValues("UpdateWorkspacesLockedDeletingAtByTemplateID").Observe(time.Since(start).Seconds())
|
||||
return r0
|
||||
}
|
||||
|
||||
|
@ -3062,6 +3062,20 @@ func (mr *MockStoreMockRecorder) UpdateTemplateVersionGitAuthProvidersByJobID(ar
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTemplateVersionGitAuthProvidersByJobID", reflect.TypeOf((*MockStore)(nil).UpdateTemplateVersionGitAuthProvidersByJobID), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpdateTemplateWorkspacesLastUsedAt mocks base method.
|
||||
func (m *MockStore) UpdateTemplateWorkspacesLastUsedAt(arg0 context.Context, arg1 database.UpdateTemplateWorkspacesLastUsedAtParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateTemplateWorkspacesLastUsedAt", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpdateTemplateWorkspacesLastUsedAt indicates an expected call of UpdateTemplateWorkspacesLastUsedAt.
|
||||
func (mr *MockStoreMockRecorder) UpdateTemplateWorkspacesLastUsedAt(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateTemplateWorkspacesLastUsedAt", reflect.TypeOf((*MockStore)(nil).UpdateTemplateWorkspacesLastUsedAt), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpdateUserDeletedByID mocks base method.
|
||||
func (m *MockStore) UpdateUserDeletedByID(arg0 context.Context, arg1 database.UpdateUserDeletedByIDParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
@ -3437,18 +3451,18 @@ func (mr *MockStoreMockRecorder) UpdateWorkspaceTTL(arg0, arg1 interface{}) *gom
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspaceTTL", reflect.TypeOf((*MockStore)(nil).UpdateWorkspaceTTL), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpdateWorkspacesDeletingAtByTemplateID mocks base method.
|
||||
func (m *MockStore) UpdateWorkspacesDeletingAtByTemplateID(arg0 context.Context, arg1 database.UpdateWorkspacesDeletingAtByTemplateIDParams) error {
|
||||
// UpdateWorkspacesLockedDeletingAtByTemplateID mocks base method.
|
||||
func (m *MockStore) UpdateWorkspacesLockedDeletingAtByTemplateID(arg0 context.Context, arg1 database.UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpdateWorkspacesDeletingAtByTemplateID", arg0, arg1)
|
||||
ret := m.ctrl.Call(m, "UpdateWorkspacesLockedDeletingAtByTemplateID", arg0, arg1)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpdateWorkspacesDeletingAtByTemplateID indicates an expected call of UpdateWorkspacesDeletingAtByTemplateID.
|
||||
func (mr *MockStoreMockRecorder) UpdateWorkspacesDeletingAtByTemplateID(arg0, arg1 interface{}) *gomock.Call {
|
||||
// UpdateWorkspacesLockedDeletingAtByTemplateID indicates an expected call of UpdateWorkspacesLockedDeletingAtByTemplateID.
|
||||
func (mr *MockStoreMockRecorder) UpdateWorkspacesLockedDeletingAtByTemplateID(arg0, arg1 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspacesDeletingAtByTemplateID", reflect.TypeOf((*MockStore)(nil).UpdateWorkspacesDeletingAtByTemplateID), arg0, arg1)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateWorkspacesLockedDeletingAtByTemplateID", reflect.TypeOf((*MockStore)(nil).UpdateWorkspacesLockedDeletingAtByTemplateID), arg0, arg1)
|
||||
}
|
||||
|
||||
// UpsertAppSecurityKey mocks base method.
|
||||
|
@ -270,6 +270,7 @@ type sqlcQuerier interface {
|
||||
UpdateTemplateVersionByID(ctx context.Context, arg UpdateTemplateVersionByIDParams) error
|
||||
UpdateTemplateVersionDescriptionByJobID(ctx context.Context, arg UpdateTemplateVersionDescriptionByJobIDParams) error
|
||||
UpdateTemplateVersionGitAuthProvidersByJobID(ctx context.Context, arg UpdateTemplateVersionGitAuthProvidersByJobIDParams) error
|
||||
UpdateTemplateWorkspacesLastUsedAt(ctx context.Context, arg UpdateTemplateWorkspacesLastUsedAtParams) error
|
||||
UpdateUserDeletedByID(ctx context.Context, arg UpdateUserDeletedByIDParams) error
|
||||
UpdateUserHashedPassword(ctx context.Context, arg UpdateUserHashedPasswordParams) error
|
||||
UpdateUserLastSeenAt(ctx context.Context, arg UpdateUserLastSeenAtParams) (User, error)
|
||||
@ -297,7 +298,7 @@ type sqlcQuerier interface {
|
||||
UpdateWorkspaceProxy(ctx context.Context, arg UpdateWorkspaceProxyParams) (WorkspaceProxy, error)
|
||||
UpdateWorkspaceProxyDeleted(ctx context.Context, arg UpdateWorkspaceProxyDeletedParams) error
|
||||
UpdateWorkspaceTTL(ctx context.Context, arg UpdateWorkspaceTTLParams) error
|
||||
UpdateWorkspacesDeletingAtByTemplateID(ctx context.Context, arg UpdateWorkspacesDeletingAtByTemplateIDParams) error
|
||||
UpdateWorkspacesLockedDeletingAtByTemplateID(ctx context.Context, arg UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error
|
||||
UpsertAppSecurityKey(ctx context.Context, value string) error
|
||||
// The default proxy is implied and not actually stored in the database.
|
||||
// So we need to store it's configuration here for display purposes.
|
||||
|
@ -9786,6 +9786,24 @@ func (q *sqlQuerier) InsertWorkspace(ctx context.Context, arg InsertWorkspacePar
|
||||
return i, err
|
||||
}
|
||||
|
||||
const updateTemplateWorkspacesLastUsedAt = `-- name: UpdateTemplateWorkspacesLastUsedAt :exec
|
||||
UPDATE workspaces
|
||||
SET
|
||||
last_used_at = $1::timestamptz
|
||||
WHERE
|
||||
template_id = $2
|
||||
`
|
||||
|
||||
type UpdateTemplateWorkspacesLastUsedAtParams struct {
|
||||
LastUsedAt time.Time `db:"last_used_at" json:"last_used_at"`
|
||||
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) UpdateTemplateWorkspacesLastUsedAt(ctx context.Context, arg UpdateTemplateWorkspacesLastUsedAtParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateTemplateWorkspacesLastUsedAt, arg.LastUsedAt, arg.TemplateID)
|
||||
return err
|
||||
}
|
||||
|
||||
const updateWorkspace = `-- name: UpdateWorkspace :one
|
||||
UPDATE
|
||||
workspaces
|
||||
@ -9945,23 +9963,28 @@ func (q *sqlQuerier) UpdateWorkspaceTTL(ctx context.Context, arg UpdateWorkspace
|
||||
return err
|
||||
}
|
||||
|
||||
const updateWorkspacesDeletingAtByTemplateID = `-- name: UpdateWorkspacesDeletingAtByTemplateID :exec
|
||||
UPDATE
|
||||
workspaces
|
||||
const updateWorkspacesLockedDeletingAtByTemplateID = `-- name: UpdateWorkspacesLockedDeletingAtByTemplateID :exec
|
||||
UPDATE workspaces
|
||||
SET
|
||||
deleting_at = CASE WHEN $1::bigint = 0 THEN NULL ELSE locked_at + interval '1 milliseconds' * $1::bigint END
|
||||
deleting_at = CASE
|
||||
WHEN $1::bigint = 0 THEN NULL
|
||||
WHEN $2::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN ($2::timestamptz) + interval '1 milliseconds' * $1::bigint
|
||||
ELSE locked_at + interval '1 milliseconds' * $1::bigint
|
||||
END,
|
||||
locked_at = CASE WHEN $2::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN $2::timestamptz ELSE locked_at END
|
||||
WHERE
|
||||
template_id = $2
|
||||
template_id = $3
|
||||
AND
|
||||
locked_at IS NOT NULL
|
||||
locked_at IS NOT NULL
|
||||
`
|
||||
|
||||
type UpdateWorkspacesDeletingAtByTemplateIDParams struct {
|
||||
type UpdateWorkspacesLockedDeletingAtByTemplateIDParams struct {
|
||||
LockedTtlMs int64 `db:"locked_ttl_ms" json:"locked_ttl_ms"`
|
||||
LockedAt time.Time `db:"locked_at" json:"locked_at"`
|
||||
TemplateID uuid.UUID `db:"template_id" json:"template_id"`
|
||||
}
|
||||
|
||||
func (q *sqlQuerier) UpdateWorkspacesDeletingAtByTemplateID(ctx context.Context, arg UpdateWorkspacesDeletingAtByTemplateIDParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateWorkspacesDeletingAtByTemplateID, arg.LockedTtlMs, arg.TemplateID)
|
||||
func (q *sqlQuerier) UpdateWorkspacesLockedDeletingAtByTemplateID(ctx context.Context, arg UpdateWorkspacesLockedDeletingAtByTemplateIDParams) error {
|
||||
_, err := q.db.ExecContext(ctx, updateWorkspacesLockedDeletingAtByTemplateID, arg.LockedTtlMs, arg.LockedAt, arg.TemplateID)
|
||||
return err
|
||||
}
|
||||
|
@ -512,12 +512,23 @@ AND
|
||||
workspaces.id = $1
|
||||
RETURNING workspaces.*;
|
||||
|
||||
-- name: UpdateWorkspacesDeletingAtByTemplateID :exec
|
||||
UPDATE
|
||||
workspaces
|
||||
-- name: UpdateWorkspacesLockedDeletingAtByTemplateID :exec
|
||||
UPDATE workspaces
|
||||
SET
|
||||
deleting_at = CASE WHEN @locked_ttl_ms::bigint = 0 THEN NULL ELSE locked_at + interval '1 milliseconds' * @locked_ttl_ms::bigint END
|
||||
deleting_at = CASE
|
||||
WHEN @locked_ttl_ms::bigint = 0 THEN NULL
|
||||
WHEN @locked_at::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN (@locked_at::timestamptz) + interval '1 milliseconds' * @locked_ttl_ms::bigint
|
||||
ELSE locked_at + interval '1 milliseconds' * @locked_ttl_ms::bigint
|
||||
END,
|
||||
locked_at = CASE WHEN @locked_at::timestamptz > '0001-01-01 00:00:00+00'::timestamptz THEN @locked_at::timestamptz ELSE locked_at END
|
||||
WHERE
|
||||
template_id = @template_id
|
||||
template_id = @template_id
|
||||
AND
|
||||
locked_at IS NOT NULL;
|
||||
locked_at IS NOT NULL;
|
||||
|
||||
-- name: UpdateTemplateWorkspacesLastUsedAt :exec
|
||||
UPDATE workspaces
|
||||
SET
|
||||
last_used_at = @last_used_at::timestamptz
|
||||
WHERE
|
||||
template_id = @template_id;
|
||||
|
@ -105,6 +105,18 @@ type TemplateScheduleOptions struct {
|
||||
// LockedTTL dictates the duration after which locked workspaces will be
|
||||
// permanently deleted.
|
||||
LockedTTL time.Duration `json:"locked_ttl"`
|
||||
// UpdateWorkspaceLastUsedAt updates the template's workspaces'
|
||||
// last_used_at field. This is useful for preventing updates to the
|
||||
// templates inactivity_ttl immediately triggering a lock action against
|
||||
// workspaces whose last_used_at field violates the new template
|
||||
// inactivity_ttl threshold.
|
||||
UpdateWorkspaceLastUsedAt bool `json:"update_workspace_last_used_at"`
|
||||
// UpdateWorkspaceLockedAt updates the template's workspaces'
|
||||
// locked_at field. This is useful for preventing updates to the
|
||||
// templates locked_ttl immediately triggering a delete action against
|
||||
// workspaces whose locked_at field violates the new template locked_ttl
|
||||
// threshold.
|
||||
UpdateWorkspaceLockedAt bool `json:"update_workspace_locked_at"`
|
||||
}
|
||||
|
||||
// TemplateScheduleStore provides an interface for retrieving template
|
||||
|
@ -622,9 +622,11 @@ func (api *API) patchTemplateMeta(rw http.ResponseWriter, r *http.Request) {
|
||||
DaysOfWeek: restartRequirementDaysOfWeekParsed,
|
||||
Weeks: req.RestartRequirement.Weeks,
|
||||
},
|
||||
FailureTTL: failureTTL,
|
||||
InactivityTTL: inactivityTTL,
|
||||
LockedTTL: lockedTTL,
|
||||
FailureTTL: failureTTL,
|
||||
InactivityTTL: inactivityTTL,
|
||||
LockedTTL: lockedTTL,
|
||||
UpdateWorkspaceLastUsedAt: req.UpdateWorkspaceLastUsedAt,
|
||||
UpdateWorkspaceLockedAt: req.UpdateWorkspaceLockedAt,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("set template schedule options: %w", err)
|
||||
|
Reference in New Issue
Block a user