Skip to content

Commit 4e027b7

Browse files
Fix limits cause incomplete update for existing silences
This commit fixes a bug where limits can cause an incomplete update for existing silences, where the old silence can be expired but the new silence cannot be created because it would execeed the maximum number of silences. Signed-off-by: George Robinson <[email protected]>
1 parent 36d653a commit 4e027b7

File tree

2 files changed

+54
-22
lines changed

2 files changed

+54
-22
lines changed

silence/silence.go

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -612,16 +612,8 @@ func (s *Silences) Set(sil *pb.Silence) error {
612612
return ErrNotFound
613613
}
614614

615-
if ok {
616-
if canUpdate(prev, sil, now) {
617-
return s.setSilence(sil, now, false)
618-
}
619-
if getState(prev, s.nowUTC()) != types.SilenceStateExpired {
620-
// We cannot update the silence, expire the old one.
621-
if err := s.expire(prev.Id); err != nil {
622-
return fmt.Errorf("expire previous silence: %w", err)
623-
}
624-
}
615+
if ok && canUpdate(prev, sil, now) {
616+
return s.setSilence(sil, now, false)
625617
}
626618

627619
// If we got here it's either a new silence or a replacing one.
@@ -631,6 +623,13 @@ func (s *Silences) Set(sil *pb.Silence) error {
631623
}
632624
}
633625

626+
if ok && getState(prev, s.nowUTC()) != types.SilenceStateExpired {
627+
// We cannot update the silence, expire the old one.
628+
if err := s.expire(prev.Id); err != nil {
629+
return fmt.Errorf("expire previous silence: %w", err)
630+
}
631+
}
632+
634633
uid, err := uuid.NewV4()
635634
if err != nil {
636635
return fmt.Errorf("generate uuid: %w", err)

silence/silence_test.go

Lines changed: 45 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ func TestSilenceLimits(t *testing.T) {
475475
// Insert sil2 should fail because maximum number of silences
476476
// has been exceeded.
477477
sil2 := &pb.Silence{
478-
Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}},
478+
Matchers: []*pb.Matcher{{Name: "c", Pattern: "d"}},
479479
StartsAt: time.Now(),
480480
EndsAt: time.Now().Add(5 * time.Minute),
481481
}
@@ -492,10 +492,7 @@ func TestSilenceLimits(t *testing.T) {
492492
require.NoError(t, s.Set(sil2))
493493
require.NotEqual(t, "", sil2.Id)
494494

495-
// Should be able to update sil2 without hitting the limit.
496-
require.NoError(t, s.Set(sil2))
497-
498-
// Expire sil2.
495+
// Expire sil2 and run the GC.
499496
require.NoError(t, s.Expire(sil2.Id))
500497
n, err = s.GC()
501498
require.NoError(t, err)
@@ -505,16 +502,16 @@ func TestSilenceLimits(t *testing.T) {
505502
sil3 := &pb.Silence{
506503
Matchers: []*pb.Matcher{
507504
{
508-
Name: strings.Repeat("a", 2<<9),
509-
Pattern: strings.Repeat("b", 2<<9),
505+
Name: strings.Repeat("e", 2<<9),
506+
Pattern: strings.Repeat("f", 2<<9),
510507
},
511508
{
512-
Name: strings.Repeat("c", 2<<9),
513-
Pattern: strings.Repeat("d", 2<<9),
509+
Name: strings.Repeat("g", 2<<9),
510+
Pattern: strings.Repeat("h", 2<<9),
514511
},
515512
},
516-
CreatedBy: strings.Repeat("e", 2<<9),
517-
Comment: strings.Repeat("f", 2<<9),
513+
CreatedBy: strings.Repeat("i", 2<<9),
514+
Comment: strings.Repeat("j", 2<<9),
518515
StartsAt: time.Now(),
519516
EndsAt: time.Now().Add(5 * time.Minute),
520517
}
@@ -523,8 +520,44 @@ func TestSilenceLimits(t *testing.T) {
523520
// Do not check the exact size as it can change between consecutive runs
524521
// due to padding.
525522
require.Contains(t, err.Error(), "silence exceeded maximum size")
526-
// TODO: Once we fix https://github.com/prometheus/alertmanager/issues/3878 this should be require.Equal.
527523
require.NotEqual(t, "", sil3.Id)
524+
525+
// Should be able to insert sil4 and then update it without modifications.
526+
sil4 := &pb.Silence{
527+
Matchers: []*pb.Matcher{{Name: "k", Pattern: "l"}},
528+
StartsAt: time.Now(),
529+
EndsAt: time.Now().Add(5 * time.Minute),
530+
}
531+
require.NoError(t, s.Set(sil4))
532+
require.NotEqual(t, "", sil4.Id)
533+
534+
// Update without modifications.
535+
id4 := sil4.Id
536+
require.NoError(t, s.Set(sil4))
537+
require.Equal(t, id4, sil4.Id)
538+
539+
// Should be able to update the comment.
540+
sil5 := cloneSilence(sil4)
541+
sil5.Comment = "m"
542+
require.NoError(t, s.Set(sil5))
543+
require.Equal(t, sil4.Id, sil5.Id)
544+
545+
// Should not be able to update the start and end time as this requires
546+
// sil5 to be expired and a new silence to be created. However, this would
547+
// exceed the maximum number of silences, which counts both active and
548+
// expired silences.
549+
sil6 := cloneSilence(sil5)
550+
sil6.StartsAt = time.Now().Add(5 * time.Minute)
551+
sil6.EndsAt = time.Now().Add(10 * time.Minute)
552+
err = s.Set(sil6)
553+
require.Error(t, err)
554+
require.EqualError(t, err, "exceeded maximum number of silences: 1 (limit: 1)")
555+
require.NotEqual(t, "", sil6.Id)
556+
557+
// sil5 should not be expired because the update failed.
558+
sil5, err = s.QueryOne(QIDs(sil5.Id))
559+
require.NoError(t, err)
560+
require.Equal(t, types.SilenceStateActive, getState(sil5, s.nowUTC()))
528561
}
529562

530563
func TestSilenceNoLimits(t *testing.T) {

0 commit comments

Comments
 (0)