feat(slo): Handle instanceId for historical summary (#163114)

This commit is contained in:
Kevin Delemme 2023-08-03 20:41:51 -04:00 committed by GitHub
parent 2e02a6cf00
commit 498d6fdccc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 4460 additions and 1811 deletions

View file

@ -159,11 +159,15 @@ const findSLOResponseSchema = t.type({
});
const fetchHistoricalSummaryParamsSchema = t.type({
body: t.type({ sloIds: t.array(sloIdSchema) }),
body: t.type({ list: t.array(t.type({ sloId: sloIdSchema, instanceId: allOrAnyString })) }),
});
const fetchHistoricalSummaryResponseSchema = t.record(
sloIdSchema,
t.array(historicalSummarySchema)
const fetchHistoricalSummaryResponseSchema = t.array(
t.type({
sloId: sloIdSchema,
instanceId: allOrAnyString,
data: t.array(historicalSummarySchema),
})
);
const getSLODiagnosisParamsSchema = t.type({

View file

@ -5,18 +5,24 @@
* 2.0.
*/
import { HistoricalSummaryResponse } from '@kbn/slo-schema';
import { FetchHistoricalSummaryResponse } from '@kbn/slo-schema';
import {
HEALTHY_ROLLING_SLO,
historicalSummaryData,
} from '../../../data/slo/historical_summary_data';
import { UseFetchHistoricalSummaryResponse, Params } from '../use_fetch_historical_summary';
import { Params, UseFetchHistoricalSummaryResponse } from '../use_fetch_historical_summary';
export const useFetchHistoricalSummary = ({
sloIds = [],
list = [],
}: Params): UseFetchHistoricalSummaryResponse => {
const data: Record<string, HistoricalSummaryResponse[]> = {};
sloIds.forEach((sloId) => (data[sloId] = historicalSummaryData[HEALTHY_ROLLING_SLO]));
const data: FetchHistoricalSummaryResponse = [];
list.forEach(({ sloId, instanceId }) =>
data.push({
sloId,
instanceId,
data: historicalSummaryData.find((datum) => datum.sloId === HEALTHY_ROLLING_SLO)!.data,
})
);
return {
isLoading: false,

View file

@ -31,7 +31,8 @@ export const sloKeys = {
activeAlerts: () => [...sloKeys.all, 'activeAlerts'] as const,
activeAlert: (sloIds: string[]) => [...sloKeys.activeAlerts(), sloIds] as const,
historicalSummaries: () => [...sloKeys.all, 'historicalSummary'] as const,
historicalSummary: (sloIds: string[]) => [...sloKeys.historicalSummaries(), sloIds] as const,
historicalSummary: (list: Array<{ sloId: string; instanceId: string }>) =>
[...sloKeys.historicalSummaries(), list] as const,
globalDiagnosis: () => [...sloKeys.all, 'globalDiagnosis'] as const,
burnRates: (sloId: string) => [...sloKeys.all, 'burnRates', sloId] as const,
preview: (indicator?: Indicator) => [...sloKeys.all, 'preview', indicator] as const,

View file

@ -21,25 +21,26 @@ export interface UseFetchHistoricalSummaryResponse {
}
export interface Params {
sloIds: string[];
list: Array<{ sloId: string; instanceId: string }>;
shouldRefetch?: boolean;
}
const LONG_REFETCH_INTERVAL = 1000 * 60; // 1 minute
export function useFetchHistoricalSummary({
sloIds = [],
list = [],
shouldRefetch,
}: Params): UseFetchHistoricalSummaryResponse {
const { http } = useKibana().services;
const { isInitialLoading, isLoading, isError, isSuccess, isRefetching, data } = useQuery({
queryKey: sloKeys.historicalSummary(sloIds),
queryKey: sloKeys.historicalSummary(list),
queryFn: async ({ signal }) => {
try {
const response = await http.post<FetchHistoricalSummaryResponse>(
'/internal/observability/slos/_historical_summary',
{
body: JSON.stringify({ sloIds }),
body: JSON.stringify({ list }),
signal,
}
);

View file

@ -13,7 +13,7 @@ import {
EuiTabbedContent,
EuiTabbedContentTab,
} from '@elastic/eui';
import { SLOWithSummaryResponse } from '@kbn/slo-schema';
import { ALL_VALUE, SLOWithSummaryResponse } from '@kbn/slo-schema';
import React, { Fragment, useState } from 'react';
import { i18n } from '@kbn/i18n';
@ -41,14 +41,23 @@ type TabId = typeof OVERVIEW_TAB_ID | typeof ALERTS_TAB_ID;
export function SloDetails({ slo, isAutoRefreshing }: Props) {
const { search } = useLocation();
const { data: activeAlerts } = useFetchActiveAlerts({ sloIds: [slo.id] });
const { isLoading: historicalSummaryLoading, data: historicalSummaryBySlo = {} } =
useFetchHistoricalSummary({ sloIds: [slo.id], shouldRefetch: isAutoRefreshing });
const { isLoading: historicalSummaryLoading, data: historicalSummaries = [] } =
useFetchHistoricalSummary({
list: [{ sloId: slo.id, instanceId: slo.instanceId ?? ALL_VALUE }],
shouldRefetch: isAutoRefreshing,
});
const sloHistoricalSummary = historicalSummaries.find(
(historicalSummary) =>
historicalSummary.sloId === slo.id &&
historicalSummary.instanceId === (slo.instanceId ?? ALL_VALUE)
);
const errorBudgetBurnDownData = formatHistoricalData(
historicalSummaryBySlo[slo.id],
sloHistoricalSummary?.data,
'error_budget_remaining'
);
const historicalSliData = formatHistoricalData(historicalSummaryBySlo[slo.id], 'sli_value');
const historicalSliData = formatHistoricalData(sloHistoricalSummary?.data, 'sli_value');
const tabs: EuiTabbedContentTab[] = [
{

View file

@ -158,7 +158,7 @@ describe('SLO Details Page', () => {
useLicenseMock.mockReturnValue({ hasAtLeast: () => true });
useFetchHistoricalSummaryMock.mockReturnValue({
isLoading: true,
data: {},
data: [],
});
render(<SloDetailsPage />);

View file

@ -28,7 +28,8 @@ const Template: ComponentStory<typeof Component> = (props: SloListItemProps) =>
const defaultProps = {
slo: buildSlo(),
historicalSummary: historicalSummaryData[HEALTHY_ROLLING_SLO],
historicalSummary: historicalSummaryData.find((datum) => datum.sloId === HEALTHY_ROLLING_SLO)!
.data,
};
export const SloListItem = Template.bind({});

View file

@ -25,8 +25,10 @@ export function SloListItems({ sloList, loading, error }: Props) {
const { data: activeAlertsBySlo } = useFetchActiveAlerts({ sloIds });
const { data: rulesBySlo } = useFetchRulesForSlo({ sloIds });
const { isLoading: historicalSummaryLoading, data: historicalSummaryBySlo } =
useFetchHistoricalSummary({ sloIds });
const { isLoading: historicalSummaryLoading, data: historicalSummaries = [] } =
useFetchHistoricalSummary({
list: sloList.map((slo) => ({ sloId: slo.id, instanceId: slo.instanceId ?? ALL_VALUE })),
});
if (!loading && !error && sloList.length === 0) {
return <SloListEmpty />;
@ -42,7 +44,13 @@ export function SloListItems({ sloList, loading, error }: Props) {
<SloListItem
activeAlerts={activeAlertsBySlo[slo.id]}
rules={rulesBySlo?.[slo.id]}
historicalSummary={historicalSummaryBySlo?.[slo.id]}
historicalSummary={
historicalSummaries.find(
(historicalSummary) =>
historicalSummary.sloId === slo.id &&
historicalSummary.instanceId === (slo.instanceId ?? ALL_VALUE)
)?.data
}
historicalSummaryLoading={historicalSummaryLoading}
slo={slo}
/>

View file

@ -5,20 +5,19 @@
* 2.0.
*/
import React from 'react';
import { ComponentStory } from '@storybook/react';
import { HistoricalSummaryResponse } from '@kbn/slo-schema';
import { ComponentStory } from '@storybook/react';
import React from 'react';
import {
HEALTHY_ROLLING_SLO,
DEGRADING_FAST_ROLLING_SLO,
HEALTHY_RANDOM_ROLLING_SLO,
HEALTHY_ROLLING_SLO,
HEALTHY_STEP_DOWN_ROLLING_SLO,
historicalSummaryData,
DEGRADING_FAST_ROLLING_SLO,
NO_DATA_TO_HEALTHY_ROLLING_SLO,
} from '../../../data/slo/historical_summary_data';
import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator';
import { SloSparkline as Component, Props } from './slo_sparkline';
import { Props, SloSparkline as Component } from './slo_sparkline';
export default {
component: Component,
@ -33,7 +32,9 @@ AreaWithHealthyFlatData.args = {
chart: 'area',
state: 'success',
id: 'history',
data: toBudgetBurnDown(historicalSummaryData[HEALTHY_ROLLING_SLO]),
data: toBudgetBurnDown(
historicalSummaryData.find((datum) => datum.sloId === HEALTHY_ROLLING_SLO)!.data
),
};
export const AreaWithHealthyRandomData = Template.bind({});
@ -41,7 +42,9 @@ AreaWithHealthyRandomData.args = {
chart: 'area',
state: 'success',
id: 'history',
data: toBudgetBurnDown(historicalSummaryData[HEALTHY_RANDOM_ROLLING_SLO]),
data: toBudgetBurnDown(
historicalSummaryData.find((datum) => datum.sloId === HEALTHY_RANDOM_ROLLING_SLO)!.data
),
};
export const AreaWithHealthyStepDownData = Template.bind({});
@ -49,7 +52,9 @@ AreaWithHealthyStepDownData.args = {
chart: 'area',
state: 'success',
id: 'history',
data: toBudgetBurnDown(historicalSummaryData[HEALTHY_STEP_DOWN_ROLLING_SLO]),
data: toBudgetBurnDown(
historicalSummaryData.find((datum) => datum.sloId === HEALTHY_STEP_DOWN_ROLLING_SLO)!.data
),
};
export const AreaWithDegradingLinearData = Template.bind({});
@ -57,7 +62,9 @@ AreaWithDegradingLinearData.args = {
chart: 'area',
state: 'error',
id: 'history',
data: toBudgetBurnDown(historicalSummaryData[DEGRADING_FAST_ROLLING_SLO]),
data: toBudgetBurnDown(
historicalSummaryData.find((datum) => datum.sloId === DEGRADING_FAST_ROLLING_SLO)!.data
),
};
export const AreaWithNoDataToDegradingLinearData = Template.bind({});
@ -65,7 +72,9 @@ AreaWithNoDataToDegradingLinearData.args = {
chart: 'area',
state: 'error',
id: 'history',
data: toBudgetBurnDown(historicalSummaryData[NO_DATA_TO_HEALTHY_ROLLING_SLO]),
data: toBudgetBurnDown(
historicalSummaryData.find((datum) => datum.sloId === NO_DATA_TO_HEALTHY_ROLLING_SLO)!.data
),
};
export const LineWithHealthyFlatData = Template.bind({});
@ -73,7 +82,9 @@ LineWithHealthyFlatData.args = {
chart: 'line',
state: 'success',
id: 'history',
data: toSliHistory(historicalSummaryData[HEALTHY_ROLLING_SLO]),
data: toSliHistory(
historicalSummaryData.find((datum) => datum.sloId === HEALTHY_ROLLING_SLO)!.data
),
};
export const LineWithHealthyRandomData = Template.bind({});
@ -81,7 +92,9 @@ LineWithHealthyRandomData.args = {
chart: 'line',
state: 'success',
id: 'history',
data: toSliHistory(historicalSummaryData[HEALTHY_RANDOM_ROLLING_SLO]),
data: toSliHistory(
historicalSummaryData.find((datum) => datum.sloId === HEALTHY_RANDOM_ROLLING_SLO)!.data
),
};
export const LineWithHealthyStepDownData = Template.bind({});
@ -89,7 +102,9 @@ LineWithHealthyStepDownData.args = {
chart: 'line',
state: 'success',
id: 'history',
data: toSliHistory(historicalSummaryData[HEALTHY_STEP_DOWN_ROLLING_SLO]),
data: toSliHistory(
historicalSummaryData.find((datum) => datum.sloId === HEALTHY_STEP_DOWN_ROLLING_SLO)!.data
),
};
export const LineWithDegradingLinearData = Template.bind({});
@ -97,7 +112,9 @@ LineWithDegradingLinearData.args = {
chart: 'line',
state: 'error',
id: 'history',
data: toSliHistory(historicalSummaryData[DEGRADING_FAST_ROLLING_SLO]),
data: toSliHistory(
historicalSummaryData.find((datum) => datum.sloId === DEGRADING_FAST_ROLLING_SLO)!.data
),
};
export const LineWithNoDataToDegradingLinearData = Template.bind({});
@ -105,7 +122,9 @@ LineWithNoDataToDegradingLinearData.args = {
chart: 'line',
state: 'error',
id: 'history',
data: toSliHistory(historicalSummaryData[NO_DATA_TO_HEALTHY_ROLLING_SLO]),
data: toSliHistory(
historicalSummaryData.find((datum) => datum.sloId === NO_DATA_TO_HEALTHY_ROLLING_SLO)!.data
),
};
function toBudgetBurnDown(data: HistoricalSummaryResponse[]) {

View file

@ -5,16 +5,15 @@
* 2.0.
*/
import React from 'react';
import { ComponentStory } from '@storybook/react';
import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator';
import React from 'react';
import {
HEALTHY_ROLLING_SLO,
historicalSummaryData,
} from '../../../data/slo/historical_summary_data';
import { buildSlo } from '../../../data/slo/slo';
import { SloSummary as Component, Props } from './slo_summary';
import { KibanaReactStorybookDecorator } from '../../../utils/kibana_react.storybook_decorator';
import { Props, SloSummary as Component } from './slo_summary';
export default {
component: Component,
@ -26,7 +25,8 @@ const Template: ComponentStory<typeof Component> = (props: Props) => <Component
const defaultProps = {
slo: buildSlo(),
historicalSummary: historicalSummaryData[HEALTHY_ROLLING_SLO],
historicalSummary: historicalSummaryData.find((datum) => datum.sloId === HEALTHY_ROLLING_SLO)!
.data,
historicalSummaryLoading: false,
};

View file

@ -5,17 +5,16 @@
* 2.0.
*/
import { FetchHistoricalSummaryResponse } from '@kbn/slo-schema';
import { HistoricalSummaryResponse } from '@kbn/slo-schema';
import { ChartData } from '../../typings/slo';
type DataType = 'error_budget_remaining' | 'error_budget_consumed' | 'sli_value';
export function formatHistoricalData(
historicalSummary: FetchHistoricalSummaryResponse[string] | undefined,
historicalSummary: HistoricalSummaryResponse[] | undefined = [],
dataType: DataType
): ChartData[] {
function getDataValue(data: FetchHistoricalSummaryResponse[string][number]) {
function getDataValue(data: HistoricalSummaryResponse) {
switch (dataType) {
case 'error_budget_consumed':
return data.errorBudget.consumed;

View file

@ -10,7 +10,7 @@ import {
FetchHistoricalSummaryResponse,
fetchHistoricalSummaryResponseSchema,
} from '@kbn/slo-schema';
import { HistoricalSummaryClient } from './historical_summary_client';
import { HistoricalSummaryClient, SLOWithInstanceId } from './historical_summary_client';
import { SLORepository } from './slo_repository';
export class FetchHistoricalSummary {
@ -19,11 +19,20 @@ export class FetchHistoricalSummary {
private historicalSummaryClient: HistoricalSummaryClient
) {}
public async execute({
sloIds,
}: FetchHistoricalSummaryParams): Promise<FetchHistoricalSummaryResponse> {
public async execute(
params: FetchHistoricalSummaryParams
): Promise<FetchHistoricalSummaryResponse> {
const sloIds = params.list.map((slo) => slo.sloId);
const sloList = await this.repository.findAllByIds(sloIds);
const historicalSummaryBySlo = await this.historicalSummaryClient.fetch(sloList);
return fetchHistoricalSummaryResponseSchema.encode(historicalSummaryBySlo);
const list: SLOWithInstanceId[] = params.list.map(({ sloId, instanceId }) => ({
sloId,
instanceId,
slo: sloList.find((slo) => slo.id === sloId)!,
}));
const historicalSummary = await this.historicalSummaryClient.fetch(list);
return fetchHistoricalSummaryResponseSchema.encode(historicalSummary);
}
}

View file

@ -6,6 +6,7 @@
*/
import { ElasticsearchClientMock, elasticsearchServiceMock } from '@kbn/core/server/mocks';
import { ALL_VALUE } from '@kbn/slo-schema';
import moment from 'moment';
import { oneMinute, oneMonth, thirtyDays } from './fixtures/duration';
import { createSLO } from './fixtures/slo';
@ -140,16 +141,18 @@ describe('FetchHistoricalSummary', () => {
const slo = createSLO({
timeWindow: { type: 'rolling', duration: thirtyDays() },
objective: { target: 0.95 },
groupBy: ALL_VALUE,
});
esClientMock.msearch.mockResolvedValueOnce(generateEsResponseForRollingSLO(30));
const client = new DefaultHistoricalSummaryClient(esClientMock);
const results = await client.fetch([slo]);
results[slo.id].forEach((dailyResult) =>
const results = await client.fetch([{ slo, sloId: slo.id, instanceId: ALL_VALUE }]);
results[0].data.forEach((dailyResult) =>
expect(dailyResult).toMatchSnapshot({ date: expect.any(Date) })
);
expect(results[slo.id]).toHaveLength(180);
expect(results[0].data).toHaveLength(180);
});
});
@ -159,16 +162,17 @@ describe('FetchHistoricalSummary', () => {
timeWindow: { type: 'rolling', duration: thirtyDays() },
budgetingMethod: 'timeslices',
objective: { target: 0.95, timesliceTarget: 0.9, timesliceWindow: oneMinute() },
groupBy: ALL_VALUE,
});
esClientMock.msearch.mockResolvedValueOnce(generateEsResponseForRollingSLO(30));
const client = new DefaultHistoricalSummaryClient(esClientMock);
const results = await client.fetch([slo]);
const results = await client.fetch([{ slo, sloId: slo.id, instanceId: ALL_VALUE }]);
results[slo.id].forEach((dailyResult) =>
results[0].data.forEach((dailyResult) =>
expect(dailyResult).toMatchSnapshot({ date: expect.any(Date) })
);
expect(results[slo.id]).toHaveLength(180);
expect(results[0].data).toHaveLength(180);
});
});
@ -185,13 +189,12 @@ describe('FetchHistoricalSummary', () => {
esClientMock.msearch.mockResolvedValueOnce(generateEsResponseForMonthlyCalendarAlignedSLO());
const client = new DefaultHistoricalSummaryClient(esClientMock);
const results = await client.fetch([slo]);
const results = await client.fetch([{ slo, sloId: slo.id, instanceId: ALL_VALUE }]);
results[slo.id].forEach((dailyResult) =>
results[0].data.forEach((dailyResult) =>
expect(dailyResult).toMatchSnapshot({ date: expect.any(Date) })
);
expect(results[slo.id]).toHaveLength(108);
expect(results[0].data).toHaveLength(108);
});
});
@ -208,13 +211,35 @@ describe('FetchHistoricalSummary', () => {
esClientMock.msearch.mockResolvedValueOnce(generateEsResponseForMonthlyCalendarAlignedSLO());
const client = new DefaultHistoricalSummaryClient(esClientMock);
const results = await client.fetch([slo]);
const results = await client.fetch([{ slo, sloId: slo.id, instanceId: ALL_VALUE }]);
results[slo.id].forEach((dailyResult) =>
results[0].data.forEach((dailyResult) =>
expect(dailyResult).toMatchSnapshot({ date: expect.any(Date) })
);
expect(results[slo.id]).toHaveLength(108);
expect(results[0].data).toHaveLength(108);
});
});
it("filters with the 'instanceId' when provided", async () => {
const slo = createSLO({
timeWindow: { type: 'rolling', duration: thirtyDays() },
objective: { target: 0.95 },
groupBy: 'host',
});
esClientMock.msearch.mockResolvedValueOnce(generateEsResponseForRollingSLO(30));
const client = new DefaultHistoricalSummaryClient(esClientMock);
const results = await client.fetch([{ slo, sloId: slo.id, instanceId: 'host-abc' }]);
expect(
// @ts-ignore
esClientMock.msearch.mock.calls[0][0].searches[1].query.bool.filter[3]
).toEqual({ term: { 'slo.instanceId': 'host-abc' } });
results[0].data.forEach((dailyResult) =>
expect(dailyResult).toMatchSnapshot({ date: expect.any(Date) })
);
expect(results[0].data).toHaveLength(180);
});
});

View file

@ -8,14 +8,17 @@
import { MsearchMultisearchBody } from '@elastic/elasticsearch/lib/api/typesWithBodyKey';
import { ElasticsearchClient } from '@kbn/core/server';
import {
ALL_VALUE,
calendarAlignedTimeWindowSchema,
Duration,
fetchHistoricalSummaryResponseSchema,
occurrencesBudgetingMethodSchema,
rollingTimeWindowSchema,
timeslicesBudgetingMethodSchema,
toMomentUnitOfTime,
} from '@kbn/slo-schema';
import { assertNever } from '@kbn/std';
import * as t from 'io-ts';
import moment from 'moment';
import { SLO_DESTINATION_INDEX_PATTERN } from '../../assets/constants';
import { DateRange, HistoricalSummary, SLO, SLOId } from '../../domain/models';
@ -44,36 +47,44 @@ interface DailyAggBucket {
};
}
export interface SLOWithInstanceId {
sloId: SLOId;
instanceId: string;
slo: SLO;
}
export type HistoricalSummaryResponse = t.TypeOf<typeof fetchHistoricalSummaryResponseSchema>;
export interface HistoricalSummaryClient {
fetch(sloList: SLO[]): Promise<Record<SLOId, HistoricalSummary[]>>;
fetch(list: SLOWithInstanceId[]): Promise<HistoricalSummaryResponse>;
}
export class DefaultHistoricalSummaryClient implements HistoricalSummaryClient {
constructor(private esClient: ElasticsearchClient) {}
async fetch(sloList: SLO[]): Promise<Record<SLOId, HistoricalSummary[]>> {
const dateRangeBySlo = sloList.reduce<Record<SLOId, DateRange>>((acc, slo) => {
acc[slo.id] = getDateRange(slo);
async fetch(list: SLOWithInstanceId[]): Promise<HistoricalSummaryResponse> {
const dateRangeBySlo = list.reduce<Record<SLOId, DateRange>>((acc, { sloId, slo }) => {
acc[sloId] = getDateRange(slo);
return acc;
}, {});
const searches = sloList.flatMap((slo) => [
const searches = list.flatMap(({ sloId, instanceId, slo }) => [
{ index: SLO_DESTINATION_INDEX_PATTERN },
generateSearchQuery(slo, dateRangeBySlo[slo.id]),
generateSearchQuery(slo, instanceId, dateRangeBySlo[sloId]),
]);
const historicalSummaryBySlo: Record<SLOId, HistoricalSummary[]> = {};
const historicalSummary: HistoricalSummaryResponse = [];
if (searches.length === 0) {
return historicalSummaryBySlo;
return historicalSummary;
}
const result = await this.esClient.msearch({ searches });
for (let i = 0; i < result.responses.length; i++) {
const slo = sloList[i];
const { slo, sloId, instanceId } = list[i];
if ('error' in result.responses[i]) {
// handle errorneous responses with an empty historical summary
historicalSummaryBySlo[slo.id] = [];
// handle errorneous responses with an empty historical summary data
historicalSummary.push({ sloId, instanceId, data: [] });
continue;
}
@ -81,26 +92,32 @@ export class DefaultHistoricalSummaryClient implements HistoricalSummaryClient {
const buckets = (result.responses[i].aggregations?.daily?.buckets as DailyAggBucket[]) || [];
if (rollingTimeWindowSchema.is(slo.timeWindow)) {
historicalSummaryBySlo[slo.id] = handleResultForRolling(slo, buckets);
historicalSummary.push({
sloId,
instanceId,
data: handleResultForRolling(slo, buckets),
});
continue;
}
if (calendarAlignedTimeWindowSchema.is(slo.timeWindow)) {
if (timeslicesBudgetingMethodSchema.is(slo.budgetingMethod)) {
const dateRange = dateRangeBySlo[slo.id];
historicalSummaryBySlo[slo.id] = handleResultForCalendarAlignedAndTimeslices(
slo,
buckets,
dateRange
);
const dateRange = dateRangeBySlo[sloId];
historicalSummary.push({
sloId,
instanceId,
data: handleResultForCalendarAlignedAndTimeslices(slo, buckets, dateRange),
});
continue;
}
if (occurrencesBudgetingMethodSchema.is(slo.budgetingMethod)) {
historicalSummaryBySlo[slo.id] = handleResultForCalendarAlignedAndOccurrences(
slo,
buckets
);
historicalSummary.push({
sloId,
instanceId,
data: handleResultForCalendarAlignedAndOccurrences(slo, buckets),
});
continue;
}
@ -110,7 +127,7 @@ export class DefaultHistoricalSummaryClient implements HistoricalSummaryClient {
assertNever(slo.timeWindow);
}
return historicalSummaryBySlo;
return historicalSummary;
}
}
@ -186,13 +203,22 @@ function handleResultForRolling(slo: SLO, buckets: DailyAggBucket[]): Historical
});
}
function generateSearchQuery(slo: SLO, dateRange: DateRange): MsearchMultisearchBody {
function generateSearchQuery(
slo: SLO,
instanceId: string,
dateRange: DateRange
): MsearchMultisearchBody {
const unit = toMomentUnitOfTime(slo.timeWindow.duration.unit);
const timeWindowDurationInDays = moment.duration(slo.timeWindow.duration.value, unit).asDays();
const { fixedInterval, bucketsPerDay } =
getFixedIntervalAndBucketsPerDay(timeWindowDurationInDays);
const extraFilterByInstanceId =
!!slo.groupBy && slo.groupBy !== ALL_VALUE && instanceId !== ALL_VALUE
? [{ term: { 'slo.instanceId': instanceId } }]
: [];
return {
size: 0,
query: {
@ -208,6 +234,7 @@ function generateSearchQuery(slo: SLO, dateRange: DateRange): MsearchMultisearch
},
},
},
...extraFilterByInstanceId,
],
},
},