Skip to content

Commit

Permalink
feat(slo): Consider empty slice as good slice for sli calculation pur…
Browse files Browse the repository at this point in the history
…poses on timeslice budgeting method (#181888)
  • Loading branch information
kdelemme authored Apr 30, 2024
1 parent d4c6e07 commit 2d63f72
Show file tree
Hide file tree
Showing 20 changed files with 1,169 additions and 1,134 deletions.
9 changes: 1 addition & 8 deletions x-pack/packages/kbn-slo-schema/src/schema/indicators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*/

import * as t from 'io-ts';
import { allOrAnyString, dateRangeSchema } from './common';
import { allOrAnyString } from './common';

const kqlQuerySchema = t.string;

Expand Down Expand Up @@ -271,12 +271,6 @@ const syntheticsAvailabilityIndicatorSchema = t.type({
]),
});

const indicatorDataSchema = t.type({
dateRange: dateRangeSchema,
good: t.number,
total: t.number,
});

const indicatorTypesSchema = t.union([
apmTransactionDurationIndicatorTypeSchema,
apmTransactionErrorRateIndicatorTypeSchema,
Expand Down Expand Up @@ -344,5 +338,4 @@ export {
indicatorSchema,
indicatorTypesArraySchema,
indicatorTypesSchema,
indicatorDataSchema,
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,22 @@
* 2.0.
*/

import * as t from 'io-ts';
import {
apmTransactionDurationIndicatorSchema,
apmTransactionErrorRateIndicatorSchema,
indicatorDataSchema,
indicatorSchema,
indicatorTypesSchema,
kqlCustomIndicatorSchema,
metricCustomIndicatorSchema,
} from '@kbn/slo-schema';
import * as t from 'io-ts';

type APMTransactionErrorRateIndicator = t.TypeOf<typeof apmTransactionErrorRateIndicatorSchema>;
type APMTransactionDurationIndicator = t.TypeOf<typeof apmTransactionDurationIndicatorSchema>;
type KQLCustomIndicator = t.TypeOf<typeof kqlCustomIndicatorSchema>;
type MetricCustomIndicator = t.TypeOf<typeof metricCustomIndicatorSchema>;
type Indicator = t.TypeOf<typeof indicatorSchema>;
type IndicatorTypes = t.TypeOf<typeof indicatorTypesSchema>;
type IndicatorData = t.TypeOf<typeof indicatorDataSchema>;

export type {
Indicator,
Expand All @@ -31,5 +29,4 @@ export type {
APMTransactionDurationIndicator,
KQLCustomIndicator,
MetricCustomIndicator,
IndicatorData,
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,59 +5,32 @@
* 2.0.
*/

import { computeBurnRate } from './compute_burn_rate';
import { toDateRange } from './date_range';
import { createSLO } from '../../services/fixtures/slo';
import { ninetyDaysRolling } from '../../services/fixtures/time_window';
import { computeBurnRate } from './compute_burn_rate';

describe('computeBurnRate', () => {
it('computes 0 when total is 0', () => {
expect(
computeBurnRate(createSLO(), {
good: 10,
total: 0,
dateRange: toDateRange(ninetyDaysRolling()),
})
).toEqual(0);
it('computes 0 when sliValue is 1', () => {
const sliValue = 1;
expect(computeBurnRate(createSLO(), sliValue)).toEqual(0);
});

it('computes 0 when good is greater than total', () => {
expect(
computeBurnRate(createSLO(), {
good: 9999,
total: 1,
dateRange: toDateRange(ninetyDaysRolling()),
})
).toEqual(0);
it('computes 0 when sliValue is greater than 1', () => {
const sliValue = 1.21;
expect(computeBurnRate(createSLO(), sliValue)).toEqual(0);
});

it('computes the burn rate as 1x the error budget', () => {
expect(
computeBurnRate(createSLO({ objective: { target: 0.9 } }), {
good: 90,
total: 100,
dateRange: toDateRange(ninetyDaysRolling()),
})
).toEqual(1);
const sliValue = 0.9;
expect(computeBurnRate(createSLO({ objective: { target: 0.9 } }), sliValue)).toEqual(1);
});

it('computes the burn rate as 10x the error budget', () => {
expect(
computeBurnRate(createSLO({ objective: { target: 0.99 } }), {
good: 90,
total: 100,
dateRange: toDateRange(ninetyDaysRolling()),
})
).toEqual(10);
const sliValue = 0.9;
expect(computeBurnRate(createSLO({ objective: { target: 0.99 } }), sliValue)).toEqual(10);
});

it('computes the burn rate as 0.5x the error budget', () => {
expect(
computeBurnRate(createSLO({ objective: { target: 0.8 } }), {
good: 90,
total: 100,
dateRange: toDateRange(ninetyDaysRolling()),
})
).toEqual(0.5);
const sliValue = 0.9;
expect(computeBurnRate(createSLO({ objective: { target: 0.8 } }), sliValue)).toEqual(0.5);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,18 @@
*/

import { toHighPrecision } from '../../utils/number';
import { IndicatorData, SLODefinition } from '../models';
import { SLODefinition } from '../models';

/**
* A Burn Rate is computed with the Indicator Data retrieved from a specific lookback period
* A Burn Rate is computed with the sliValue retrieved from a specific lookback period
* It tells how fast we are consumming our error budget during a specific period
*/
export function computeBurnRate(slo: SLODefinition, sliData: IndicatorData): number {
const { good, total } = sliData;
if (total === 0 || good >= total) {
export function computeBurnRate(slo: SLODefinition, sliValue: number): number {
if (sliValue >= 1) {
return 0;
}

const errorBudget = 1 - slo.objective.target;
const errorRate = 1 - good / total;
const errorRate = 1 - sliValue;
return toHighPrecision(errorRate / errorBudget);
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,8 @@ describe('computeSLI', () => {
it('returns rounds the value to 6 digits', () => {
expect(computeSLI(33, 90)).toEqual(0.366667);
});

it('returns the sli value using totalSlicesInRange when provided', () => {
expect(computeSLI(90, 100, 10_080)).toEqual(0.999008);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,14 @@ import { toHighPrecision } from '../../utils/number';

const NO_DATA = -1;

export function computeSLI(good: number, total: number): number {
export function computeSLI(good: number, total: number, totalSlicesInRange?: number): number {
// We calculate the sli based on the totalSlices in the dateRange, as
// 1 - error rate observed = 1 - (1 - SLI Observed) = SLI
// a slice without data will be considered as a good slice
if (totalSlicesInRange !== undefined && totalSlicesInRange > 0) {
return toHighPrecision(1 - (total - good) / totalSlicesInRange);
}

if (total === 0) {
return NO_DATA;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -596,7 +596,8 @@ const getSloBurnRates = createSloServerRoute({
const esClient = (await context.core).elasticsearch.client.asCurrentUser;
const soClient = (await context.core).savedObjects.client;
const { instanceId, windows, remoteName } = params.body;
const burnRates = await getBurnRates({

return await getBurnRates({
instanceId,
spaceId,
windows,
Expand All @@ -608,7 +609,6 @@ const getSloBurnRates = createSloServerRoute({
logger,
},
});
return { burnRates };
},
});

Expand Down
Loading

0 comments on commit 2d63f72

Please sign in to comment.