@@ -2,6 +2,7 @@ package prebuilds
2
2
3
3
import (
4
4
"context"
5
+ "fmt"
5
6
"sync/atomic"
6
7
"time"
7
8
@@ -11,7 +12,6 @@ import (
11
12
"cdr.dev/slog"
12
13
13
14
"github.com/coder/coder/v2/coderd/database"
14
- "github.com/coder/coder/v2/coderd/database/dbauthz"
15
15
"github.com/coder/coder/v2/coderd/prebuilds"
16
16
)
17
17
57
57
labels ,
58
58
nil ,
59
59
)
60
+ lastUpdateDesc = prometheus .NewDesc (
61
+ "coderd_prebuilt_workspaces_metrics_last_updated" ,
62
+ "The unix timestamp when the metrics related to prebuilt workspaces were last updated; these metrics are cached." ,
63
+ []string {},
64
+ nil ,
65
+ )
60
66
)
61
67
62
68
const (
@@ -74,7 +80,6 @@ type MetricsCollector struct {
74
80
75
81
var _ prometheus.Collector = new (MetricsCollector )
76
82
77
- // NewMetricsCollector returns a
78
83
func NewMetricsCollector (db database.Store , logger slog.Logger , snapshotter prebuilds.StateSnapshotter ) * MetricsCollector {
79
84
log := logger .Named ("prebuilds_metrics_collector" )
80
85
return & MetricsCollector {
@@ -91,18 +96,16 @@ func (*MetricsCollector) Describe(descCh chan<- *prometheus.Desc) {
91
96
descCh <- desiredPrebuildsDesc
92
97
descCh <- runningPrebuildsDesc
93
98
descCh <- eligiblePrebuildsDesc
99
+ descCh <- lastUpdateDesc
94
100
}
95
101
96
102
// Collect uses the cached state to set configured metrics.
97
103
// The state is cached because this function can be called multiple times per second and retrieving the current state
98
104
// is an expensive operation.
99
105
func (mc * MetricsCollector ) Collect (metricsCh chan <- prometheus.Metric ) {
100
- // nolint:gocritic // We need to set an authz context to read metrics from the db.
101
- ctx := dbauthz .AsPrebuildsOrchestrator (context .Background ())
102
-
103
- currentState := mc .latestState .Load ()
106
+ currentState := mc .latestState .Load () // Grab a copy; it's ok if it goes stale during the course of this func.
104
107
if currentState == nil {
105
- mc .logger .Warn (ctx , "failed to set prebuilds metrics; state not set" )
108
+ mc .logger .Warn (context . Background () , "failed to set prebuilds metrics; state not set" )
106
109
return
107
110
}
108
111
@@ -119,7 +122,7 @@ func (mc *MetricsCollector) Collect(metricsCh chan<- prometheus.Metric) {
119
122
120
123
presetSnapshot , err := currentState .snapshot .FilterByPreset (preset .ID )
121
124
if err != nil {
122
- mc .logger .Error (ctx , "failed to filter by preset" , slog .Error (err ))
125
+ mc .logger .Error (context . Background () , "failed to filter by preset" , slog .Error (err ))
123
126
continue
124
127
}
125
128
state := presetSnapshot .CalculateState ()
@@ -128,11 +131,14 @@ func (mc *MetricsCollector) Collect(metricsCh chan<- prometheus.Metric) {
128
131
metricsCh <- prometheus .MustNewConstMetric (runningPrebuildsDesc , prometheus .GaugeValue , float64 (state .Actual ), preset .TemplateName , preset .Name , preset .OrganizationName )
129
132
metricsCh <- prometheus .MustNewConstMetric (eligiblePrebuildsDesc , prometheus .GaugeValue , float64 (state .Eligible ), preset .TemplateName , preset .Name , preset .OrganizationName )
130
133
}
134
+
135
+ metricsCh <- prometheus .MustNewConstMetric (lastUpdateDesc , prometheus .GaugeValue , float64 (currentState .createdAt .Unix ()))
131
136
}
132
137
133
138
type state struct {
134
139
prebuildMetrics []database.GetPrebuildMetricsRow
135
140
snapshot * prebuilds.GlobalSnapshot
141
+ createdAt time.Time
136
142
}
137
143
138
144
// BackgroundFetch updates the metrics state every given interval.
@@ -157,6 +163,7 @@ func (mc *MetricsCollector) BackgroundFetch(ctx context.Context, updateInterval,
157
163
158
164
// UpdateState builds the current metrics state.
159
165
func (mc * MetricsCollector ) UpdateState (ctx context.Context , timeout time.Duration ) error {
166
+ start := time .Now ()
160
167
mc .logger .Debug (ctx , "fetching prebuilds metrics state" )
161
168
fetchCtx , fetchCancel := context .WithTimeout (ctx , timeout )
162
169
defer fetchCancel ()
@@ -170,11 +177,12 @@ func (mc *MetricsCollector) UpdateState(ctx context.Context, timeout time.Durati
170
177
if err != nil {
171
178
return xerrors .Errorf ("snapshot state: %w" , err )
172
179
}
173
- mc .logger .Debug (ctx , "fetched prebuilds metrics state" )
180
+ mc .logger .Debug (ctx , "fetched prebuilds metrics state" , slog . F ( "duration_secs" , fmt . Sprintf ( "%.2f" , time . Since ( start ). Seconds ())) )
174
181
175
182
mc .latestState .Store (& state {
176
183
prebuildMetrics : prebuildMetrics ,
177
184
snapshot : snapshot ,
185
+ createdAt : time .Now (),
178
186
})
179
187
return nil
180
188
}
0 commit comments