Skip to content

Commit 45fbf1c

Browse files
Victoremepuntoclauderajivnathan
authored
Skip unavailable metrics instead of killing the test process (#1305)
When a Prometheus query returns an empty vector (e.g. because a monitored pod was OOMKilled), the metrics gatherer retries for 5 minutes then calls Fatalf, which terminates the entire test run via os.Exit(1). This is disproportionate for a transient condition. - Replace Fatalf with Infof + continue so the current gathering cycle skips the unavailable metric and the next cycle retries - Guard avg() against division by zero when sampleCount is 0 - Add tests for empty vector, zero-sample avg, and ComputeResults with no samples Fixes #1304 🤖 Generated with [Claude Code](https://claude.com/claude-code) Signed-off-by: Víctor M. Múgica <vmugicag@redhat.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: Rajiv Senthilnathan <rajivnathan@gmail.com>
1 parent dbffb75 commit 45fbf1c

2 files changed

Lines changed: 60 additions & 1 deletion

File tree

setup/metrics/gather.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ type aggregateResult struct {
4343
}
4444

4545
func (r aggregateResult) avg() float64 {
46+
if r.sampleCount == 0 {
47+
return 0
48+
}
4649
return r.sum / float64(r.sampleCount)
4750
}
4851

@@ -109,7 +112,8 @@ func (g *Gatherer) StartGathering() chan struct{} {
109112
return metricsErr == nil, nil
110113
})
111114
if err != nil {
112-
g.term.Fatalf(metricsErr, "metrics error")
115+
g.term.Infof("⚠ Skipping metric %q: %s (will retry on next gathering interval)", q.Name(), metricsErr)
116+
continue
113117
}
114118
}
115119
}, g.queryInterval, stop)

setup/metrics/gather_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package metrics
22

33
import (
44
"fmt"
5+
"io"
56
"reflect"
67
"testing"
78

@@ -17,6 +18,18 @@ func TestExecuteQueryAndProcessResult(t *testing.T) {
1718
var testTime = model.Now()
1819

1920
tests := []testcase{
21+
{
22+
query: testQuery{
23+
name: "empty vector",
24+
sample: queryResult{
25+
val: model.Vector{},
26+
},
27+
},
28+
exp: expected{
29+
err: "metrics value could not be retrieved for query empty vector",
30+
resultLen: 0,
31+
},
32+
},
2033
{
2134
query: testQuery{
2235
name: "first sample",
@@ -190,3 +203,45 @@ func (q testQuery) Execute() (model.Value, prometheus.Warnings, error) {
190203
func (q testQuery) ResultType() string {
191204
return "memory"
192205
}
206+
207+
func TestAvgZeroSamples(t *testing.T) {
208+
// given
209+
r := aggregateResult{sampleCount: 0, sum: 0, max: 0}
210+
211+
// when
212+
result := r.avg()
213+
214+
// then
215+
require.InDelta(t, float64(0), result, 0.01)
216+
}
217+
218+
func TestComputeResultsZeroSamples(t *testing.T) {
219+
// given
220+
q := testQuery{name: "zero sample metric"}
221+
g := &Gatherer{
222+
mqueries: []queries.Query{q},
223+
results: map[string]aggregateResult{"zero sample metric": {}},
224+
term: &noopTerminal{},
225+
}
226+
227+
// when
228+
tuples := g.ComputeResults()
229+
230+
// then
231+
require.Len(t, tuples, 2)
232+
require.Equal(t, "Average zero sample metric (MB)", tuples[0][0])
233+
require.Equal(t, "0.00", tuples[0][1])
234+
require.Equal(t, "Max zero sample metric (MB)", tuples[1][0])
235+
require.Equal(t, "0.00", tuples[1][1])
236+
}
237+
238+
type noopTerminal struct{}
239+
240+
func (t *noopTerminal) InOrStdin() io.Reader { return nil }
241+
func (t *noopTerminal) OutOrStdout() io.Writer { return io.Discard }
242+
func (t *noopTerminal) Debugf(msg string, args ...interface{}) {}
243+
func (t *noopTerminal) Infof(msg string, args ...interface{}) {}
244+
func (t *noopTerminal) Errorf(err error, msg string, args ...interface{}) {}
245+
func (t *noopTerminal) Fatalf(err error, msg string, args ...interface{}) { panic("unexpected Fatalf") }
246+
func (t *noopTerminal) PromptBoolf(msg string, args ...interface{}) bool { return false }
247+
func (t *noopTerminal) AddPreFatalExitHook(func()) {}

0 commit comments

Comments
 (0)