mirror of
https://github.com/prymitive/karma
synced 2026-05-05 03:16:51 +00:00
Dynamic fingerprints made the code much slower, pprof shows they are responsible for ~70% of all cpu usage for any API call. To make it worse they are applied to all alerts, since dedup layer doesn't know which alerts will be filtered later, it operates on all of them. This PR will: 1. add benchmarks to so it's easier to track performance 2. Keep current methods for accessing fingerprints, but use precomputed static fields in those 3. Refactor Alert methods to use pointers, so we're not working on a copy Benchmark timing went down from ~4000ns to 0.4ns for fingerprint calls and API response times from 1.3s (for my test sample) to 0.2s, which puts it back to the same level as before moving fingerprints to be dynamic. I still don't like this code much, it's all over the place, but I don't have a good idea how to better structure this, let's hope I'll be wiser in the future.
86 lines
2.9 KiB
Go
86 lines
2.9 KiB
Go
package models
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/cnf/structhash"
|
|
)
|
|
|
|
// AlertStateUnprocessed means that Alertmanager notify didn't yet process it
|
|
// and AM doesn't know if alert is active or suppressed
|
|
const AlertStateUnprocessed = "unprocessed"
|
|
|
|
// AlertStateActive is the state in which we know that the alert should fire
|
|
const AlertStateActive = "active"
|
|
|
|
// AlertStateSuppressed means that we know that alert is silenced or inhibited
|
|
const AlertStateSuppressed = "suppressed"
|
|
|
|
// AlertStateList exports all alert states so other packages can get this list
|
|
var AlertStateList = []string{
|
|
AlertStateUnprocessed,
|
|
AlertStateActive,
|
|
AlertStateSuppressed,
|
|
}
|
|
|
|
// Alert is vanilla alert + some additional attributes
|
|
// unsee extends an alert object with:
|
|
// * Links map, it's generated from annotations if annotation value is an url
|
|
// it's pulled out of annotation map and returned under links field,
|
|
// unsee UI used this to show links differently than other annotations
|
|
type Alert struct {
|
|
Annotations map[string]string `json:"annotations"`
|
|
Labels map[string]string `json:"labels"`
|
|
StartsAt time.Time `json:"startsAt"`
|
|
EndsAt time.Time `json:"endsAt"`
|
|
State string `json:"state"`
|
|
// those are not exposed in JSON, Alertmanager specific value will be in kept
|
|
// in the Alertmanager slice
|
|
// skip those when generating alert fingerprint too
|
|
GeneratorURL string `json:"-" hash:"-"`
|
|
SilencedBy []string `json:"-" hash:"-"`
|
|
InhibitedBy []string `json:"-" hash:"-"`
|
|
// unsee fields
|
|
Alertmanager []AlertmanagerInstance `json:"alertmanager"`
|
|
Receiver string `json:"receiver"`
|
|
Links map[string]string `json:"links"`
|
|
// fingerprints are precomputed for speed
|
|
labelsFP string `hash:"-"`
|
|
contentFP string `hash:"-"`
|
|
}
|
|
|
|
// UpdateFingerprints will generate a new set of fingerprints for this alert
|
|
// it should be called after modifying any field that isn't tagged with hash:"-"
|
|
func (a *Alert) UpdateFingerprints() {
|
|
a.labelsFP = fmt.Sprintf("%x", structhash.Sha1(a.Labels, 1))
|
|
a.contentFP = fmt.Sprintf("%x", structhash.Sha1(a, 1))
|
|
}
|
|
|
|
// LabelsFingerprint is a checksum computed only from labels which should be
|
|
// unique for every alert
|
|
func (a *Alert) LabelsFingerprint() string {
|
|
return a.labelsFP
|
|
}
|
|
|
|
// ContentFingerprint is a checksum computed from entire alert object
|
|
// except some blacklisted fields tagged with hash:"-"
|
|
func (a *Alert) ContentFingerprint() string {
|
|
return a.contentFP
|
|
}
|
|
|
|
// IsSilenced will return true if alert should be considered silenced
|
|
func (a *Alert) IsSilenced() bool {
|
|
return (a.State == AlertStateSuppressed && len(a.SilencedBy) > 0)
|
|
}
|
|
|
|
// IsInhibited will return true if alert should be considered silenced
|
|
func (a *Alert) IsInhibited() bool {
|
|
return (a.State == AlertStateSuppressed && len(a.InhibitedBy) > 0)
|
|
}
|
|
|
|
// IsActive will return true if alert is not suppressed in any way
|
|
func (a *Alert) IsActive() bool {
|
|
return (a.State == AlertStateActive)
|
|
}
|