Skip to content

Commit 987ae3f

Browse files
committedJun 24, 2021
Add jitter_percent option on fake
Signed-off-by: Xabier Larrakoetxea <me@slok.dev>
1 parent 4ec0cfe commit 987ae3f

File tree

5 files changed

+67
-13
lines changed

5 files changed

+67
-13
lines changed
 

‎plugins/fake/README.md

+12
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ You can get more information on the [SRE workbook `Alert on burn rate`][examples
77
## Options
88

99
- `burn_rate`: (**Required**) A number that tells the burn rate factor (e.g: `1`, `2`, `10`...).
10+
- `jitter_percent`: (**Optional**) A percent number that will add/remove jitter on the burned rate.
1011

1112
## Metric requirements
1213

@@ -64,4 +65,15 @@ sli:
6465
burn_rate: "1000"
6566
```
6667

68+
### 1x speed `30d` window, consumed in `30d` using jitter
69+
70+
```yaml
71+
sli:
72+
plugin:
73+
id: "sloth-common/fake"
74+
options:
75+
burn_rate: "1"
76+
jitter_percent: "10"
77+
```
78+
6779
[examples-sre-book]: https://sre.google/workbook/alerting-on-slos/#burn_rates_and_time_to_complete_budget_ex

‎plugins/fake/plugin.go

+28-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ const (
1313

1414
// SLIPlugin will return a query that will fake a burning error budget at the desired speed using
1515
// `burn_rate` option.
16+
// The plugins also accepts a `jitter_percent` that will add/remove a jitter in the range of that jitter percent.
1617
func SLIPlugin(ctx context.Context, meta, labels, options map[string]string) (string, error) {
1718
objective, err := getSLOObjective(meta)
1819
if err != nil {
@@ -24,16 +25,28 @@ func SLIPlugin(ctx context.Context, meta, labels, options map[string]string) (st
2425
return "", fmt.Errorf("could not get burn rate: %w", err)
2526
}
2627

28+
jitter, err := getJitterPercent(options)
29+
if err != nil {
30+
return "", fmt.Errorf("could not get jitter percent: %w", err)
31+
}
32+
2733
// Get the error budget.
2834
errorBudgetPerc := 100 - objective
2935
errorBudgetRatio := errorBudgetPerc / 100
3036

3137
// Apply factor (burn rate) to error budget.
3238
expectedSLIError := errorBudgetRatio * burnRate
3339

40+
// Create regular query and if no jitter required then regular query
3441
query := fmt.Sprintf(`max_over_time(vector(%f)[{{.window}}:])`, expectedSLIError)
42+
if jitter == 0 {
43+
return query, nil
44+
}
3545

36-
return query, nil
46+
// Create jitter (this is the best random I could came up with) and rest to the regular query.
47+
jitterQuery := fmt.Sprintf(`(%f * (%f - ((time() * minute() * hour() * day_of_week() * month()) %% %f))) / 100`, expectedSLIError, jitter, jitter*2)
48+
49+
return fmt.Sprintf("(%s) - (%s)", query, jitterQuery), nil
3750
}
3851

3952
func getBurnRate(options map[string]string) (float64, error) {
@@ -50,6 +63,20 @@ func getBurnRate(options map[string]string) (float64, error) {
5063
return burnRate, nil
5164
}
5265

66+
func getJitterPercent(options map[string]string) (float64, error) {
67+
jitterPercentS, ok := options["jitter_percent"]
68+
if !ok {
69+
return 0, nil
70+
}
71+
72+
jitterPercent, err := strconv.ParseFloat(jitterPercentS, 64)
73+
if err != nil {
74+
return 0, fmt.Errorf("not a valid jitter_percent, can't parse to float64: %w", err)
75+
}
76+
77+
return jitterPercent, nil
78+
}
79+
5380
func getSLOObjective(meta map[string]string) (float64, error) {
5481
objectiveS, ok := meta["objective"]
5582
if !ok {

‎plugins/fake/plugin_test.go

+12
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,18 @@ func TestSLIPlugin(t *testing.T) {
6464
options: map[string]string{"burn_rate": "0.5"},
6565
expQuery: `max_over_time(vector(0.010000)[{{.window}}:])`,
6666
},
67+
68+
"Having objective, burn rate and jitter, should return a correct query (speed 1, +-10%).": {
69+
meta: map[string]string{"objective": "99"},
70+
options: map[string]string{"burn_rate": "1", "jitter_percent": "10"},
71+
expQuery: `(max_over_time(vector(0.010000)[{{.window}}:])) - ((0.010000 * (10.000000 - ((time() * minute() * hour() * day_of_week() * month()) % 20.000000))) / 100)`,
72+
},
73+
74+
"Having objective, burn rate and jitter, should return a correct query (speed ~2 +-50%).": {
75+
meta: map[string]string{"objective": "99.9"},
76+
options: map[string]string{"burn_rate": "2", "jitter_percent": "50"},
77+
expQuery: `(max_over_time(vector(0.002000)[{{.window}}:])) - ((0.002000 * (50.000000 - ((time() * minute() * hour() * day_of_week() * month()) % 100.000000))) / 100)`,
78+
},
6779
}
6880

6981
for name, test := range tests {

‎scripts/check/integration-test.sh

+1-12
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,4 @@ IFS=$'\t\n'
66

77
command -v sloth >/dev/null 2>&1 || { echo 'please install sloth'; exit 1; }
88

9-
set +f # Allow asterisk expansion.
10-
11-
# Load all plugins and try generating SLOs without error, for now this is good enough
12-
# for integration tests along with each plugin unit tests.
13-
for file in ./test/integration/*.yml; do
14-
fname=$(basename "$file")
15-
echo -n "[TEST] [${file}] Generating SLOs..."
16-
sloth generate -p ./plugins -i "${file}" --no-log > /dev/null
17-
echo ": OK"
18-
done
19-
20-
set -f
9+
sloth validate -p ./plugins -i ./test/integration/ --debug

‎test/integration/fake.yml

+14
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,17 @@ slos:
2626
disable: true
2727
ticket_alert:
2828
disable: true
29+
30+
- name: "test-jitter"
31+
objective: 99
32+
sli:
33+
plugin:
34+
id: "sloth-common/fake"
35+
options:
36+
burn_rate: "1000"
37+
jitter_percent: "10"
38+
alerting:
39+
page_alert:
40+
disable: true
41+
ticket_alert:
42+
disable: true

0 commit comments

Comments
 (0)
Please sign in to comment.