Skip to content

Commit 2443d09

Browse files
committed
fix(backend): reduce allocations in fingerprint code
1 parent e5de49e commit 2443d09

File tree

5 files changed

+97
-32
lines changed

5 files changed

+97
-32
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## v0.115
4+
5+
### Fixed
6+
7+
- Improved memory usage.
8+
39
## v0.114
410

511
### Fixed

cmd/karma/api_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ var groupTests = []groupTest{
5656
Receiver: "by-name",
5757
},
5858
},
59-
id: "099c5ca6d1c92f615b13056b935d0c8dee70f18c",
59+
id: "990fb0cdc86aae89",
6060
stateCount: map[string]int{
6161
models.AlertStateActive: 1,
6262
models.AlertStateSuppressed: 0,
@@ -92,7 +92,7 @@ var groupTests = []groupTest{
9292
Receiver: "by-cluster-service",
9393
},
9494
},
95-
id: "0b1963665aac588dc4b18e17c7a4f70466c622ea",
95+
id: "6b15d34b0ed69d02",
9696
stateCount: map[string]int{
9797
models.AlertStateActive: 1,
9898
models.AlertStateSuppressed: 0,
@@ -159,7 +159,7 @@ var groupTests = []groupTest{
159159
Receiver: "by-cluster-service",
160160
},
161161
},
162-
id: "2d3f39413b41c873cb72e0b8065aa7b8631e983e",
162+
id: "f08998b6581752f4",
163163
stateCount: map[string]int{
164164
models.AlertStateActive: 3,
165165
models.AlertStateSuppressed: 0,
@@ -228,7 +228,7 @@ var groupTests = []groupTest{
228228
Receiver: "by-cluster-service",
229229
},
230230
},
231-
id: "3c09c4156e6784dcf6d5b2e1629253798f82909b",
231+
id: "97dba9e211f41cf6",
232232
stateCount: map[string]int{
233233
models.AlertStateActive: 0,
234234
models.AlertStateSuppressed: 3,
@@ -396,7 +396,7 @@ var groupTests = []groupTest{
396396
Receiver: "by-name",
397397
},
398398
},
399-
id: "58c6a3467cebc53abe68ecbe8643ce478c5a1573",
399+
id: "db6e3af075b36419",
400400
stateCount: map[string]int{
401401
models.AlertStateActive: 5,
402402
models.AlertStateSuppressed: 3,
@@ -433,7 +433,7 @@ var groupTests = []groupTest{
433433
Receiver: "by-cluster-service",
434434
},
435435
},
436-
id: "8ca8151d9e30baba2334507dca53e16b7be93c5e",
436+
id: "c8a1ec76d51e9d96",
437437
stateCount: map[string]int{
438438
models.AlertStateActive: 1,
439439
models.AlertStateSuppressed: 0,
@@ -485,7 +485,7 @@ var groupTests = []groupTest{
485485
Receiver: "by-cluster-service",
486486
},
487487
},
488-
id: "98c1a53d0f71af9c734c9180697383f3b8aff80f",
488+
id: "922d04650baba3fe",
489489
stateCount: map[string]int{
490490
models.AlertStateActive: 2,
491491
models.AlertStateSuppressed: 0,
@@ -535,7 +535,7 @@ var groupTests = []groupTest{
535535
Receiver: "by-name",
536536
},
537537
},
538-
id: "bc4845fec77585cdfebe946234279d785ca93891",
538+
id: "69b99170489a6c64",
539539
stateCount: map[string]int{
540540
models.AlertStateActive: 1,
541541
models.AlertStateSuppressed: 1,
@@ -572,7 +572,7 @@ var groupTests = []groupTest{
572572
Receiver: "by-name",
573573
},
574574
},
575-
id: "bf78806d2a80b1c8150c1391669813722428e858",
575+
id: "37f9b50559e97fd0",
576576
stateCount: map[string]int{
577577
models.AlertStateActive: 1,
578578
models.AlertStateSuppressed: 0,
@@ -624,7 +624,7 @@ var groupTests = []groupTest{
624624
Receiver: "by-cluster-service",
625625
},
626626
},
627-
id: "ecefc3705b1ab4e4c3283c879540be348d2d9dce",
627+
id: "ca10a29d2e729cff",
628628
stateCount: map[string]int{
629629
models.AlertStateActive: 1,
630630
models.AlertStateSuppressed: 1,
@@ -905,15 +905,15 @@ var sortTests = []sortTest{
905905
sortLabel: "",
906906
sortReverse: false,
907907
expectedLabel: "cluster",
908-
expectedValues: []string{"dev", "prod", "staging", "dev", "staging", "prod"},
908+
expectedValues: []string{"staging", "dev", "staging", "dev", "prod", "prod"},
909909
},
910910
{
911911
filter: []string{"@receiver=by-cluster-service"},
912912
sortOrder: "disabled",
913913
sortLabel: "",
914914
sortReverse: true,
915915
expectedLabel: "cluster",
916-
expectedValues: []string{"prod", "staging", "dev", "staging", "prod", "dev"},
916+
expectedValues: []string{"prod", "prod", "dev", "staging", "dev", "staging"},
917917
},
918918
{
919919
filter: []string{"@receiver=by-cluster-service"},

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ go 1.20
55
require (
66
github.com/Masterminds/semver/v3 v3.2.1
77
github.com/beme/abide v0.0.0-20190723115211-635a09831760
8+
github.com/cespare/xxhash/v2 v2.2.0
89
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08
910
github.com/fvbommel/sortorder v1.1.0
1011
github.com/go-chi/chi/v5 v5.0.10
@@ -35,7 +36,6 @@ require (
3536
require (
3637
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
3738
github.com/beorn7/perks v1.0.1 // indirect
38-
github.com/cespare/xxhash/v2 v2.2.0 // indirect
3939
github.com/fsnotify/fsnotify v1.6.0 // indirect
4040
github.com/go-logr/logr v1.2.4 // indirect
4141
github.com/go-logr/stdr v1.2.2 // indirect

internal/models/alert.go

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ package models
22

33
import (
44
"fmt"
5+
"strconv"
56
"strings"
67
"time"
78

8-
"github.com/cnf/structhash"
9+
"github.com/cespare/xxhash/v2"
910
"github.com/fvbommel/sortorder"
1011

1112
"github.com/prymitive/karma/internal/config"
@@ -130,23 +131,77 @@ type Alert struct {
130131
// those are not exposed in JSON, Alertmanager specific value will be in kept
131132
// in the Alertmanager slice
132133
// skip those when generating alert fingerprint too
133-
Fingerprint string `json:"-" hash:"-"`
134-
GeneratorURL string `json:"-" hash:"-"`
135-
SilencedBy []string `json:"-" hash:"-"`
136-
InhibitedBy []string `json:"-" hash:"-"`
134+
Fingerprint string `json:"-"`
135+
GeneratorURL string `json:"-"`
136+
SilencedBy []string `json:"-"`
137+
InhibitedBy []string `json:"-"`
137138
// karma fields
138139
Alertmanager []AlertmanagerInstance `json:"alertmanager"`
139140
Receiver string `json:"receiver"`
140141
// fingerprints are precomputed for speed
141-
LabelsFP string `json:"id" hash:"-"`
142-
contentFP string `hash:"-"`
142+
LabelsFP string `json:"id"`
143+
contentFP string
143144
}
144145

146+
var seps = []byte{'\xff'}
147+
145148
// UpdateFingerprints will generate a new set of fingerprints for this alert
146-
// it should be called after modifying any field that isn't tagged with hash:"-"
147149
func (a *Alert) UpdateFingerprints() {
148-
a.LabelsFP = fmt.Sprintf("%x", structhash.Sha1(a.Labels.Map(), 1))
149-
a.contentFP = fmt.Sprintf("%x", structhash.Sha1(a, 1))
150+
h := xxhash.New()
151+
for _, l := range a.Labels {
152+
_, _ = h.WriteString(l.Name)
153+
_, _ = h.Write(seps)
154+
_, _ = h.WriteString(l.Value)
155+
_, _ = h.Write(seps)
156+
}
157+
a.LabelsFP = fmt.Sprintf("%x", h.Sum64())
158+
159+
h.Reset()
160+
for _, a := range a.Annotations {
161+
_, _ = h.WriteString(a.Name)
162+
_, _ = h.Write(seps)
163+
_, _ = h.WriteString(a.Value)
164+
_, _ = h.Write(seps)
165+
_, _ = h.WriteString(strconv.FormatBool(a.IsAction))
166+
_, _ = h.Write(seps)
167+
_, _ = h.WriteString(strconv.FormatBool(a.IsLink))
168+
_, _ = h.Write(seps)
169+
_, _ = h.WriteString(strconv.FormatBool(a.Visible))
170+
_, _ = h.Write(seps)
171+
172+
}
173+
for _, l := range a.Labels {
174+
_, _ = h.WriteString(l.Name)
175+
_, _ = h.Write(seps)
176+
_, _ = h.WriteString(l.Value)
177+
_, _ = h.Write(seps)
178+
}
179+
_, _ = h.WriteString(a.StartsAt.Format(time.RFC3339))
180+
_, _ = h.WriteString(a.State)
181+
for _, am := range a.Alertmanager {
182+
_, _ = h.WriteString(am.Fingerprint)
183+
_, _ = h.Write(seps)
184+
_, _ = h.WriteString(am.Name)
185+
_, _ = h.Write(seps)
186+
_, _ = h.WriteString(am.Cluster)
187+
_, _ = h.Write(seps)
188+
_, _ = h.WriteString(am.State)
189+
_, _ = h.Write(seps)
190+
_, _ = h.WriteString(am.StartsAt.Format(time.RFC3339))
191+
_, _ = h.Write(seps)
192+
_, _ = h.WriteString(am.Source)
193+
_, _ = h.Write(seps)
194+
for _, s := range am.SilencedBy {
195+
_, _ = h.WriteString(s)
196+
_, _ = h.Write(seps)
197+
}
198+
for _, s := range am.InhibitedBy {
199+
_, _ = h.WriteString(s)
200+
_, _ = h.Write(seps)
201+
}
202+
}
203+
_, _ = h.WriteString(a.Receiver)
204+
a.contentFP = fmt.Sprintf("%x", h.Sum64())
150205
}
151206

152207
// LabelsFingerprint is a checksum computed only from labels which should be
@@ -156,7 +211,6 @@ func (a *Alert) LabelsFingerprint() string {
156211
}
157212

158213
// ContentFingerprint is a checksum computed from entire alert object
159-
// except some blacklisted fields tagged with hash:"-"
160214
func (a *Alert) ContentFingerprint() string {
161215
return a.contentFP
162216
}

internal/models/alertgroup.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
package models
22

33
import (
4-
"crypto/sha1"
54
"fmt"
65
"io"
76
"time"
87

9-
"github.com/cnf/structhash"
8+
"github.com/cespare/xxhash/v2"
109
)
1110

1211
// AlertList is flat list of karmaAlert objects
@@ -48,19 +47,25 @@ type AlertGroup struct {
4847
// LabelsFingerprint is a checksum of this AlertGroup labels and the receiver
4948
// it should be unique for each AlertGroup
5049
func (ag AlertGroup) LabelsFingerprint() string {
51-
agIDHasher := sha1.New()
52-
_, _ = io.WriteString(agIDHasher, ag.Receiver)
53-
_, _ = io.WriteString(agIDHasher, fmt.Sprintf("%x", structhash.Sha1(ag.Labels.Map(), 1)))
54-
return fmt.Sprintf("%x", agIDHasher.Sum(nil))
50+
h := xxhash.New()
51+
_, _ = h.WriteString(ag.Receiver)
52+
_, _ = h.Write(seps)
53+
for _, l := range ag.Labels {
54+
_, _ = h.WriteString(l.Name)
55+
_, _ = h.Write(seps)
56+
_, _ = h.WriteString(l.Value)
57+
_, _ = h.Write(seps)
58+
}
59+
return fmt.Sprintf("%x", h.Sum64())
5560
}
5661

5762
// ContentFingerprint is a checksum of all alerts in the group
5863
func (ag AlertGroup) ContentFingerprint() string {
59-
h := sha1.New()
64+
h := xxhash.New()
6065
for _, alert := range ag.Alerts {
6166
_, _ = io.WriteString(h, alert.ContentFingerprint())
6267
}
63-
return fmt.Sprintf("%x", h.Sum(nil))
68+
return fmt.Sprintf("%x", h.Sum64())
6469
}
6570

6671
func (ag AlertGroup) FindLatestStartsAt() time.Time {

0 commit comments

Comments
 (0)