Skip to content

Commit

Permalink
Merge pull request #87 from grobinson-grafana/grobinson/update-silenc…
Browse files Browse the repository at this point in the history
…e-limits
  • Loading branch information
santihernandezc authored Jun 21, 2024
2 parents 70d9d63 + edab848 commit d75ea57
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 16 deletions.
6 changes: 3 additions & 3 deletions cmd/alertmanager/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func run() int {
retention = kingpin.Flag("data.retention", "How long to keep data for.").Default("120h").Duration()
maintenanceInterval = kingpin.Flag("data.maintenance-interval", "Interval between garbage collection and snapshotting to disk of the silences and the notification logs.").Default("15m").Duration()
maxSilences = kingpin.Flag("silences.max-silences", "Maximum number of silences, including expired silences. If negative or zero, no limit is set.").Default("0").Int()
maxPerSilenceBytes = kingpin.Flag("silences.max-per-silence-bytes", "Maximum per silence size in bytes. If negative or zero, no limit is set.").Default("0").Int()
maxSilenceSizeBytes = kingpin.Flag("silences.max-silence-size-bytes", "Maximum silence size in bytes. If negative or zero, no limit is set.").Default("0").Int()
alertGCInterval = kingpin.Flag("alerts.gc-interval", "Interval between alert GC.").Default("30m").Duration()

webConfig = webflag.AddFlags(kingpin.CommandLine, ":9093")
Expand Down Expand Up @@ -261,8 +261,8 @@ func run() int {
SnapshotFile: filepath.Join(*dataDir, "silences"),
Retention: *retention,
Limits: silence.Limits{
MaxSilences: *maxSilences,
MaxPerSilenceBytes: *maxPerSilenceBytes,
MaxSilences: func() int { return *maxSilences },
MaxSilenceSizeBytes: func() int { return *maxSilenceSizeBytes },
},
Logger: log.With(logger, "component", "silences"),
Metrics: prometheus.DefaultRegisterer,
Expand Down
25 changes: 14 additions & 11 deletions silence/silence.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ import (
)

// ErrNotFound is returned if a silence was not found.
var ErrNotFound = fmt.Errorf("silence not found")
var ErrNotFound = errors.New("silence not found")

// ErrInvalidState is returned if the state isn't valid.
var ErrInvalidState = fmt.Errorf("invalid state")
var ErrInvalidState = errors.New("invalid state")

type matcherCache map[*pb.Silence]labels.Matchers

Expand Down Expand Up @@ -206,10 +206,10 @@ type Silences struct {
type Limits struct {
// MaxSilences limits the maximum number of silences, including expired
// silences.
MaxSilences int
// MaxPerSilenceBytes is the maximum size of an individual silence as
MaxSilences func() int
// MaxSilenceSizeBytes is the maximum size of an individual silence as
// stored on disk.
MaxPerSilenceBytes int
MaxSilenceSizeBytes func() int
}

// MaintenanceFunc represents the function to run as part of the periodic maintenance for silences.
Expand Down Expand Up @@ -338,7 +338,7 @@ type Options struct {

func (o *Options) validate() error {
if o.SnapshotFile != "" && o.SnapshotReader != nil {
return fmt.Errorf("only one of SnapshotFile and SnapshotReader must be set")
return errors.New("only one of SnapshotFile and SnapshotReader must be set")
}
return nil
}
Expand Down Expand Up @@ -585,8 +585,11 @@ func (s *Silences) setSilence(sil *pb.Silence, now time.Time, skipValidate bool)
// Check the limit unless the silence has been expired. This is to avoid
// situations where silences cannot be expired after the limit has been
// reduced.
if n := msil.Size(); s.limits.MaxPerSilenceBytes > 0 && n > s.limits.MaxPerSilenceBytes && sil.EndsAt.After(now) {
return fmt.Errorf("silence exceeded maximum size: %d bytes (limit: %d bytes)", n, s.limits.MaxPerSilenceBytes)
if s.limits.MaxSilenceSizeBytes != nil {
n := msil.Size()
if m := s.limits.MaxSilenceSizeBytes(); m > 0 && n > m && sil.EndsAt.After(now) {
return fmt.Errorf("silence exceeded maximum size: %d bytes (limit: %d bytes)", n, m)
}
}

if s.st.merge(msil, now) {
Expand Down Expand Up @@ -645,9 +648,9 @@ func (s *Silences) set(sil *pb.Silence) (string, error) {
}

// If we got here it's either a new silence or a replacing one.
if s.limits.MaxSilences > 0 {
if len(s.st)+1 > s.limits.MaxSilences {
return "", fmt.Errorf("exceeded maximum number of silences: %d (limit: %d)", len(s.st), s.limits.MaxSilences)
if s.limits.MaxSilences != nil {
if m := s.limits.MaxSilences(); m > 0 && len(s.st)+1 > m {
return "", fmt.Errorf("exceeded maximum number of silences: %d (limit: %d)", len(s.st), m)
}
}

Expand Down
22 changes: 20 additions & 2 deletions silence/silence_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,8 +462,8 @@ func TestSilenceSet(t *testing.T) {
func TestSilenceLimits(t *testing.T) {
s, err := New(Options{
Limits: Limits{
MaxSilences: 1,
MaxPerSilenceBytes: 2 << 11, // 4KB
MaxSilences: func() int { return 1 },
MaxSilenceSizeBytes: func() int { return 2 << 11 }, // 4KB
},
})
require.NoError(t, err)
Expand Down Expand Up @@ -535,6 +535,24 @@ func TestSilenceLimits(t *testing.T) {
require.Equal(t, "", id3)
}

func TestSilenceNoLimits(t *testing.T) {
s, err := New(Options{
Limits: Limits{},
})
require.NoError(t, err)

// Insert sil should succeed without error.
sil := &pb.Silence{
Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}},
StartsAt: time.Now(),
EndsAt: time.Now().Add(5 * time.Minute),
Comment: strings.Repeat("c", 2<<9),
}
id, err := s.Set(sil)
require.NoError(t, err)
require.NotEqual(t, "", id)
}

func TestSilenceUpsert(t *testing.T) {
s, err := New(Options{
Retention: time.Hour,
Expand Down

0 comments on commit d75ea57

Please sign in to comment.