Skip to content

Commit

Permalink
Fix range merges
Browse files Browse the repository at this point in the history
  • Loading branch information
prymitive committed Oct 28, 2022
1 parent a07c52a commit 9448eae
Show file tree
Hide file tree
Showing 6 changed files with 111 additions and 12 deletions.
1 change: 1 addition & 0 deletions internal/checks/alerts_count.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func (c AlertsCheck) Check(ctx context.Context, rule parser.Rule, entries []disc
if err != nil {
log.Warn().Err(err).Str("name", c.prom.Name()).Msg("Cannot detect Prometheus uptime gaps")
} else {
// FIXME: gaps are not used
qr.Series.FindGaps(promUptime.Series, qr.Series.From, qr.Series.Until)
}
}
Expand Down
7 changes: 7 additions & 0 deletions internal/checks/alerts_count_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,42 +132,49 @@ func TestAlertsCountCheck(t *testing.T) {
},
resp: matrixResponse{
samples: []*model.SampleStream{
// 7m
generateSampleStream(
map[string]string{"job": "foo"},
time.Now().Add(time.Hour*-24),
time.Now().Add(time.Hour*-24).Add(time.Minute*6),
time.Minute,
),
// 7m
generateSampleStream(
map[string]string{"job": "foo"},
time.Now().Add(time.Hour*-23),
time.Now().Add(time.Hour*-23).Add(time.Minute*6),
time.Minute,
),
// 2m
generateSampleStream(
map[string]string{"job": "foo"},
time.Now().Add(time.Hour*-22),
time.Now().Add(time.Hour*-22).Add(time.Minute),
time.Minute,
),
// 17m
generateSampleStream(
map[string]string{"job": "foo"},
time.Now().Add(time.Hour*-21),
time.Now().Add(time.Hour*-21).Add(time.Minute*16),
time.Minute,
),
// 37m
generateSampleStream(
map[string]string{"job": "foo"},
time.Now().Add(time.Hour*-20),
time.Now().Add(time.Hour*-20).Add(time.Minute*36),
time.Minute,
),
// 37m
generateSampleStream(
map[string]string{"job": "foo"},
time.Now().Add(time.Hour*-19),
time.Now().Add(time.Hour*-19).Add(time.Minute*36),
time.Minute,
),
// 2h1m
generateSampleStream(
map[string]string{"job": "foo"},
time.Now().Add(time.Hour*-10),
Expand Down
5 changes: 4 additions & 1 deletion internal/checks/base_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,7 @@ var (
samples: []*model.SampleStream{
generateSampleStream(
map[string]string{},
time.Now().Add(time.Hour*24),
time.Now().Add(time.Hour*-24),
time.Now(),
time.Minute*5,
),
Expand Down Expand Up @@ -500,6 +500,9 @@ func generateSampleWithValue(labels map[string]string, val float64) *model.Sampl
}

func generateSampleStream(labels map[string]string, from, until time.Time, step time.Duration) (s *model.SampleStream) {
if from.After(until) {
panic(fmt.Sprintf("generateSampleStream() got from > until: %s ~ %s", from.UTC().Format(time.RFC3339), until.UTC().Format(time.RFC3339)))
}
metric := model.Metric{}
for k, v := range labels {
metric[model.LabelName(k)] = model.LabelValue(v)
Expand Down
2 changes: 1 addition & 1 deletion internal/promapi/range.go
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ func (p *Prometheus) RangeQuery(ctx context.Context, expr string, params RangeQu
wg.Done()
}
if len(merged.Series.Ranges) > 1 {
merged.Series.Ranges = MergeRanges(merged.Series.Ranges)
merged.Series.Ranges = MergeRanges(merged.Series.Ranges, step)
}

if lastErr != nil {
Expand Down
10 changes: 8 additions & 2 deletions internal/promapi/range_normalize.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ func (str *SeriesTimeRanges) FindGaps(baseline SeriesTimeRanges, from, until tim
}

// merge [t1:t2] [t2:t3] together
func MergeRanges(dst MetricTimeRanges) MetricTimeRanges {
func MergeRanges(dst MetricTimeRanges, step time.Duration) MetricTimeRanges {
sort.Stable(dst)

toPurge := map[int]struct{}{}
Expand All @@ -139,7 +139,13 @@ func MergeRanges(dst MetricTimeRanges) MetricTimeRanges {
if _, ok = toPurge[j]; ok {
continue
}
if dst[i].Start.Before(dst[j].Start) && !dst[i].End.Before(dst[j].Start) && !dst[i].End.After(dst[j].End) {
// 1. Merge two ranges next to each other
// [s1 ~ e1]
// [s2 - e2]
// 2. Merge two overlapping ranges
// [s1 ~ e1]
// [s2 ~ e2]
if dst[i].Start.Before(dst[j].Start) && !dst[i].End.Before(dst[j].Start.Add(step*-1)) {
dst[i].End = dst[j].End
toPurge[j] = struct{}{}
}
Expand Down
98 changes: 90 additions & 8 deletions internal/promapi/range_normalize_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,84 @@ func TestAppendSampleToRanges(t *testing.T) {
},
},
},
{
in: nil,
samples: []model.SampleStream{
{
Metric: model.Metric{"instance": "1"},
Values: generateSamples(timeParse("2022-10-27T09:14:59Z"), timeParse("2022-10-27T09:20:59Z"), time.Minute),
},
{
Metric: model.Metric{"instance": "1"},
Values: generateSamples(timeParse("2022-10-27T10:14:59Z"), timeParse("2022-10-27T10:20:59Z"), time.Minute),
},
{
Metric: model.Metric{"instance": "1"},
Values: generateSamples(timeParse("2022-10-27T11:14:59Z"), timeParse("2022-10-27T11:15:59Z"), time.Minute),
},
{
Metric: model.Metric{"instance": "1"},
Values: generateSamples(timeParse("2022-10-27T12:14:59Z"), timeParse("2022-10-27T12:30:59Z"), time.Minute),
},
{
Metric: model.Metric{"instance": "1"},
Values: generateSamples(timeParse("2022-10-27T13:14:59Z"), timeParse("2022-10-27T13:50:59Z"), time.Minute),
},
{
Metric: model.Metric{"instance": "1"},
Values: generateSamples(timeParse("2022-10-27T14:14:59Z"), timeParse("2022-10-27T14:50:59Z"), time.Minute),
},
{
Metric: model.Metric{"instance": "1"},
Values: generateSamples(timeParse("2022-10-27T23:14:59Z"), timeParse("2022-10-28T01:14:59Z"), time.Minute),
},
},
step: time.Minute,
out: []promapi.MetricTimeRange{
{
Fingerprint: labels.FromStrings("instance", "1").Hash(),
Labels: labels.FromStrings("instance", "1"),
Start: timeParse("2022-10-27T09:14:59Z"),
End: timeParse("2022-10-27T09:21:59Z"),
},
{
Fingerprint: labels.FromStrings("instance", "1").Hash(),
Labels: labels.FromStrings("instance", "1"),
Start: timeParse("2022-10-27T10:14:59Z"),
End: timeParse("2022-10-27T10:21:59Z"),
},
{
Fingerprint: labels.FromStrings("instance", "1").Hash(),
Labels: labels.FromStrings("instance", "1"),
Start: timeParse("2022-10-27T11:14:59Z"),
End: timeParse("2022-10-27T11:16:59Z"),
},
{
Fingerprint: labels.FromStrings("instance", "1").Hash(),
Labels: labels.FromStrings("instance", "1"),
Start: timeParse("2022-10-27T12:14:59Z"),
End: timeParse("2022-10-27T12:31:59Z"),
},
{
Fingerprint: labels.FromStrings("instance", "1").Hash(),
Labels: labels.FromStrings("instance", "1"),
Start: timeParse("2022-10-27T13:14:59Z"),
End: timeParse("2022-10-27T13:51:59Z"),
},
{
Fingerprint: labels.FromStrings("instance", "1").Hash(),
Labels: labels.FromStrings("instance", "1"),
Start: timeParse("2022-10-27T14:14:59Z"),
End: timeParse("2022-10-27T14:51:59Z"),
},
{
Fingerprint: labels.FromStrings("instance", "1").Hash(),
Labels: labels.FromStrings("instance", "1"),
Start: timeParse("2022-10-27T23:14:59Z"),
End: timeParse("2022-10-28T01:15:59Z"),
},
},
},
}

for i, tc := range testCases {
Expand All @@ -233,7 +311,7 @@ func TestAppendSampleToRanges(t *testing.T) {
lset := promapi.MetricToLabels(s.Metric)
tc.in = promapi.AppendSampleToRanges(tc.in, lset, s.Values, tc.step)
}
tc.in = promapi.MergeRanges(tc.in)
tc.in = promapi.MergeRanges(tc.in, tc.step)
sort.Stable(tc.in)
require.Equal(t, printRange(tc.out), printRange(tc.in))
})
Expand All @@ -242,8 +320,9 @@ func TestAppendSampleToRanges(t *testing.T) {

func TestMergeRanges(t *testing.T) {
type testCaseT struct {
in promapi.MetricTimeRanges
out promapi.MetricTimeRanges
in promapi.MetricTimeRanges
out promapi.MetricTimeRanges
step time.Duration
}

timeParse := func(s string) time.Time {
Expand All @@ -264,12 +343,14 @@ func TestMergeRanges(t *testing.T) {

testCases := []testCaseT{
{
in: nil,
out: nil,
in: nil,
out: nil,
step: time.Minute,
},
{
in: promapi.MetricTimeRanges{},
out: promapi.MetricTimeRanges{},
in: promapi.MetricTimeRanges{},
out: promapi.MetricTimeRanges{},
step: time.Minute,
},
{
in: promapi.MetricTimeRanges{
Expand Down Expand Up @@ -299,12 +380,13 @@ func TestMergeRanges(t *testing.T) {
out: promapi.MetricTimeRanges{
{Fingerprint: labels.EmptyLabels().Hash(), Labels: labels.EmptyLabels(), Start: timeParse("2022-10-19T10:50:44Z"), End: timeParse("2022-10-26T10:55:44Z")},
},
step: time.Minute,
},
}

for i, tc := range testCases {
t.Run(strconv.Itoa(i), func(t *testing.T) {
out := promapi.MergeRanges(tc.in)
out := promapi.MergeRanges(tc.in, tc.step)
sort.Stable(out)
require.Equal(t, printRange(tc.out), printRange(out))
})
Expand Down

0 comments on commit 9448eae

Please sign in to comment.