Files
coder/coderd/x/nats/metrics_test.go
T

125 lines
5.8 KiB
Go

package nats_test
import (
"context"
"testing"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/coder/coder/v2/coderd/database/pubsub"
"github.com/coder/coder/v2/coderd/x/nats"
"github.com/coder/coder/v2/testutil"
)
func TestPubsub_Metrics(t *testing.T) {
t.Parallel()
ctx := testutil.Context(t, testutil.WaitLong)
uut := newPubsub(t, nats.Options{})
registry := prometheus.NewRegistry()
err := registry.Register(uut)
require.NoError(t, err)
// each Gather measures pubsub latency by publishing a message & subscribing to it
var gatherCount float64
metrics, err := registry.Gather()
gatherCount++
require.NoError(t, err)
require.True(t, testutil.PromGaugeHasValue(t, metrics, 0, "coder_nats_pubsub_current_events"))
require.True(t, testutil.PromGaugeHasValue(t, metrics, 0, "coder_nats_pubsub_current_subscribers"))
event := "test"
data := "testing"
messageChannel := make(chan []byte)
unsub0, err := uut.Subscribe(event, func(_ context.Context, message []byte) {
messageChannel <- message
})
require.NoError(t, err)
go func() {
err := uut.Publish(event, []byte(data))
assert.NoError(t, err)
}()
_ = testutil.TryReceive(ctx, t, messageChannel)
require.Eventually(t, func() bool {
latencyBytes := gatherCount * pubsub.LatencyMessageLength
metrics, err = registry.Gather()
gatherCount++
assert.NoError(t, err)
return testutil.PromGaugeHasValue(t, metrics, 1, "coder_nats_pubsub_current_events") &&
testutil.PromGaugeHasValue(t, metrics, 1, "coder_nats_pubsub_current_subscribers") &&
testutil.PromGaugeHasValue(t, metrics, 1, "coder_nats_pubsub_connected") &&
testutil.PromCounterHasValue(t, metrics, gatherCount, "coder_nats_pubsub_publishes_total", "true") &&
testutil.PromCounterHasValue(t, metrics, gatherCount, "coder_nats_pubsub_subscribes_total", "true") &&
testutil.PromCounterHasValue(t, metrics, gatherCount, "coder_nats_pubsub_messages_total", "normal") &&
testutil.PromCounterHasValue(t, metrics, float64(len(data))+latencyBytes, "coder_nats_pubsub_received_bytes_total") &&
testutil.PromCounterHasValue(t, metrics, float64(len(data))+latencyBytes, "coder_nats_pubsub_published_bytes_total") &&
// The latency gauges can be exactly 0 on Windows, whose monotonic
// clock advances in coarse (up to 15.6ms) ticks; a sub-tick publish
// measures a zero duration. An absent gauge reads as -1, so >= 0
// still proves the gauge was gathered, and the measures_total/errs
// counters below prove measurement ran and succeeded.
testutil.PromGaugeAssertion(t, metrics, func(in float64) bool { return in >= 0 }, "coder_nats_pubsub_send_latency_seconds") &&
testutil.PromGaugeAssertion(t, metrics, func(in float64) bool { return in >= 0 }, "coder_nats_pubsub_receive_latency_seconds") &&
testutil.PromCounterHasValue(t, metrics, gatherCount, "coder_nats_pubsub_latency_measures_total") &&
!testutil.PromCounterGathered(t, metrics, "coder_nats_pubsub_latency_measure_errs_total")
}, testutil.WaitShort, testutil.IntervalFast)
colossalSize := 7600
colossalData := make([]byte, colossalSize)
for i := range colossalData {
colossalData[i] = 'q'
}
unsub1, err := uut.Subscribe(event, func(_ context.Context, message []byte) {
messageChannel <- message
})
require.NoError(t, err)
go func() {
err := uut.Publish(event, colossalData)
assert.NoError(t, err)
}()
// should get 2 messages because we have 2 subs
_ = testutil.TryReceive(ctx, t, messageChannel)
_ = testutil.TryReceive(ctx, t, messageChannel)
require.Eventually(t, func() bool {
latencyBytes := gatherCount * pubsub.LatencyMessageLength
metrics, err = registry.Gather()
gatherCount++
assert.NoError(t, err)
return testutil.PromGaugeHasValue(t, metrics, 1, "coder_nats_pubsub_current_events") &&
testutil.PromGaugeHasValue(t, metrics, 2, "coder_nats_pubsub_current_subscribers") &&
testutil.PromGaugeHasValue(t, metrics, 1, "coder_nats_pubsub_connected") &&
testutil.PromCounterHasValue(t, metrics, 1+gatherCount, "coder_nats_pubsub_publishes_total", "true") &&
testutil.PromCounterHasValue(t, metrics, 1+gatherCount, "coder_nats_pubsub_subscribes_total", "true") &&
testutil.PromCounterHasValue(t, metrics, gatherCount, "coder_nats_pubsub_messages_total", "normal") &&
testutil.PromCounterHasValue(t, metrics, 1, "coder_nats_pubsub_messages_total", "colossal") &&
testutil.PromCounterHasValue(t, metrics, float64(colossalSize+len(data))+latencyBytes, "coder_nats_pubsub_received_bytes_total") &&
testutil.PromCounterHasValue(t, metrics, float64(colossalSize+len(data))+latencyBytes, "coder_nats_pubsub_published_bytes_total") &&
// Zero latency is accepted for coarse clocks; see the comment on
// the first Eventually above.
testutil.PromGaugeAssertion(t, metrics, func(in float64) bool { return in >= 0 }, "coder_nats_pubsub_send_latency_seconds") &&
testutil.PromGaugeAssertion(t, metrics, func(in float64) bool { return in >= 0 }, "coder_nats_pubsub_receive_latency_seconds") &&
testutil.PromCounterHasValue(t, metrics, gatherCount, "coder_nats_pubsub_latency_measures_total") &&
!testutil.PromCounterGathered(t, metrics, "coder_nats_pubsub_latency_measure_errs_total")
}, testutil.WaitShort, testutil.IntervalFast)
// Unsubscribing both local subscribers should decrement the
// subscriber gauge back to 0, and removing the last subscriber on the
// event should decrement the event gauge back to 0.
unsub0()
unsub1()
require.Eventually(t, func() bool {
metrics, err = registry.Gather()
gatherCount++
assert.NoError(t, err)
return testutil.PromGaugeHasValue(t, metrics, 0, "coder_nats_pubsub_current_events") &&
testutil.PromGaugeHasValue(t, metrics, 0, "coder_nats_pubsub_current_subscribers")
}, testutil.WaitShort, testutil.IntervalFast)
}