From b14b27b78b3a69742f90c121166dbb1280e5924f Mon Sep 17 00:00:00 2001 From: clawbot <35+clawbot@noreply.example.org> Date: Fri, 2 Oct 2026 12:45:05 +0200 Subject: [PATCH] Serve the delivery duration histogram from boot (closes #267) The delivery duration histogram was registered only when a delivery first ran, so until then /metrics had no series for it and a dashboard or alert on it saw nothing at all rather than zero. Each of the four target types now has its series registered at boot with zero counts, so the histogram is served from the first scrape. Tests pin that all four series are present before any delivery, on the metrics registry and through the production router. Model: opus-5-5 --- internal/metrics/metrics.go | 1 + internal/metrics/metrics_test.go | 1 + internal/server/routes_test.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index 1d8f8f3..3658d9c 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -384,6 +384,7 @@ func (s *Set) initSeries() { s.deliveriesFailed.WithLabelValues(label) s.deliveryRetries.WithLabelValues(label) s.deliveryReplays.WithLabelValues(label) + s.deliveryDuration.WithLabelValues(label) s.deliveriesPending.WithLabelValues(label) s.deliveriesRetrying.WithLabelValues(label) s.circuitBreakersOpen.WithLabelValues(label) diff --git a/internal/metrics/metrics_test.go b/internal/metrics/metrics_test.go index a8b3a64..d89ea73 100644 --- a/internal/metrics/metrics_test.go +++ b/internal/metrics/metrics_test.go @@ -167,6 +167,7 @@ func TestKnownSeriesExistBeforeAnyDelivery(t *testing.T) { "webhooker_deliveries_succeeded_total", "webhooker_deliveries_failed_total", "webhooker_delivery_retries_total", + "webhooker_delivery_duration_seconds", "webhooker_circuit_breakers_open", } { assert.ElementsMatch(t, diff --git a/internal/server/routes_test.go b/internal/server/routes_test.go index f25eb86..44304f5 100644 --- a/internal/server/routes_test.go +++ b/internal/server/routes_test.go @@ -1580,3 +1580,31 @@ func TestTwoMetricsRoutersInOneProcess(t *testing.T) { } } } + +// TestMetricsScrapeBeforeAnyDelivery pins +// https://git.eeqj.de/sneak/webhooker/issues/267: an instance that +// has delivered nothing must still serve the delivery duration +// histogram, at zero, for every target type. +func TestMetricsScrapeBeforeAnyDelivery(t *testing.T) { + t.Parallel() + + env := newTestEnvWithConfig( + t, metricsConfig(t, metricsUser, metricsAuthValue), + ) + + scrape := env.metricsRequest(metricsUser, metricsAuthValue) + require.Equal(t, http.StatusOK, scrape.Code) + + for _, targetType := range []database.TargetType{ + database.TargetTypeHTTP, + database.TargetTypeDatabase, + database.TargetTypeLog, + database.TargetTypeSlack, + } { + assert.Contains( + t, scrape.Body.String(), + `webhooker_delivery_duration_seconds_count{target_type="`+ + string(targetType)+`"} 0`, + ) + } +}