@@ -2,6 +2,7 @@ package prometheusmetrics_test
2
2
3
3
import (
4
4
"context"
5
+ "sync/atomic"
5
6
"testing"
6
7
"time"
7
8
@@ -13,6 +14,7 @@ import (
13
14
"cdr.dev/slog/sloggers/slogtest"
14
15
"github.com/coder/coder/coderd/prometheusmetrics"
15
16
"github.com/coder/coder/codersdk/agentsdk"
17
+ "github.com/coder/coder/cryptorand"
16
18
"github.com/coder/coder/testutil"
17
19
)
18
20
@@ -175,3 +177,86 @@ func TestUpdateMetrics_MetricsExpire(t *testing.T) {
175
177
return len (actual ) == 0
176
178
}, testutil .WaitShort , testutil .IntervalFast )
177
179
}
180
+
181
+ func Benchmark_MetricsAggregator_Run (b * testing.B ) {
182
+ // Number of metrics to generate and send in each iteration.
183
+ // Hard-coded to 1024 to avoid overflowing the queue in the metrics aggregator.
184
+ numMetrics := 1024
185
+
186
+ // given
187
+ registry := prometheus .NewRegistry ()
188
+ metricsAggregator := must (prometheusmetrics .NewMetricsAggregator (
189
+ slogtest .Make (b , & slogtest.Options {IgnoreErrors : true }),
190
+ registry ,
191
+ time .Hour ,
192
+ ))
193
+
194
+ ctx , cancelFunc := context .WithCancel (context .Background ())
195
+ b .Cleanup (cancelFunc )
196
+
197
+ closeFunc := metricsAggregator .Run (ctx )
198
+ b .Cleanup (closeFunc )
199
+
200
+ ch := make (chan prometheus.Metric )
201
+ go func () {
202
+ for {
203
+ select {
204
+ case <- ctx .Done ():
205
+ return
206
+ default :
207
+ metricsAggregator .Collect (ch )
208
+ }
209
+ }
210
+ }()
211
+
212
+ for i := 0 ; i < b .N ; i ++ {
213
+ b .StopTimer ()
214
+ b .Logf ("N=%d generating %d metrics" , b .N , numMetrics )
215
+ metrics := make ([]agentsdk.AgentMetric , 0 , numMetrics )
216
+ for i := 0 ; i < numMetrics ; i ++ {
217
+ metrics = append (metrics , genAgentMetric (b ))
218
+ }
219
+
220
+ b .Logf ("N=%d sending %d metrics" , b .N , numMetrics )
221
+ var nGot atomic.Int64
222
+ b .StartTimer ()
223
+ metricsAggregator .Update (ctx , testUsername , testWorkspaceName , testAgentName , metrics )
224
+ for i := 0 ; i < numMetrics ; i ++ {
225
+ select {
226
+ case <- ctx .Done ():
227
+ b .FailNow ()
228
+ case <- ch :
229
+ nGot .Add (1 )
230
+ }
231
+ }
232
+ b .StopTimer ()
233
+ b .Logf ("N=%d got %d metrics" , b .N , nGot .Load ())
234
+ }
235
+ }
236
+
237
+ func genAgentMetric (t testing.TB ) agentsdk.AgentMetric {
238
+ t .Helper ()
239
+
240
+ var metricType agentsdk.AgentMetricType
241
+ if must (cryptorand .Float64 ()) >= 0.5 {
242
+ metricType = agentsdk .AgentMetricTypeCounter
243
+ } else {
244
+ metricType = agentsdk .AgentMetricTypeGauge
245
+ }
246
+
247
+ // Ensure that metric name does not start or end with underscore, as it is not allowed by Prometheus.
248
+ metricName := "metric_" + must (cryptorand .StringCharset (cryptorand .Alpha , 80 )) + "_gen"
249
+ // Generate random metric value between 0 and 1000.
250
+ metricValue := must (cryptorand .Float64 ()) * float64 (must (cryptorand .Intn (1000 )))
251
+
252
+ return agentsdk.AgentMetric {
253
+ Name : metricName , Type : metricType , Value : metricValue , Labels : []agentsdk.AgentMetricLabel {},
254
+ }
255
+ }
256
+
257
+ func must [T any ](t T , err error ) T {
258
+ if err != nil {
259
+ panic (err )
260
+ }
261
+ return t
262
+ }
0 commit comments