Repository: prometheus/alertmanager
File: silence/silence_test.go (Go)

Code before:
	t.Run("updating a silences does not leak cache entries", func(t *testing.T) {
		s, err := New(Options{})
		require.NoError(t, err)
		clock := clock.NewMock()
		s.clock = clock
		sil1 := &pb.Silence{
			Id: "1",
			Matchers: []*pb.Matcher{{
				Type:    pb.Matcher_EQUAL,
				Name:    "foo",
				Pattern: "bar",
			}},
			StartsAt: clock.Now(),
			EndsAt:   clock.Now().Add(time.Minute),
		}
		s.st["1"] = &pb.MeshSilence{Silence: sil1, ExpiresAt: clock.Now().Add(time.Minute)}
		// Need to query the silence to populate the matcher cache.
		s.Query(QMatches(model.LabelSet{"foo": "bar"}))
		require.Len(t, s.mc, 1)
		// must clone sil1 before updating it.
		sil2 := cloneSilence(sil1)
		require.NoError(t, s.Set(sil2))
		// The memory leak occurred because updating a silence would add a new
		// entry in the matcher cache even though no new silence was created.
		// This check asserts that this no longer happens.
		s.Query(QMatches(model.LabelSet{"foo": "bar"}))
		require.Len(t, s.st, 1)
		require.Len(t, s.mc, 1)
		// Move time forward and both silence and cache entry should be garbage
		// collected.
		clock.Add(time.Minute)
		n, err := s.GC()
		require.NoError(t, err)
		require.Equal(t, 1, n)
		require.Len(t, s.st, 0)
		require.Len(t, s.mc, 0)
	})
}

func TestSilencesSnapshot(t *testing.T) {
	// Check whether storing and loading the snapshot is symmetric.
	now := clock.NewMock().Now().UTC()

	cases := []struct {
		entries []*pb.MeshSilence
	}{
		{
			entries: []*pb.MeshSilence{
				{
					Silence: &pb.Silence{
						Id: "3be80475-e219-4ee7-b6fc-4b65114e362f",

Review comment (performance):
I added this line because the memory leak only occurs if the matcher cache is populated because of a query.

Code after:
	t.Run("updating a silences does not leak cache entries", func(t *testing.T) {
		s, err := New(Options{})
		require.NoError(t, err)
		clock := clock.NewMock()
		s.clock = clock
		sil1 := &pb.Silence{
			Id: "1",
			Matchers: []*pb.Matcher{{
				Type:    pb.Matcher_EQUAL,
				Name:    "foo",
				Pattern: "bar",
			}},
			StartsAt: clock.Now(),
			EndsAt:   clock.Now().Add(time.Minute),
		}
		s.st["1"] = &pb.MeshSilence{Silence: sil1, ExpiresAt: clock.Now().Add(time.Minute)}
		// Need to query the silence to populate the matcher cache.
		s.Query(QMatches(model.LabelSet{"foo": "bar"}))
		require.Len(t, s.mc, 1)
		// must clone sil1 before updating it.
		sil2 := cloneSilence(sil1)
		require.NoError(t, s.Set(sil2))
		// The memory leak occurred because updating a silence would add a new
		// entry in the matcher cache even though no new silence was created.
		// This check asserts that this no longer happens.
		s.Query(QMatches(model.LabelSet{"foo": "bar"}))
		require.Len(t, s.st, 1)
		require.Len(t, s.mc, 1)
		// Move time forward and both silence and cache entry should be garbage
		// collected.
		clock.Add(time.Minute)
		n, err := s.GC()
		require.NoError(t, err)
		require.Equal(t, 1, n)
		require.Empty(t, s.st)
		require.Empty(t, s.mc)
	})
}

func TestSilencesSnapshot(t *testing.T) {
	// Check whether storing and loading the snapshot is symmetric.
	now := clock.NewMock().Now().UTC()

	cases := []struct {
		entries []*pb.MeshSilence
	}{
		{
			entries: []*pb.MeshSilence{
				{
					Silence: &pb.Silence{
						Id: "3be80475-e219-4ee7-b6fc-4b65114e362f",