Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bugfix: only increment Silences version if a silence is added #3961

Merged
merged 1 commit into from
Oct 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 14 additions & 7 deletions silence/silence.go
Original file line number Diff line number Diff line change
Expand Up @@ -580,7 +580,8 @@ func (s *Silences) setSilence(msil *pb.MeshSilence, now time.Time) error {
if err != nil {
return err
}
if s.st.merge(msil, now) {
_, added := s.st.merge(msil, now)
if added {
s.version++
}
s.broadcast(b)
Expand Down Expand Up @@ -927,8 +928,11 @@ func (s *Silences) Merge(b []byte) error {
now := s.nowUTC()

for _, e := range st {
if merged := s.st.merge(e, now); merged {
s.version++
merged, added := s.st.merge(e, now)
if merged {
if added {
s.version++
}
if !cluster.OversizedMessage(b) {
// If this is the first we've seen the message and it's
// not oversized, gossip it to other nodes. We don't
Expand All @@ -953,10 +957,13 @@ func (s *Silences) SetBroadcast(f func([]byte)) {

type state map[string]*pb.MeshSilence

func (s state) merge(e *pb.MeshSilence, now time.Time) bool {
// merge returns two bools: the first is true when merge caused a state change. The second
// is true if that state change added a new silence. In other words, the second return is
// true whenever a silence with a new ID has been added to the state as a result of merge.
func (s state) merge(e *pb.MeshSilence, now time.Time) (bool, bool) {
id := e.Silence.Id
if e.ExpiresAt.Before(now) {
return false
return false, false
}
// Comments list was moved to a single comment. Apply upgrade
// on silences received from peers.
Expand All @@ -969,9 +976,9 @@ func (s state) merge(e *pb.MeshSilence, now time.Time) bool {
prev, ok := s[id]
if !ok || prev.Silence.UpdatedAt.Before(e.Silence.UpdatedAt) {
s[id] = e
return true
return true, !ok
}
return false
return false, false
}

func (s state) MarshalBinary() ([]byte, error) {
Expand Down
16 changes: 16 additions & 0 deletions silence/silence_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,10 @@ func TestSilenceSet(t *testing.T) {
StartsAt: start1.Add(2 * time.Minute),
EndsAt: start1.Add(5 * time.Minute),
}
versionBeforeOp := s.Version()
require.NoError(t, s.Set(sil1))
require.NotEqual(t, "", sil1.Id)
require.NotEqual(t, versionBeforeOp, s.Version())

want := state{
sil1.Id: &pb.MeshSilence{
Expand All @@ -453,8 +455,10 @@ func TestSilenceSet(t *testing.T) {
Matchers: []*pb.Matcher{{Name: "a", Pattern: "b"}},
EndsAt: start2.Add(1 * time.Minute),
}
versionBeforeOp = s.Version()
require.NoError(t, s.Set(sil2))
require.NotEqual(t, "", sil2.Id)
require.NotEqual(t, versionBeforeOp, s.Version())

want = state{
sil1.Id: want[sil1.Id],
Expand All @@ -474,22 +478,27 @@ func TestSilenceSet(t *testing.T) {
// Should be able to update silence without modifications. It is expected to
// keep the same ID.
sil3 := cloneSilence(sil2)
versionBeforeOp = s.Version()
require.NoError(t, s.Set(sil3))
require.Equal(t, sil2.Id, sil3.Id)
require.Equal(t, versionBeforeOp, s.Version())

// Should be able to update silence with comment. It is also expected to
// keep the same ID.
sil4 := cloneSilence(sil3)
sil4.Comment = "c"
versionBeforeOp = s.Version()
require.NoError(t, s.Set(sil4))
require.Equal(t, sil3.Id, sil4.Id)
require.Equal(t, versionBeforeOp, s.Version())

// Extend sil4 to expire at a later time. This should not expire the
// existing silence, and so should also keep the same ID.
clock.Add(time.Minute)
start5 := s.nowUTC()
sil5 := cloneSilence(sil4)
sil5.EndsAt = start5.Add(100 * time.Minute)
versionBeforeOp = s.Version()
require.NoError(t, s.Set(sil5))
require.Equal(t, sil4.Id, sil5.Id)
want = state{
Expand All @@ -507,6 +516,7 @@ func TestSilenceSet(t *testing.T) {
},
}
require.Equal(t, want, s.st, "unexpected state after silence creation")
require.Equal(t, versionBeforeOp, s.Version())

// Replace the silence sil5 with another silence with different matchers.
// Unlike previous updates, changing the matchers for an existing silence
Expand All @@ -518,6 +528,7 @@ func TestSilenceSet(t *testing.T) {

sil6 := cloneSilence(sil5)
sil6.Matchers = []*pb.Matcher{{Name: "a", Pattern: "c"}}
versionBeforeOp = s.Version()
require.NoError(t, s.Set(sil6))
require.NotEqual(t, sil5.Id, sil6.Id)
want = state{
Expand Down Expand Up @@ -546,6 +557,7 @@ func TestSilenceSet(t *testing.T) {
},
}
require.Equal(t, want, s.st, "unexpected state after silence creation")
require.NotEqual(t, versionBeforeOp, s.Version())

// Re-create the silence that we just replaced. Changing the start time,
// just like changing the matchers, creates a new silence with a different
Expand All @@ -555,6 +567,7 @@ func TestSilenceSet(t *testing.T) {
sil7 := cloneSilence(sil5)
sil7.StartsAt = start1
sil7.EndsAt = start1.Add(5 * time.Minute)
versionBeforeOp = s.Version()
require.NoError(t, s.Set(sil7))
require.NotEqual(t, sil2.Id, sil7.Id)
want = state{
Expand All @@ -574,19 +587,22 @@ func TestSilenceSet(t *testing.T) {
},
}
require.Equal(t, want, s.st, "unexpected state after silence creation")
require.NotEqual(t, versionBeforeOp, s.Version())

// Updating an existing silence with an invalid silence should not expire
// the original silence.
clock.Add(time.Millisecond)
sil8 := cloneSilence(sil7)
sil8.EndsAt = time.Time{}
versionBeforeOp = s.Version()
require.EqualError(t, s.Set(sil8), "invalid silence: invalid zero end timestamp")

// sil7 should not be expired because the update failed.
clock.Add(time.Millisecond)
sil7, err = s.QueryOne(QIDs(sil7.Id))
require.NoError(t, err)
require.Equal(t, types.SilenceStateActive, getState(sil7, s.nowUTC()))
require.Equal(t, versionBeforeOp, s.Version())
}

func TestSilenceLimits(t *testing.T) {
Expand Down
Loading