@@ -77,36 +77,37 @@ func Workspaces(ctx context.Context, logger slog.Logger, registerer prometheus.R
77
77
duration = 5 * time .Minute
78
78
}
79
79
80
- workspacesByStatus := prometheus .NewGaugeVec (prometheus.GaugeOpts {
80
+ workspaceStatuses := prometheus .NewGaugeVec (prometheus.GaugeOpts {
81
81
Namespace : "coderd" ,
82
82
Subsystem : "api" ,
83
83
Name : "workspace_latest_build_total" ,
84
84
Help : "The current number of workspace builds by status." ,
85
85
}, []string {"status" })
86
- if err := registerer .Register (workspacesByStatus ); err != nil {
86
+ if err := registerer .Register (workspaceStatuses ); err != nil {
87
87
return nil , err
88
88
}
89
89
90
- workspacesDetail := prometheus .NewGaugeVec (prometheus.GaugeOpts {
90
+ workspaceDetails := prometheus .NewGaugeVec (prometheus.GaugeOpts {
91
91
Namespace : "coderd" ,
92
92
Subsystem : "api" ,
93
93
Name : "workspace_detail" ,
94
94
Help : "The current workspace details by template, transition, owner, and status." ,
95
95
}, []string {"status" , "template_name" , "template_version" , "workspace_name" , "workspace_owner" , "workspace_transition" })
96
- if err := registerer .Register (workspacesDetail ); err != nil {
96
+ if err := registerer .Register (workspaceDetails ); err != nil {
97
97
return nil , err
98
98
}
99
- // This exists so the prometheus metric exports immediately when set.
100
- // It helps with tests so they don't have to wait for a tick.
101
- workspacesByStatus .WithLabelValues (string (database .ProvisionerJobStatusPending )).Set (0 )
102
- workspacesDetail .WithLabelValues (string (database .ProvisionerJobStatusPending ), "" , "" , "" , "" , "" ).Set (0 )
103
99
104
100
ctx , cancelFunc := context .WithCancel (ctx )
105
101
done := make (chan struct {})
106
102
107
- updateWorkspacesByStatus := func () {
103
+ updateWorkspaceStatuses := func () {
108
104
builds , err := db .GetLatestWorkspaceBuilds (ctx )
109
105
if err != nil {
106
+ if errors .Is (err , sql .ErrNoRows ) {
107
+ // clear all series if there are no database entries
108
+ workspaceStatuses .Reset ()
109
+ }
110
+
110
111
logger .Warn (ctx , "failed to load latest workspace builds" , slog .Error (err ))
111
112
return
112
113
}
@@ -125,24 +126,29 @@ func Workspaces(ctx context.Context, logger slog.Logger, registerer prometheus.R
125
126
return
126
127
}
127
128
128
- workspacesByStatus .Reset ()
129
+ workspaceStatuses .Reset ()
129
130
for _ , job := range jobs {
130
131
status := codersdk .ProvisionerJobStatus (job .JobStatus )
131
- workspacesByStatus .WithLabelValues (string (status )).Add (1 )
132
+ workspaceStatuses .WithLabelValues (string (status )).Add (1 )
132
133
}
133
134
}
134
135
135
- updateWorkspacesDetail := func () {
136
+ updateWorkspaceDetails := func () {
136
137
ws , err := db .GetWorkspaces (ctx , database.GetWorkspacesParams {
137
138
Deleted : false ,
138
139
WithSummary : false ,
139
140
})
140
141
if err != nil {
142
+ if errors .Is (err , sql .ErrNoRows ) {
143
+ // clear all series if there are no database entries
144
+ workspaceDetails .Reset ()
145
+ }
146
+
141
147
logger .Warn (ctx , "failed to load active workspaces" , slog .Error (err ))
142
148
return
143
149
}
144
150
145
- workspacesDetail .Reset ()
151
+ workspaceDetails .Reset ()
146
152
for _ , w := range ws {
147
153
// TODO: there may be a more elegant/idiomatic way to do this?
148
154
buildStatus := string (database .ProvisionerJobStatusUnknown )
@@ -162,8 +168,8 @@ func Workspaces(ctx context.Context, logger slog.Logger, registerer prometheus.R
162
168
doTick := func () {
163
169
defer ticker .Reset (duration )
164
170
165
- updateWorkspacesByStatus ()
166
- updateWorkspacesDetail ()
171
+ updateWorkspaceStatuses ()
172
+ updateWorkspaceDetails ()
167
173
}
168
174
169
175
go func () {
0 commit comments