Skip to content

Commit 314c643

Browse files
committed
When existing alert grows and doesn't fit the size limit anymore, it is rejected.
Signed-off-by: Peter Štibraný <[email protected]>
1 parent 42ff3b1 commit 314c643

File tree

2 files changed

+37
-8
lines changed

2 files changed

+37
-8
lines changed

pkg/alertmanager/alertmanager.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -646,26 +646,26 @@ func (a *alertsLimiter) PreStore(alert *types.Alert, existing bool) error {
646646
return nil
647647
}
648648

649-
// We allow existing alerts in with no checks, as we want to make sure that alerts already in
650-
// store can be resolved.
651-
if existing {
652-
return nil
653-
}
649+
fp := alert.Fingerprint()
654650

655651
countLimit := a.limits.AlertmanagerMaxAlertsCount(a.tenant)
656652
sizeLimit := a.limits.AlertmanagerMaxAlertsSizeBytes(a.tenant)
657653

658-
newSize := alertSize(alert.Alert)
654+
sizeDiff := alertSize(alert.Alert)
659655

660656
a.mx.Lock()
661657
defer a.mx.Unlock()
662658

663-
if countLimit > 0 && (a.count+1) > countLimit {
659+
if !existing && countLimit > 0 && (a.count+1) > countLimit {
664660
a.failureCounter.Inc()
665661
return fmt.Errorf(errTooManyAlerts, countLimit)
666662
}
667663

668-
if sizeLimit > 0 && (a.totalSize+newSize) > sizeLimit {
664+
if existing {
665+
sizeDiff -= a.sizes[fp]
666+
}
667+
668+
if sizeLimit > 0 && (a.totalSize+sizeDiff) > sizeLimit {
669669
a.failureCounter.Inc()
670670
return fmt.Errorf(errAlertsTooBig, sizeLimit)
671671
}

pkg/alertmanager/alertmanager_test.go

+29
Original file line numberDiff line numberDiff line change
@@ -155,22 +155,32 @@ func TestAlertsLimiterWithNoLimits(t *testing.T) {
155155
}
156156

157157
func TestAlertsLimiterWithCountLimit(t *testing.T) {
158+
alert2WithMoreAnnotations := alert2
159+
alert2WithMoreAnnotations.Annotations = model.LabelSet{"job": "test", "cluster": "prod", "new": "super-long-annotation"}
160+
alert2WithMoreAnnotationsSize := alertSize(alert2WithMoreAnnotations)
161+
158162
ops := []callbackOp{
159163
{alert: &types.Alert{Alert: alert1}, existing: false, expectedCount: 1, expectedTotalSize: alert1Size},
160164
{alert: &types.Alert{Alert: alert2}, existing: false, expectedInsertError: fmt.Errorf(errTooManyAlerts, 1), expectedCount: 1, expectedTotalSize: alert1Size},
161165
{alert: &types.Alert{Alert: alert1}, delete: true, expectedCount: 0, expectedTotalSize: 0},
162166

163167
{alert: &types.Alert{Alert: alert2}, existing: false, expectedCount: 1, expectedTotalSize: alert2Size},
168+
// Update of existing alert works -- doesn't change count.
169+
{alert: &types.Alert{Alert: alert2WithMoreAnnotations}, existing: true, expectedCount: 1, expectedTotalSize: alert2WithMoreAnnotationsSize},
164170
{alert: &types.Alert{Alert: alert2}, delete: true, expectedCount: 0, expectedTotalSize: 0},
165171
}
166172

167173
testLimiter(t, &mockAlertManagerLimits{maxAlertsCount: 1}, ops)
168174
}
169175

170176
func TestAlertsLimiterWithSizeLimit(t *testing.T) {
177+
alert2WithMoreAnnotations := alert2
178+
alert2WithMoreAnnotations.Annotations = model.LabelSet{"job": "test", "cluster": "prod", "new": "super-long-annotation"}
179+
171180
ops := []callbackOp{
172181
{alert: &types.Alert{Alert: alert1}, existing: false, expectedCount: 1, expectedTotalSize: alert1Size},
173182
{alert: &types.Alert{Alert: alert2}, existing: false, expectedInsertError: fmt.Errorf(errAlertsTooBig, alert2Size), expectedCount: 1, expectedTotalSize: alert1Size},
183+
{alert: &types.Alert{Alert: alert2WithMoreAnnotations}, existing: false, expectedInsertError: fmt.Errorf(errAlertsTooBig, alert2Size), expectedCount: 1, expectedTotalSize: alert1Size},
174184
{alert: &types.Alert{Alert: alert1}, delete: true, expectedCount: 0, expectedTotalSize: 0},
175185

176186
{alert: &types.Alert{Alert: alert2}, existing: false, expectedCount: 1, expectedTotalSize: alert2Size},
@@ -183,6 +193,25 @@ func TestAlertsLimiterWithSizeLimit(t *testing.T) {
183193
testLimiter(t, &mockAlertManagerLimits{maxAlertsSizeBytes: alert2Size}, ops)
184194
}
185195

196+
func TestAlertsLimiterWithSizeLimitAndAnnotationUpdate(t *testing.T) {
197+
alert2WithMoreAnnotations := alert2
198+
alert2WithMoreAnnotations.Annotations = model.LabelSet{"job": "test", "cluster": "prod", "new": "super-long-annotation"}
199+
alert2WithMoreAnnotationsSize := alertSize(alert2WithMoreAnnotations)
200+
201+
// Updating alert with larger annotation that goes over the size limit fails.
202+
testLimiter(t, &mockAlertManagerLimits{maxAlertsSizeBytes: alert2Size}, []callbackOp{
203+
{alert: &types.Alert{Alert: alert2}, existing: false, expectedCount: 1, expectedTotalSize: alert2Size},
204+
{alert: &types.Alert{Alert: alert2WithMoreAnnotations}, existing: true, expectedInsertError: fmt.Errorf(errAlertsTooBig, alert2Size), expectedCount: 1, expectedTotalSize: alert2Size},
205+
})
206+
207+
// Updating alert with larger annotations in the limit works fine.
208+
testLimiter(t, &mockAlertManagerLimits{maxAlertsSizeBytes: alert2WithMoreAnnotationsSize}, []callbackOp{
209+
{alert: &types.Alert{Alert: alert2}, existing: false, expectedCount: 1, expectedTotalSize: alert2Size},
210+
{alert: &types.Alert{Alert: alert2WithMoreAnnotations}, existing: true, expectedCount: 1, expectedTotalSize: alert2WithMoreAnnotationsSize},
211+
{alert: &types.Alert{Alert: alert2}, existing: true, expectedCount: 1, expectedTotalSize: alert2Size},
212+
})
213+
}
214+
186215
// testLimiter sends sequence of alerts to limiter, and checks if limiter updated reacted correctly.
187216
func testLimiter(t *testing.T, limits Limits, ops []callbackOp) {
188217
reg := prometheus.NewPedanticRegistry()

0 commit comments

Comments
 (0)