Skip to content

Commit b9659c8

Browse files
amisevsksleshchenko
authored andcommitted
Set conditions more carefully in start/stop to improve metric accuracy
* Move setting "Started" condition to the point in the reconcile loop where we know the workspace is started to avoid marking new stopped workspaces as started * Set "Started" condition to false when a workspace is stopped * Only update metrics if the API call to sync workspace status succeeds to avoid accidentally incrementing metrics when a request fails Signed-off-by: Angel Misevski <[email protected]>
1 parent d879a81 commit b9659c8

File tree

2 files changed

+29
-14
lines changed

2 files changed

+29
-14
lines changed

controllers/workspace/devworkspace_controller.go

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -120,18 +120,7 @@ func (r *DevWorkspaceReconciler) Reconcile(req ctrl.Request) (reconcileResult ct
120120
return reconcile.Result{}, err
121121
}
122122
workspace.Status.DevWorkspaceId = workspaceId
123-
workspace.Status.Phase = dw.DevWorkspaceStatusStarting
124-
workspace.Status.Message = "Initializing DevWorkspace"
125-
workspace.Status.Conditions = []dw.DevWorkspaceCondition{
126-
{
127-
Type: conditions.Started,
128-
Status: corev1.ConditionTrue,
129-
LastTransitionTime: metav1.Time{Time: clock.Now()},
130-
Message: "DevWorkspace is starting",
131-
},
132-
}
133123
err = r.Status().Update(ctx, workspace)
134-
metrics.WorkspaceStarted(workspace, reqLogger)
135124
return reconcile.Result{Requeue: true}, err
136125
}
137126

@@ -160,6 +149,27 @@ func (r *DevWorkspaceReconciler) Reconcile(req ctrl.Request) (reconcileResult ct
160149
return r.stopWorkspace(workspace, reqLogger)
161150
}
162151

152+
// If this is the first reconcile for a starting workspace, mark it as starting now. This is done outside the regular
153+
// updateWorkspaceStatus function to ensure it gets set immediately
154+
if workspace.Status.Phase != dw.DevWorkspaceStatusStarting && workspace.Status.Phase != dw.DevWorkspaceStatusRunning {
155+
// Set 'Started' condition as early as possible to get accurate timing metrics
156+
workspace.Status.Phase = dw.DevWorkspaceStatusStarting
157+
workspace.Status.Message = "Initializing DevWorkspace"
158+
workspace.Status.Conditions = []dw.DevWorkspaceCondition{
159+
{
160+
Type: conditions.Started,
161+
Status: corev1.ConditionTrue,
162+
LastTransitionTime: metav1.Time{Time: clock.Now()},
163+
Message: "DevWorkspace is starting",
164+
},
165+
}
166+
err = r.Status().Update(ctx, workspace)
167+
if err == nil {
168+
metrics.WorkspaceStarted(workspace, reqLogger)
169+
}
170+
return reconcile.Result{}, err
171+
}
172+
163173
// Prepare handling workspace status and condition
164174
reconcileStatus := currentStatus{phase: dw.DevWorkspaceStatusStarting}
165175
reconcileStatus.setConditionTrue(conditions.Started, "DevWorkspace is starting")
@@ -370,8 +380,10 @@ func (r *DevWorkspaceReconciler) stopWorkspace(workspace *dw.DevWorkspace, logge
370380
switch status.phase {
371381
case devworkspacePhaseFailing, dw.DevWorkspaceStatusFailed:
372382
status.phase = dw.DevWorkspaceStatusFailed
383+
status.setConditionFalse(conditions.Started, "Workspace stopped due to error")
373384
default:
374385
status.phase = dw.DevWorkspaceStatusStopped
386+
status.setConditionFalse(conditions.Started, "Workspace is stopped")
375387
}
376388
}
377389
return r.updateWorkspaceStatus(workspace, logger, &status, reconcile.Result{}, nil)

controllers/workspace/status.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ var healthHttpClient = &http.Client{
6868
// updating the status.
6969
func (r *DevWorkspaceReconciler) updateWorkspaceStatus(workspace *dw.DevWorkspace, logger logr.Logger, status *currentStatus, reconcileResult reconcile.Result, reconcileError error) (reconcile.Result, error) {
7070
syncConditions(&workspace.Status, status)
71-
updateMetricsForPhase(workspace, status.phase, logger)
71+
oldPhase := workspace.Status.Phase
7272
workspace.Status.Phase = status.phase
7373

7474
infoMessage := getInfoMessage(workspace, status)
@@ -85,7 +85,10 @@ func (r *DevWorkspaceReconciler) updateWorkspaceStatus(workspace *dw.DevWorkspac
8585
if reconcileError == nil {
8686
reconcileError = err
8787
}
88+
} else {
89+
updateMetricsForPhase(workspace, oldPhase, status.phase, logger)
8890
}
91+
8992
return reconcileResult, reconcileError
9093
}
9194

@@ -221,8 +224,8 @@ func getInfoMessage(workspace *dw.DevWorkspace, status *currentStatus) string {
221224
// updateMetricsForPhase increments DevWorkspace startup metrics based on phase transitions in a DevWorkspace. It avoids
222225
// incrementing the underlying metrics where possible (e.g. reconciling an already running workspace) by only incrementing
223226
// counters when the new phase is different from the current on in the DevWorkspace.
224-
func updateMetricsForPhase(workspace *dw.DevWorkspace, newPhase dw.DevWorkspacePhase, logger logr.Logger) {
225-
if workspace.Status.Phase == newPhase {
227+
func updateMetricsForPhase(workspace *dw.DevWorkspace, oldPhase, newPhase dw.DevWorkspacePhase, logger logr.Logger) {
228+
if oldPhase == newPhase {
226229
return
227230
}
228231
switch newPhase {

0 commit comments

Comments
 (0)