Support Noop limits for silences

This commit changes how limits work for silences. Instead of checking
if a limit is non-nil, it instead adds support for a set of no-op
limits called NoopLimits.

It changes the limits in Options and Silences from Limits to *Limits
because Go cannot do comparisons on structs containing function
values, so we must use a nil check to see if limits are set in the
options or not.

Signed-off-by: George Robinson <george.robinson@grafana.com>
This commit is contained in:
George Robinson 2024-06-25 15:09:31 +01:00
parent 4c1a187fef
commit 404a4afa11
3 changed files with 22 additions and 15 deletions

View File

@ -271,7 +271,7 @@ func run() int {
silenceOpts := silence.Options{
SnapshotFile: filepath.Join(*dataDir, "silences"),
Retention: *retention,
Limits: silence.Limits{
Limits: &silence.Limits{
MaxSilences: func() int { return *maxSilences },
MaxSilenceSizeBytes: func() int { return *maxSilenceSizeBytes },
},

View File

@ -193,7 +193,7 @@ type Silences struct {
logger log.Logger
metrics *metrics
retention time.Duration
limits Limits
limits *Limits
mtx sync.RWMutex
st state
@ -202,6 +202,12 @@ type Silences struct {
mc matcherCache
}
// NoopLimits are the default limits. All limits are disabled.
var NoopLimits = &Limits{
MaxSilences: func() int { return 0 },
MaxSilenceSizeBytes: func() int { return 0 },
}
// Limits contains the limits for silences.
type Limits struct {
// MaxSilences limits the maximum number of silences, including expired
@ -329,7 +335,7 @@ type Options struct {
// Retention time for newly created Silences. Silences may be
// garbage collected after the given duration after they ended.
Retention time.Duration
Limits Limits
Limits *Limits
// A logger used by background processing.
Logger log.Logger
@ -354,7 +360,7 @@ func New(o Options) (*Silences, error) {
mc: matcherCache{},
logger: log.NewNopLogger(),
retention: o.Retention,
limits: o.Limits,
limits: NoopLimits,
broadcast: func([]byte) {},
st: state{},
}
@ -364,6 +370,10 @@ func New(o Options) (*Silences, error) {
s.logger = o.Logger
}
if o.Limits != nil {
s.limits = o.Limits
}
if o.SnapshotFile != "" {
if r, err := os.Open(o.SnapshotFile); err != nil {
if !os.IsNotExist(err) {
@ -551,11 +561,9 @@ func cloneSilence(sil *pb.Silence) *pb.Silence {
}
func (s *Silences) checkSizeLimits(msil *pb.MeshSilence) error {
if s.limits.MaxSilenceSizeBytes != nil {
n := msil.Size()
if m := s.limits.MaxSilenceSizeBytes(); m > 0 && n > m {
return fmt.Errorf("silence exceeded maximum size: %d bytes (limit: %d bytes)", n, m)
}
n := msil.Size()
if m := s.limits.MaxSilenceSizeBytes(); m > 0 && n > m {
return fmt.Errorf("silence exceeded maximum size: %d bytes (limit: %d bytes)", n, m)
}
return nil
}
@ -619,10 +627,8 @@ func (s *Silences) Set(sil *pb.Silence) error {
// If we got here it's either a new silence or a replacing one (which would
// also create a new silence) so we need to make sure we have capacity for
// the new silence.
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)
}
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)
}
uid, err := uuid.NewV4()

View File

@ -485,7 +485,7 @@ func TestSilenceSet(t *testing.T) {
func TestSilenceLimits(t *testing.T) {
s, err := New(Options{
Limits: Limits{
Limits: &Limits{
MaxSilences: func() int { return 1 },
MaxSilenceSizeBytes: func() int { return 2 << 11 }, // 4KB
},
@ -606,7 +606,8 @@ func TestSilenceLimits(t *testing.T) {
func TestSilenceNoLimits(t *testing.T) {
s, err := New(Options{
Limits: Limits{},
// Should be replaced with NoopLimits.
Limits: nil,
})
require.NoError(t, err)