From 1af81dc5c9f67227aaf7d404b0ffe05355790513 Mon Sep 17 00:00:00 2001 From: Goutham Veeramachaneni Date: Mon, 3 Jan 2022 11:13:48 +0100 Subject: [PATCH 01/10] Update sent timestamp when write irrecoverably fails. We have an alert that fires when prometheus_remote_storage_highest_timestamp_in_seconds - prometheus_remote_storage_queue_highest_sent_timestamp_seconds becomes too high. But we have an agent that fires this when the remote "rate-limits" the user. This is because prometheus_remote_storage_queue_highest_sent_timestamp_seconds doesn't get updated when the remote sends a 429. I think we should update the metrics, and the change I made makes sense. Because if the requests fails because of connectivity issues, etc. we will never exit the `sendWriteRequestWithBackoff` function. It only exits the function when there is a non-recoverable error, like a bad status code, and in that case, I think the metric needs to be updated. Signed-off-by: Goutham Veeramachaneni --- storage/remote/queue_manager.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/storage/remote/queue_manager.go b/storage/remote/queue_manager.go index 49de2c8a35..05ead4712a 100644 --- a/storage/remote/queue_manager.go +++ b/storage/remote/queue_manager.go @@ -1311,12 +1311,11 @@ func (s *shards) sendSamplesWithBackoff(ctx context.Context, samples []prompb.Ti } err = sendWriteRequestWithBackoff(ctx, s.qm.cfg, s.qm.logger, attemptStore, onRetry) - if err != nil { - return err - } + s.qm.metrics.sentBytesTotal.Add(float64(reqSize)) s.qm.metrics.highestSentTimestamp.Set(float64(highest / 1000)) - return nil + + return err } func sendWriteRequestWithBackoff(ctx context.Context, cfg config.QueueConfig, l log.Logger, attempt func(int) error, onRetry func()) error { From 6696b7a5f09a228728732d736c8474c3cc16dae0 Mon Sep 17 00:00:00 2001 From: Goutham Veeramachaneni Date: Tue, 4 Jan 2022 10:46:52 +0100 Subject: [PATCH 02/10] Don't update metrics on context cancellation Signed-off-by: Goutham Veeramachaneni --- storage/remote/queue_manager.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/storage/remote/queue_manager.go b/storage/remote/queue_manager.go index 05ead4712a..e4a2b5399c 100644 --- a/storage/remote/queue_manager.go +++ b/storage/remote/queue_manager.go @@ -15,6 +15,7 @@ package remote import ( "context" + "errors" "math" "strconv" "sync" @@ -1311,6 +1312,11 @@ func (s *shards) sendSamplesWithBackoff(ctx context.Context, samples []prompb.Ti } err = sendWriteRequestWithBackoff(ctx, s.qm.cfg, s.qm.logger, attemptStore, onRetry) + if errors.Is(err, context.Canceled) { + // When there is resharding, we cancel the context for this queue, which means the data is not sent. + // So we exit early to not update the metrics. + return err + } s.qm.metrics.sentBytesTotal.Add(float64(reqSize)) s.qm.metrics.highestSentTimestamp.Set(float64(highest / 1000)) From b75843543c57c45c46bf9e38fcf2fb2d4a1e1c5a Mon Sep 17 00:00:00 2001 From: beorn7 Date: Tue, 4 Jan 2022 16:51:25 +0100 Subject: [PATCH 03/10] release: Volunteer @beorn7 and @csmarchbanks for 2.33 and 2.34 In a meeting at Grafana Labs, we had the shocking revelation that the next release is scheduled to happen soon, but there is no release shepherd for it yet. (Which reminds me: Perhaps we should make it a responsibility of the release shepherd to make sure there is one for the next release, too?) Given my current part-time commitment, I would actually try to avoid being the release shepherd, but on the other hand, it's about time that a Grafanista to do the job again, and the only one who could do it at all in the room was me. @csmarchbanks volunteered for the next release. So here we are. Of course, if any other team member would like to jump in and do this release or the next, we'll happily insert you into the list. Signed-off-by: beorn7 --- RELEASE.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/RELEASE.md b/RELEASE.md index ff89dad1e7..3012ac6734 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -37,7 +37,9 @@ Release cadence of first pre-releases being cut is 6 weeks. | v2.30 | 2021-09-08 | Ganesh Vernekar (GitHub: @codesome) | | v2.31 | 2021-10-20 | Julien Pivotto (GitHub: @roidelapluie) | | v2.32 | 2021-12-01 | Julius Volz (GitHub: @juliusv) | -| v2.33 | 2022-01-12 | **searching for volunteer** | +| v2.33 | 2022-01-12 | Björn Rabenstein (GitHub: @beorn7) | +| v2.34 | 2022-02-23 | Chris Marchbanks (GitHub: @csmarchbanks) | +| v2.35 | 2022-04-06 | **searching for volunteer** | If you are interested in volunteering please create a pull request against the [prometheus/prometheus](https://github.com/prometheus/prometheus) repository and propose yourself for the release series of your choice. From 3be33de316f3cc1ee20498a594819dbcb0a694af Mon Sep 17 00:00:00 2001 From: beorn7 Date: Tue, 4 Jan 2022 17:12:30 +0100 Subject: [PATCH 04/10] release: Extend instructions for the release shepherd This adds the following: - Find a new volunteer for the release shepherd if needed. - Consider experimental features for promotion/demotion. The latter could need more detailed explanation, but I guess we can add those once we have the planned metrics for feature flags. For now, I just wanted to have that point in so that it is not completely forgotten. Signed-off-by: beorn7 Co-authored-by: Levi Harrison --- RELEASE.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/RELEASE.md b/RELEASE.md index ff89dad1e7..3bae2fbd9c 100644 --- a/RELEASE.md +++ b/RELEASE.md @@ -70,7 +70,7 @@ If a bug fix got accidentally merged into main after non-bug-fix changes in main Maintaining the release branches for older minor releases happens on a best effort basis. -### 0. Updating dependencies +### 0. Updating dependencies and promoting/demoting experimental features A few days before a major or minor release, consider updating the dependencies. @@ -85,6 +85,10 @@ you can skip the dependency update or only update select dependencies. In such a case, you have to create an issue or pull request in the GitHub project for later follow-up. +This is also a good time to consider any experimental features and feature +flags for promotion to stable or for deprecation or ultimately removal. Do any +of these in pull requests, one per feature. + #### Updating Go dependencies ``` @@ -155,3 +159,5 @@ For release candidate versions (`v2.16.0-rc.0`), run the benchmark for 3 days us If the release has happened in the latest release branch, merge the changes into main. Once the binaries have been uploaded, announce the release on `prometheus-announce@googlegroups.com`. (Please do not use `prometheus-users@googlegroups.com` for announcements anymore.) Check out previous announcement mails for inspiration. + +Finally, in case there is no release shepherd listed for the next release yet, find a volunteer. From 701545286d0cdc4def5cafe6274d327bb41b782d Mon Sep 17 00:00:00 2001 From: Oleg Zaytsev Date: Wed, 5 Jan 2022 11:46:43 +0100 Subject: [PATCH 05/10] Pop intersected postings heap without popping (#10092) See this comment for detailed explanation: https://github.com/prometheus/prometheus/pull/9907#issuecomment-1002189932 TL;DR: if we don't call Pop() on the heap implementation, we don't need to return our param as an `interface{}` so we save an allocation. This would be popped for every label value, so it can be thousands of saved allocations here (see benchmarks). Signed-off-by: Oleg Zaytsev --- tsdb/index/postings.go | 41 +++++++++++++++++++++++++++++-------- tsdb/index/postings_test.go | 2 +- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/tsdb/index/postings.go b/tsdb/index/postings.go index 69a20b08b6..3f7b63a60f 100644 --- a/tsdb/index/postings.go +++ b/tsdb/index/postings.go @@ -846,18 +846,17 @@ func FindIntersectingPostings(p Postings, candidates []Postings) (indexes []int, return nil, it.Err() } } - if len(h) == 0 { + if h.empty() { return nil, nil } h.Init() - for len(h) > 0 { + for !h.empty() { if !p.Seek(h.At()) { return indexes, p.Err() } if p.At() == h.At() { - found := heap.Pop(&h).(postingsWithIndex) - indexes = append(indexes, found.index) + indexes = append(indexes, h.popIndex()) } else if err := h.Next(); err != nil { return nil, err } @@ -869,10 +868,29 @@ func FindIntersectingPostings(p Postings, candidates []Postings) (indexes []int, type postingsWithIndex struct { index int p Postings + // popped means that this postings shouldn't be considered anymore. + // See popIndex() comment to understand why we need this. + popped bool } type postingsWithIndexHeap []postingsWithIndex +// empty checks whether the heap is empty, which is true if it has no elements, of if the smallest element is popped. +func (h *postingsWithIndexHeap) empty() bool { + return len(*h) == 0 || (*h)[0].popped +} + +// popIndex pops the smallest heap element and returns its index. +// In our implementation we don't actually do heap.Pop(), instead we mark the element as `popped` and fix its position, which +// should be after all the non-popped elements according to our sorting strategy. +// By skipping the `heap.Pop()` call we avoid an extra allocation in this heap's Pop() implementation which returns an interface{}. +func (h *postingsWithIndexHeap) popIndex() int { + index := (*h)[0].index + (*h)[0].popped = true + heap.Fix(h, 0) + return index +} + func (h *postingsWithIndexHeap) Init() { heap.Init(h) } func (h postingsWithIndexHeap) At() storage.SeriesRef { return h[0].p.At() } func (h *postingsWithIndexHeap) Next() error { @@ -886,14 +904,18 @@ func (h *postingsWithIndexHeap) Next() error { if err := pi.p.Err(); err != nil { return errors.Wrapf(err, "postings %d", pi.index) } - - heap.Pop(h) + h.popIndex() return nil } -func (h postingsWithIndexHeap) Len() int { return len(h) } -func (h postingsWithIndexHeap) Less(i, j int) bool { return h[i].p.At() < h[j].p.At() } -func (h *postingsWithIndexHeap) Swap(i, j int) { (*h)[i], (*h)[j] = (*h)[j], (*h)[i] } +func (h postingsWithIndexHeap) Len() int { return len(h) } +func (h postingsWithIndexHeap) Less(i, j int) bool { + if h[i].popped != h[j].popped { + return h[j].popped + } + return h[i].p.At() < h[j].p.At() +} +func (h *postingsWithIndexHeap) Swap(i, j int) { (*h)[i], (*h)[j] = (*h)[j], (*h)[i] } func (h *postingsWithIndexHeap) Push(x interface{}) { *h = append(*h, x.(postingsWithIndex)) @@ -901,6 +923,7 @@ func (h *postingsWithIndexHeap) Push(x interface{}) { // Pop implements heap.Interface and pops the last element, which is NOT the min element, // so this doesn't return the same heap.Pop() +// Although this method is implemented for correctness, we don't expect it to be used, see popIndex() method for details. func (h *postingsWithIndexHeap) Pop() interface{} { old := *h n := len(old) diff --git a/tsdb/index/postings_test.go b/tsdb/index/postings_test.go index 4eaa9a080f..888725f8da 100644 --- a/tsdb/index/postings_test.go +++ b/tsdb/index/postings_test.go @@ -1047,7 +1047,7 @@ func TestPostingsWithIndexHeap(t *testing.T) { require.Equal(t, expected, h.At()) require.NoError(t, h.Next()) } - require.Empty(t, h) + require.True(t, h.empty()) }) t.Run("pop", func(t *testing.T) { From d26fd5c97b0c505f5cb36f719abd6ccc8130bdc9 Mon Sep 17 00:00:00 2001 From: Shihao Xia Date: Wed, 5 Jan 2022 05:51:06 -0500 Subject: [PATCH 06/10] fix potential goroutine leaks (#10057) Signed-off-by: Shihao Xia --- discovery/legacymanager/manager_test.go | 7 +++++-- discovery/manager_test.go | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/discovery/legacymanager/manager_test.go b/discovery/legacymanager/manager_test.go index ce2278d231..57c82b72a8 100644 --- a/discovery/legacymanager/manager_test.go +++ b/discovery/legacymanager/manager_test.go @@ -668,13 +668,16 @@ func TestTargetUpdatesOrder(t *testing.T) { discoveryManager.updatert = 100 * time.Millisecond var totalUpdatesCount int - provUpdates := make(chan []*targetgroup.Group) for _, up := range tc.updates { - go newMockDiscoveryProvider(up...).Run(ctx, provUpdates) if len(up) > 0 { totalUpdatesCount += len(up) } } + provUpdates := make(chan []*targetgroup.Group, totalUpdatesCount) + + for _, up := range tc.updates { + go newMockDiscoveryProvider(up...).Run(ctx, provUpdates) + } for x := 0; x < totalUpdatesCount; x++ { select { diff --git a/discovery/manager_test.go b/discovery/manager_test.go index 80ea1008e4..970168b0f5 100644 --- a/discovery/manager_test.go +++ b/discovery/manager_test.go @@ -668,13 +668,16 @@ func TestTargetUpdatesOrder(t *testing.T) { discoveryManager.updatert = 100 * time.Millisecond var totalUpdatesCount int - provUpdates := make(chan []*targetgroup.Group) for _, up := range tc.updates { - go newMockDiscoveryProvider(up...).Run(ctx, provUpdates) if len(up) > 0 { totalUpdatesCount += len(up) } } + provUpdates := make(chan []*targetgroup.Group, totalUpdatesCount) + + for _, up := range tc.updates { + go newMockDiscoveryProvider(up...).Run(ctx, provUpdates) + } for x := 0; x < totalUpdatesCount; x++ { select { From 82860a770c88f7624002ee38dbe3634a56ca2064 Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Thu, 6 Jan 2022 10:28:58 +0000 Subject: [PATCH 07/10] tsdb: use simpler map key to improve exemplar ingest performance (#10111) * tsdb: fix exemplar benchmarks Go benchmarks are expected to do an amount of work that varies with the `b.N` parameter. Previously these benchmarks would report a result like 0.01 ns/op, which is nonsense. Signed-off-by: Bryan Boreham * tsdb: use simpler map key to improve exemplar perf Prometheus holds an index of exemplars so it can discard the oldest one for a series when a new one is added. Since the keys are not for human eyes, we can use a simpler format and save the effort of quoting label values. Signed-off-by: Bryan Boreham * Exemplars: allocate index map with estimated size This avoids Go having to re-size the map several times as it grows. 16 exemplars per series is a guess; if it is too low then the map will be sparse, while if it is too high then the map will have to resize once or twice. Signed-off-by: Bryan Boreham --- tsdb/exemplar.go | 46 ++++++++++++++++----------- tsdb/exemplar_test.go | 73 +++++++++++++++++++++++-------------------- 2 files changed, 66 insertions(+), 53 deletions(-) diff --git a/tsdb/exemplar.go b/tsdb/exemplar.go index 516b538e18..3718d9591d 100644 --- a/tsdb/exemplar.go +++ b/tsdb/exemplar.go @@ -27,8 +27,12 @@ import ( "github.com/prometheus/prometheus/storage" ) -// Indicates that there is no index entry for an exmplar. -const noExemplar = -1 +const ( + // Indicates that there is no index entry for an exmplar. + noExemplar = -1 + // Estimated number of exemplars per series, for sizing the index. + estimatedExemplarsPerSeries = 16 +) type CircularExemplarStorage struct { lock sync.RWMutex @@ -117,7 +121,7 @@ func NewCircularExemplarStorage(len int64, m *ExemplarMetrics) (ExemplarStorage, } c := &CircularExemplarStorage{ exemplars: make([]*circularBufferEntry, len), - index: make(map[string]*indexEntry), + index: make(map[string]*indexEntry, len/estimatedExemplarsPerSeries), metrics: m, } @@ -202,7 +206,8 @@ Outer: } func (ce *CircularExemplarStorage) ValidateExemplar(l labels.Labels, e exemplar.Exemplar) error { - seriesLabels := l.String() + var buf [1024]byte + seriesLabels := l.Bytes(buf[:]) // TODO(bwplotka): This lock can lock all scrapers, there might high contention on this on scale. // Optimize by moving the lock to be per series (& benchmark it). @@ -213,7 +218,7 @@ func (ce *CircularExemplarStorage) ValidateExemplar(l labels.Labels, e exemplar. // Not thread safe. The append parameters tells us whether this is an external validation, or internal // as a result of an AddExemplar call, in which case we should update any relevant metrics. -func (ce *CircularExemplarStorage) validateExemplar(l string, e exemplar.Exemplar, append bool) error { +func (ce *CircularExemplarStorage) validateExemplar(key []byte, e exemplar.Exemplar, append bool) error { if len(ce.exemplars) <= 0 { return storage.ErrExemplarsDisabled } @@ -230,7 +235,7 @@ func (ce *CircularExemplarStorage) validateExemplar(l string, e exemplar.Exempla } } - idx, ok := ce.index[l] + idx, ok := ce.index[string(key)] if !ok { return nil } @@ -269,7 +274,7 @@ func (ce *CircularExemplarStorage) Resize(l int64) int { oldNextIndex := int64(ce.nextIndex) ce.exemplars = make([]*circularBufferEntry, l) - ce.index = make(map[string]*indexEntry) + ce.index = make(map[string]*indexEntry, l/estimatedExemplarsPerSeries) ce.nextIndex = 0 // Replay as many entries as needed, starting with oldest first. @@ -305,13 +310,14 @@ func (ce *CircularExemplarStorage) Resize(l int64) int { // migrate is like AddExemplar but reuses existing structs. Expected to be called in batch and requires // external lock and does not compute metrics. func (ce *CircularExemplarStorage) migrate(entry *circularBufferEntry) { - seriesLabels := entry.ref.seriesLabels.String() + var buf [1024]byte + seriesLabels := entry.ref.seriesLabels.Bytes(buf[:]) - idx, ok := ce.index[seriesLabels] + idx, ok := ce.index[string(seriesLabels)] if !ok { idx = entry.ref idx.oldest = ce.nextIndex - ce.index[seriesLabels] = idx + ce.index[string(seriesLabels)] = idx } else { entry.ref = idx ce.exemplars[idx.newest].next = ce.nextIndex @@ -329,7 +335,8 @@ func (ce *CircularExemplarStorage) AddExemplar(l labels.Labels, e exemplar.Exemp return storage.ErrExemplarsDisabled } - seriesLabels := l.String() + var buf [1024]byte + seriesLabels := l.Bytes(buf[:]) // TODO(bwplotka): This lock can lock all scrapers, there might high contention on this on scale. // Optimize by moving the lock to be per series (& benchmark it). @@ -345,11 +352,11 @@ func (ce *CircularExemplarStorage) AddExemplar(l labels.Labels, e exemplar.Exemp return err } - _, ok := ce.index[seriesLabels] + _, ok := ce.index[string(seriesLabels)] if !ok { - ce.index[seriesLabels] = &indexEntry{oldest: ce.nextIndex, seriesLabels: l} + ce.index[string(seriesLabels)] = &indexEntry{oldest: ce.nextIndex, seriesLabels: l} } else { - ce.exemplars[ce.index[seriesLabels].newest].next = ce.nextIndex + ce.exemplars[ce.index[string(seriesLabels)].newest].next = ce.nextIndex } if prev := ce.exemplars[ce.nextIndex]; prev == nil { @@ -357,12 +364,13 @@ func (ce *CircularExemplarStorage) AddExemplar(l labels.Labels, e exemplar.Exemp } else { // There exists exemplar already on this ce.nextIndex entry, drop it, to make place // for others. - prevLabels := prev.ref.seriesLabels.String() + var buf [1024]byte + prevLabels := prev.ref.seriesLabels.Bytes(buf[:]) if prev.next == noExemplar { // Last item for this series, remove index entry. - delete(ce.index, prevLabels) + delete(ce.index, string(prevLabels)) } else { - ce.index[prevLabels].oldest = prev.next + ce.index[string(prevLabels)].oldest = prev.next } } @@ -370,8 +378,8 @@ func (ce *CircularExemplarStorage) AddExemplar(l labels.Labels, e exemplar.Exemp // since this is the first exemplar stored for this series. ce.exemplars[ce.nextIndex].next = noExemplar ce.exemplars[ce.nextIndex].exemplar = e - ce.exemplars[ce.nextIndex].ref = ce.index[seriesLabels] - ce.index[seriesLabels].newest = ce.nextIndex + ce.exemplars[ce.nextIndex].ref = ce.index[string(seriesLabels)] + ce.index[string(seriesLabels)].newest = ce.nextIndex ce.nextIndex = (ce.nextIndex + 1) % len(ce.exemplars) diff --git a/tsdb/exemplar_test.go b/tsdb/exemplar_test.go index 1418dcca98..30c497426f 100644 --- a/tsdb/exemplar_test.go +++ b/tsdb/exemplar_test.go @@ -112,7 +112,7 @@ func TestAddExemplar(t *testing.T) { } require.NoError(t, es.AddExemplar(l, e)) - require.Equal(t, es.index[l.String()].newest, 0, "exemplar was not stored correctly") + require.Equal(t, es.index[string(l.Bytes(nil))].newest, 0, "exemplar was not stored correctly") e2 := exemplar.Exemplar{ Labels: labels.Labels{ @@ -126,8 +126,8 @@ func TestAddExemplar(t *testing.T) { } require.NoError(t, es.AddExemplar(l, e2)) - require.Equal(t, es.index[l.String()].newest, 1, "exemplar was not stored correctly, location of newest exemplar for series in index did not update") - require.True(t, es.exemplars[es.index[l.String()].newest].exemplar.Equals(e2), "exemplar was not stored correctly, expected %+v got: %+v", e2, es.exemplars[es.index[l.String()].newest].exemplar) + require.Equal(t, es.index[string(l.Bytes(nil))].newest, 1, "exemplar was not stored correctly, location of newest exemplar for series in index did not update") + require.True(t, es.exemplars[es.index[string(l.Bytes(nil))].newest].exemplar.Equals(e2), "exemplar was not stored correctly, expected %+v got: %+v", e2, es.exemplars[es.index[string(l.Bytes(nil))].newest].exemplar) require.NoError(t, es.AddExemplar(l, e2), "no error is expected attempting to add duplicate exemplar") @@ -300,7 +300,7 @@ func TestSelectExemplar_TimeRange(t *testing.T) { Ts: int64(101 + i), }) require.NoError(t, err) - require.Equal(t, es.index[l.String()].newest, i, "exemplar was not stored correctly") + require.Equal(t, es.index[string(l.Bytes(nil))].newest, i, "exemplar was not stored correctly") } m, err := labels.NewMatcher(labels.MatchEqual, l[0].Name, l[0].Value) @@ -376,14 +376,14 @@ func TestIndexOverwrite(t *testing.T) { // Ensure index GC'ing is taking place, there should no longer be any // index entry for series l1 since we just wrote two exemplars for series l2. - _, ok := es.index[l1.String()] + _, ok := es.index[string(l1.Bytes(nil))] require.False(t, ok) - require.Equal(t, &indexEntry{1, 0, l2}, es.index[l2.String()]) + require.Equal(t, &indexEntry{1, 0, l2}, es.index[string(l2.Bytes(nil))]) err = es.AddExemplar(l1, exemplar.Exemplar{Value: 4, Ts: 4}) require.NoError(t, err) - i := es.index[l2.String()] + i := es.index[string(l2.Bytes(nil))] require.Equal(t, &indexEntry{0, 0, l2}, i) } @@ -492,18 +492,23 @@ func BenchmarkAddExemplar(b *testing.B) { for _, n := range []int{10000, 100000, 1000000} { b.Run(fmt.Sprintf("%d", n), func(b *testing.B) { - exs, err := NewCircularExemplarStorage(int64(n), eMetrics) - require.NoError(b, err) - es := exs.(*CircularExemplarStorage) - - b.ResetTimer() - l := labels.Labels{{Name: "service", Value: strconv.Itoa(0)}} - for i := 0; i < n; i++ { - if i%100 == 0 { - l = labels.Labels{{Name: "service", Value: strconv.Itoa(i)}} - } - err = es.AddExemplar(l, exemplar.Exemplar{Value: float64(i), Ts: int64(i), Labels: exLabels}) + for j := 0; j < b.N; j++ { + b.StopTimer() + exs, err := NewCircularExemplarStorage(int64(n), eMetrics) require.NoError(b, err) + es := exs.(*CircularExemplarStorage) + l := labels.Labels{{Name: "service", Value: strconv.Itoa(0)}} + b.StartTimer() + + for i := 0; i < n; i++ { + if i%100 == 0 { + l = labels.Labels{{Name: "service", Value: strconv.Itoa(i)}} + } + err = es.AddExemplar(l, exemplar.Exemplar{Value: float64(i), Ts: int64(i), Labels: exLabels}) + if err != nil { + require.NoError(b, err) + } + } } }) } @@ -543,24 +548,24 @@ func BenchmarkResizeExemplars(b *testing.B) { } for _, tc := range testCases { - exs, err := NewCircularExemplarStorage(tc.startSize, eMetrics) - require.NoError(b, err) - es := exs.(*CircularExemplarStorage) + b.Run(fmt.Sprintf("%s-%d-to-%d", tc.name, tc.startSize, tc.endSize), func(b *testing.B) { + for j := 0; j < b.N; j++ { + b.StopTimer() + exs, err := NewCircularExemplarStorage(tc.startSize, eMetrics) + require.NoError(b, err) + es := exs.(*CircularExemplarStorage) - for i := 0; i < int(float64(tc.startSize)*float64(1.5)); i++ { - l := labels.FromStrings("service", strconv.Itoa(i)) + for i := 0; i < int(float64(tc.startSize)*float64(1.5)); i++ { + l := labels.FromStrings("service", strconv.Itoa(i)) - err = es.AddExemplar(l, exemplar.Exemplar{Value: float64(i), Ts: int64(i)}) - require.NoError(b, err) - } - saveIndex := es.index - saveExemplars := es.exemplars - - b.Run(fmt.Sprintf("%s-%d-to-%d", tc.name, tc.startSize, tc.endSize), func(t *testing.B) { - es.index = saveIndex - es.exemplars = saveExemplars - b.ResetTimer() - es.Resize(tc.endSize) + err = es.AddExemplar(l, exemplar.Exemplar{Value: float64(i), Ts: int64(i)}) + if err != nil { + require.NoError(b, err) + } + } + b.StartTimer() + es.Resize(tc.endSize) + } }) } } From a83d46ee9ca233a10c9b6255915260e776279fad Mon Sep 17 00:00:00 2001 From: Oleg Zaytsev Date: Thu, 6 Jan 2022 11:33:44 +0100 Subject: [PATCH 08/10] Tidy postingsWithIndexHeap (#10123) Unexported postingsWithIndexHeap's methods that don't need to be exported, and added detailed comments. Signed-off-by: Oleg Zaytsev --- tsdb/index/postings.go | 36 ++++++++++++++++++++++++++++-------- tsdb/index/postings_test.go | 14 +++++++------- 2 files changed, 35 insertions(+), 15 deletions(-) diff --git a/tsdb/index/postings.go b/tsdb/index/postings.go index 3f7b63a60f..22aaf7f20e 100644 --- a/tsdb/index/postings.go +++ b/tsdb/index/postings.go @@ -849,15 +849,15 @@ func FindIntersectingPostings(p Postings, candidates []Postings) (indexes []int, if h.empty() { return nil, nil } - h.Init() + heap.Init(&h) for !h.empty() { - if !p.Seek(h.At()) { + if !p.Seek(h.at()) { return indexes, p.Err() } - if p.At() == h.At() { + if p.At() == h.at() { indexes = append(indexes, h.popIndex()) - } else if err := h.Next(); err != nil { + } else if err := h.next(); err != nil { return nil, err } } @@ -865,14 +865,20 @@ func FindIntersectingPostings(p Postings, candidates []Postings) (indexes []int, return indexes, nil } +// postingsWithIndex is used as postingsWithIndexHeap elements by FindIntersectingPostings, +// keeping track of the original index of each postings while they move inside the heap. type postingsWithIndex struct { index int p Postings - // popped means that this postings shouldn't be considered anymore. + // popped means that these postings shouldn't be considered anymore. // See popIndex() comment to understand why we need this. popped bool } +// postingsWithIndexHeap implements heap.Interface, +// with root always pointing to the postings with minimum Postings.At() value. +// It also implements a special way of removing elements that marks them as popped and moves them to the bottom of the +// heap instead of actually removing them, see popIndex() for more details. type postingsWithIndexHeap []postingsWithIndex // empty checks whether the heap is empty, which is true if it has no elements, of if the smallest element is popped. @@ -891,9 +897,14 @@ func (h *postingsWithIndexHeap) popIndex() int { return index } -func (h *postingsWithIndexHeap) Init() { heap.Init(h) } -func (h postingsWithIndexHeap) At() storage.SeriesRef { return h[0].p.At() } -func (h *postingsWithIndexHeap) Next() error { +// at provides the storage.SeriesRef where root Postings is pointing at this moment. +func (h postingsWithIndexHeap) at() storage.SeriesRef { return h[0].p.At() } + +// next performs the Postings.Next() operation on the root of the heap, performing the related operation on the heap +// and conveniently returning the result of calling Postings.Err() if the result of calling Next() was false. +// If Next() succeeds, heap is fixed to move the root to its new position, according to its Postings.At() value. +// If Next() returns fails and there's no error reported by Postings.Err(), then root is marked as removed and heap is fixed. +func (h *postingsWithIndexHeap) next() error { pi := (*h)[0] next := pi.p.Next() if next { @@ -908,15 +919,24 @@ func (h *postingsWithIndexHeap) Next() error { return nil } +// Len implements heap.Interface. +// Notice that Len() > 0 does not imply that heap is not empty as elements are not removed from this heap. +// Use empty() to check whether heap is empty or not. func (h postingsWithIndexHeap) Len() int { return len(h) } + +// Less implements heap.Interface, it puts all the popped elements at the bottom, +// and then sorts by Postings.At() property of each node. func (h postingsWithIndexHeap) Less(i, j int) bool { if h[i].popped != h[j].popped { return h[j].popped } return h[i].p.At() < h[j].p.At() } + +// Swap implements heap.Interface. func (h *postingsWithIndexHeap) Swap(i, j int) { (*h)[i], (*h)[j] = (*h)[j], (*h)[i] } +// Push implements heap.Interface. func (h *postingsWithIndexHeap) Push(x interface{}) { *h = append(*h, x.(postingsWithIndex)) } diff --git a/tsdb/index/postings_test.go b/tsdb/index/postings_test.go index 888725f8da..75e1677b2b 100644 --- a/tsdb/index/postings_test.go +++ b/tsdb/index/postings_test.go @@ -1041,11 +1041,11 @@ func TestPostingsWithIndexHeap(t *testing.T) { for _, node := range h { node.p.Next() } - h.Init() + heap.Init(&h) for _, expected := range []storage.SeriesRef{1, 5, 10, 20, 25, 30, 50} { - require.Equal(t, expected, h.At()) - require.NoError(t, h.Next()) + require.Equal(t, expected, h.at()) + require.NoError(t, h.next()) } require.True(t, h.empty()) }) @@ -1059,13 +1059,13 @@ func TestPostingsWithIndexHeap(t *testing.T) { for _, node := range h { node.p.Next() } - h.Init() + heap.Init(&h) for _, expected := range []storage.SeriesRef{1, 5, 10, 20} { - require.Equal(t, expected, h.At()) - require.NoError(t, h.Next()) + require.Equal(t, expected, h.at()) + require.NoError(t, h.next()) } - require.Equal(t, storage.SeriesRef(25), h.At()) + require.Equal(t, storage.SeriesRef(25), h.at()) node := heap.Pop(&h).(postingsWithIndex) require.Equal(t, 2, node.index) require.Equal(t, storage.SeriesRef(25), node.p.At()) From 4727c41a4b321f7b9058d21a611bf74f586270c1 Mon Sep 17 00:00:00 2001 From: beorn7 Date: Thu, 6 Jan 2022 16:22:33 +0100 Subject: [PATCH 09/10] Update Go dependencies Note that go.mod has been manually modified to use the following versions: - k8s.io/api v0.22.4 - k8s.io/apimachinery v0.22.4 - k8s.io/client-go v0.22.4 The current version of those modules requires Go1.17, which we only intend to require once Go1.18 is out. Signed-off-by: beorn7 --- go.mod | 14 +++++++------- go.sum | 24 +++++++++++++----------- 2 files changed, 20 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 2c3227a5f5..5b9095633e 100644 --- a/go.mod +++ b/go.mod @@ -3,13 +3,13 @@ module github.com/prometheus/prometheus go 1.16 require ( - github.com/Azure/azure-sdk-for-go v60.3.0+incompatible + github.com/Azure/azure-sdk-for-go v61.1.0+incompatible github.com/Azure/go-autorest/autorest v0.11.23 github.com/Azure/go-autorest/autorest/adal v0.9.18 github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect - github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a - github.com/aws/aws-sdk-go v1.42.25 + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 + github.com/aws/aws-sdk-go v1.42.28 github.com/cespare/xxhash/v2 v2.1.2 github.com/containerd/containerd v1.5.7 // indirect github.com/dennwc/varint v1.0.0 @@ -27,7 +27,7 @@ require ( github.com/go-zookeeper/zk v1.0.2 github.com/gogo/protobuf v1.3.2 github.com/golang/snappy v0.0.4 - github.com/google/pprof v0.0.0-20211122183932-1daafda22083 + github.com/google/pprof v0.0.0-20211214055906-6f57359322fd github.com/gophercloud/gophercloud v0.24.0 github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/hashicorp/consul/api v1.12.0 @@ -59,14 +59,14 @@ require ( github.com/uber/jaeger-lib v2.4.1+incompatible go.uber.org/atomic v1.9.0 go.uber.org/goleak v1.1.12 - golang.org/x/net v0.0.0-20211209124913-491a49abca63 + golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e golang.org/x/time v0.0.0-20211116232009-f0f3c7e86c11 golang.org/x/tools v0.1.8 google.golang.org/api v0.63.0 - google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa + google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb google.golang.org/protobuf v1.27.1 gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/yaml.v2 v2.4.0 @@ -75,7 +75,7 @@ require ( k8s.io/apimachinery v0.22.4 k8s.io/client-go v0.22.4 k8s.io/klog v1.0.0 - k8s.io/klog/v2 v2.30.0 + k8s.io/klog/v2 v2.40.1 ) replace ( diff --git a/go.sum b/go.sum index 85508d00ab..fec2cfed30 100644 --- a/go.sum +++ b/go.sum @@ -52,8 +52,8 @@ collectd.org v0.3.0/go.mod h1:A/8DzQBkF6abtvrT2j/AU/4tiBgJWYyh0y/oB/4MlWE= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v41.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= -github.com/Azure/azure-sdk-for-go v60.3.0+incompatible h1:u6EXgnASaUOh38GXCwEpRs+u2bbfJpJpXeB42kU2cjg= -github.com/Azure/azure-sdk-for-go v60.3.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v61.1.0+incompatible h1:Qbz3jdfkXIPjZECEuk2E7i3iLhC9Ul74pG5mQRQC+z4= +github.com/Azure/azure-sdk-for-go v61.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= @@ -154,8 +154,8 @@ github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRF github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20210208195552-ff826a37aa15/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a h1:E/8AP5dFtMhl5KPJz66Kt9G0n+7Sn41Fy1wv9/jHOrc= -github.com/alecthomas/units v0.0.0-20210927113745-59d0afb8317a/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= github.com/antihax/optional v0.0.0-20180407024304-ca021399b1a6/go.mod h1:V8iCPQYkqmusNa815XgQio277wI47sdRh1dUOLdyC6Q= @@ -188,8 +188,8 @@ github.com/aws/aws-sdk-go v1.30.12/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZve github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48= github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.40.11/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q= -github.com/aws/aws-sdk-go v1.42.25 h1:BbdvHAi+t9LRiaYUyd53noq9jcaAcfzOhSVbKfr6Avs= -github.com/aws/aws-sdk-go v1.42.25/go.mod h1:gyRszuZ/icHmHAVE4gc/r+cfCmhA1AD+vqfWbgI+eHs= +github.com/aws/aws-sdk-go v1.42.28 h1:YJtgL7IGSN41saY4JLW08jya5nU0vGcuAeAa1OL2M6c= +github.com/aws/aws-sdk-go v1.42.28/go.mod h1:OGr6lGMAKGlG9CVrYnWYDKIyb829c6EVBRjxqjmPepc= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/benbjohnson/immutable v0.2.1/go.mod h1:uc6OHo6PN2++n98KHLxW8ef4W42ylHiQSENghE1ezxI= github.com/benbjohnson/tmpl v1.0.0/go.mod h1:igT620JFIi44B6awvU9IsDhR77IXWtFigTLil/RPdps= @@ -719,8 +719,8 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20211122183932-1daafda22083 h1:c8EUapQFi+kjzedr4c6WqbwMdmB95+oDBWZ5XFHFYxY= -github.com/google/pprof v0.0.0-20211122183932-1daafda22083/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd h1:1FjCyPC+syAzJ5/2S8fqdZK1R22vvA0J7JZKcuOIQ7Y= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -1534,8 +1534,9 @@ golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211209124913-491a49abca63 h1:iocB37TsdFuN6IBRZ+ry36wrkoV51/tl5vOWqkcPGvY= -golang.org/x/net v0.0.0-20211209124913-491a49abca63/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98 h1:+6WJMRLHlD7X7frgp7TUZ36RnQzSf9wVVTNakEp+nqY= +golang.org/x/net v0.0.0-20220105145211-5b0dc2dfae98/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1906,8 +1907,9 @@ google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa h1:I0YcKz0I7OAhddo7ya8kMnvprhcWM045PmkBdMO9zN0= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb h1:ZrsicilzPCS/Xr8qtBZZLpy4P9TYXAfl49ctG1/5tgw= +google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= From 931acc3ee8f0f4e0eac68b4a2c4d880df7d82944 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Victor=20H=C3=A4ggqvist?= Date: Fri, 7 Jan 2022 10:38:01 +0100 Subject: [PATCH 10/10] fix overlapping click-targets of alert state checkboxes (#10136) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit using the same margin as checkboxes on top of /graph Signed-off-by: Victor Häggqvist --- web/ui/react-app/src/pages/alerts/AlertContents.tsx | 2 +- .../pages/alerts/__snapshots__/AlertContents.test.tsx.snap | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web/ui/react-app/src/pages/alerts/AlertContents.tsx b/web/ui/react-app/src/pages/alerts/AlertContents.tsx index 5165eae5b4..f96ae75232 100644 --- a/web/ui/react-app/src/pages/alerts/AlertContents.tsx +++ b/web/ui/react-app/src/pages/alerts/AlertContents.tsx @@ -63,7 +63,7 @@ const AlertsContent: FC = ({ groups = [], statsCount }) => { return ( @@ -35,7 +35,7 @@ exports[`AlertsContent matches a snapshot 1`] = ` onChange={[Function]} wrapperStyles={ Object { - "marginRight": 10, + "marginRight": 20, } } > @@ -58,7 +58,7 @@ exports[`AlertsContent matches a snapshot 1`] = ` onChange={[Function]} wrapperStyles={ Object { - "marginRight": 10, + "marginRight": 20, } } >