@@ -2,6 +2,7 @@ package prebuilds
22
33import (
44"context"
5+ "fmt"
56"sync/atomic"
67"time"
78
@@ -11,7 +12,6 @@ import (
1112"cdr.dev/slog"
1213
1314"github.com/coder/coder/v2/coderd/database"
14- "github.com/coder/coder/v2/coderd/database/dbauthz"
1515"github.com/coder/coder/v2/coderd/prebuilds"
1616)
1717
5757labels ,
5858nil ,
5959)
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+ )
6066)
6167
6268const (
@@ -74,7 +80,6 @@ type MetricsCollector struct {
7480
7581var _ prometheus.Collector = new (MetricsCollector )
7682
77- // NewMetricsCollector returns a
7883func NewMetricsCollector (db database.Store ,logger slog.Logger ,snapshotter prebuilds.StateSnapshotter )* MetricsCollector {
7984log := logger .Named ("prebuilds_metrics_collector" )
8085return & MetricsCollector {
@@ -91,18 +96,16 @@ func (*MetricsCollector) Describe(descCh chan<- *prometheus.Desc) {
9196descCh <- desiredPrebuildsDesc
9297descCh <- runningPrebuildsDesc
9398descCh <- eligiblePrebuildsDesc
99+ descCh <- lastUpdateDesc
94100}
95101
96102// Collect uses the cached state to set configured metrics.
97103// The state is cached because this function can be called multiple times per second and retrieving the current state
98104// is an expensive operation.
99105func (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.
104107if 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" )
106109return
107110}
108111
@@ -119,7 +122,7 @@ func (mc *MetricsCollector) Collect(metricsCh chan<- prometheus.Metric) {
119122
120123presetSnapshot ,err := currentState .snapshot .FilterByPreset (preset .ID )
121124if 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 ))
123126continue
124127}
125128state := presetSnapshot .CalculateState ()
@@ -128,11 +131,14 @@ func (mc *MetricsCollector) Collect(metricsCh chan<- prometheus.Metric) {
128131metricsCh <- prometheus .MustNewConstMetric (runningPrebuildsDesc ,prometheus .GaugeValue ,float64 (state .Actual ),preset .TemplateName ,preset .Name ,preset .OrganizationName )
129132metricsCh <- prometheus .MustNewConstMetric (eligiblePrebuildsDesc ,prometheus .GaugeValue ,float64 (state .Eligible ),preset .TemplateName ,preset .Name ,preset .OrganizationName )
130133}
134+
135+ metricsCh <- prometheus .MustNewConstMetric (lastUpdateDesc ,prometheus .GaugeValue ,float64 (currentState .createdAt .Unix ()))
131136}
132137
133138type state struct {
134139prebuildMetrics []database.GetPrebuildMetricsRow
135140snapshot * prebuilds.GlobalSnapshot
141+ createdAt time.Time
136142}
137143
138144// BackgroundFetch updates the metrics state every given interval.
@@ -157,6 +163,7 @@ func (mc *MetricsCollector) BackgroundFetch(ctx context.Context, updateInterval,
157163
158164// UpdateState builds the current metrics state.
159165func (mc * MetricsCollector )UpdateState (ctx context.Context ,timeout time.Duration )error {
166+ start := time .Now ()
160167mc .logger .Debug (ctx ,"fetching prebuilds metrics state" )
161168fetchCtx ,fetchCancel := context .WithTimeout (ctx ,timeout )
162169defer fetchCancel ()
@@ -170,11 +177,12 @@ func (mc *MetricsCollector) UpdateState(ctx context.Context, timeout time.Durati
170177if err != nil {
171178return xerrors .Errorf ("snapshot state: %w" ,err )
172179}
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 ())) )
174181
175182mc .latestState .Store (& state {
176183prebuildMetrics :prebuildMetrics ,
177184snapshot :snapshot ,
185+ createdAt :time .Now (),
178186})
179187return nil
180188}