mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 02:09:32 -04:00
[Metrics UI] Display Too Many Buckets error when previewing Inventory Alerts (#70508)
* [Metrics UI] Display Too Many Buckets error when previewing Inventory Alerts * Fix typecheck
This commit is contained in:
parent
e7c54d3684
commit
5e869b0e77
3 changed files with 103 additions and 74 deletions
|
@ -5,6 +5,10 @@
|
|||
*/
|
||||
import { mapValues, last, first } from 'lodash';
|
||||
import moment from 'moment';
|
||||
import {
|
||||
isTooManyBucketsPreviewException,
|
||||
TOO_MANY_BUCKETS_PREVIEW_EXCEPTION,
|
||||
} from '../../../../common/alerting/metrics';
|
||||
import {
|
||||
InfraDatabaseSearchResponse,
|
||||
CallWithRequestParams,
|
||||
|
@ -57,18 +61,23 @@ export const evaluateCondition = async (
|
|||
|
||||
const comparisonFunction = comparatorMap[comparator];
|
||||
|
||||
return mapValues(currentValues, (value) => ({
|
||||
...condition,
|
||||
shouldFire:
|
||||
value !== undefined &&
|
||||
value !== null &&
|
||||
(Array.isArray(value)
|
||||
? value.map((v) => comparisonFunction(Number(v), threshold))
|
||||
: comparisonFunction(value, threshold)),
|
||||
isNoData: value === null,
|
||||
isError: value === undefined,
|
||||
currentValue: getCurrentValue(value),
|
||||
}));
|
||||
const result = mapValues(currentValues, (value) => {
|
||||
if (isTooManyBucketsPreviewException(value)) throw value;
|
||||
return {
|
||||
...condition,
|
||||
shouldFire:
|
||||
value !== undefined &&
|
||||
value !== null &&
|
||||
(Array.isArray(value)
|
||||
? value.map((v) => comparisonFunction(Number(v), threshold))
|
||||
: comparisonFunction(value as number, threshold)),
|
||||
isNoData: value === null,
|
||||
isError: value === undefined,
|
||||
currentValue: getCurrentValue(value),
|
||||
};
|
||||
}) as unknown; // Typescript doesn't seem to know what `throw` is doing
|
||||
|
||||
return result as Record<string, ConditionResult>;
|
||||
};
|
||||
|
||||
const getCurrentValue: (value: any) => number = (value) => {
|
||||
|
@ -99,21 +108,36 @@ const getData = async (
|
|||
timerange,
|
||||
includeTimeseries: Boolean(timerange.lookbackSize),
|
||||
};
|
||||
try {
|
||||
const { nodes } = await snapshot.getNodes(esClient, options);
|
||||
|
||||
const { nodes } = await snapshot.getNodes(esClient, options);
|
||||
|
||||
return nodes.reduce((acc, n) => {
|
||||
const nodePathItem = last(n.path) as any;
|
||||
const m = first(n.metrics);
|
||||
if (m && m.value && m.timeseries) {
|
||||
const { timeseries } = m;
|
||||
const values = timeseries.rows.map((row) => row.metric_0) as Array<number | null>;
|
||||
acc[nodePathItem.label] = values;
|
||||
} else {
|
||||
acc[nodePathItem.label] = m && m.value;
|
||||
return nodes.reduce((acc, n) => {
|
||||
const nodePathItem = last(n.path) as any;
|
||||
const m = first(n.metrics);
|
||||
if (m && m.value && m.timeseries) {
|
||||
const { timeseries } = m;
|
||||
const values = timeseries.rows.map((row) => row.metric_0) as Array<number | null>;
|
||||
acc[nodePathItem.label] = values;
|
||||
} else {
|
||||
acc[nodePathItem.label] = m && m.value;
|
||||
}
|
||||
return acc;
|
||||
}, {} as Record<string, number | Array<number | string | null | undefined> | undefined | null>);
|
||||
} catch (e) {
|
||||
if (timerange.lookbackSize) {
|
||||
// This code should only ever be reached when previewing the alert, not executing it
|
||||
const causedByType = e.body?.error?.caused_by?.type;
|
||||
if (causedByType === 'too_many_buckets_exception') {
|
||||
return {
|
||||
'*': {
|
||||
[TOO_MANY_BUCKETS_PREVIEW_EXCEPTION]: true,
|
||||
maxBuckets: e.body.error.caused_by.max_buckets,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
return acc;
|
||||
}, {} as Record<string, number | Array<number | string | null | undefined> | undefined | null>);
|
||||
return { '*': undefined };
|
||||
}
|
||||
};
|
||||
|
||||
const comparatorMap = {
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
import { Unit } from '@elastic/datemath';
|
||||
import { first } from 'lodash';
|
||||
import { InventoryMetricConditions } from './types';
|
||||
import {
|
||||
TOO_MANY_BUCKETS_PREVIEW_EXCEPTION,
|
||||
isTooManyBucketsPreviewException,
|
||||
} from '../../../../common/alerting/metrics';
|
||||
import { ILegacyScopedClusterClient } from '../../../../../../../src/core/server';
|
||||
import { InfraSource } from '../../../../common/http_api/source_api';
|
||||
import { getIntervalInSeconds } from '../../../utils/get_interval_in_seconds';
|
||||
|
@ -46,38 +50,43 @@ export const previewInventoryMetricThresholdAlert = async ({
|
|||
|
||||
const alertIntervalInSeconds = getIntervalInSeconds(alertInterval);
|
||||
const alertResultsPerExecution = alertIntervalInSeconds / bucketIntervalInSeconds;
|
||||
|
||||
const results = await Promise.all(
|
||||
criteria.map((c) =>
|
||||
evaluateCondition(c, nodeType, config, callCluster, filterQuery, lookbackSize)
|
||||
)
|
||||
);
|
||||
|
||||
const inventoryItems = Object.keys(first(results) as any);
|
||||
const previewResults = inventoryItems.map((item) => {
|
||||
const isNoData = results.some((result) => result[item].isNoData);
|
||||
if (isNoData) {
|
||||
return null;
|
||||
}
|
||||
const isError = results.some((result) => result[item].isError);
|
||||
if (isError) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const numberOfResultBuckets = lookbackSize;
|
||||
const numberOfExecutionBuckets = Math.floor(numberOfResultBuckets / alertResultsPerExecution);
|
||||
return [...Array(numberOfExecutionBuckets)].reduce(
|
||||
(totalFired, _, i) =>
|
||||
totalFired +
|
||||
(results.every((result) => {
|
||||
const shouldFire = result[item].shouldFire as boolean[];
|
||||
return shouldFire[Math.floor(i * alertResultsPerExecution)];
|
||||
})
|
||||
? 1
|
||||
: 0),
|
||||
0
|
||||
try {
|
||||
const results = await Promise.all(
|
||||
criteria.map((c) =>
|
||||
evaluateCondition(c, nodeType, config, callCluster, filterQuery, lookbackSize)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
return previewResults;
|
||||
const inventoryItems = Object.keys(first(results) as any);
|
||||
const previewResults = inventoryItems.map((item) => {
|
||||
const isNoData = results.some((result) => result[item].isNoData);
|
||||
if (isNoData) {
|
||||
return null;
|
||||
}
|
||||
const isError = results.some((result) => result[item].isError);
|
||||
if (isError) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const numberOfResultBuckets = lookbackSize;
|
||||
const numberOfExecutionBuckets = Math.floor(numberOfResultBuckets / alertResultsPerExecution);
|
||||
return [...Array(numberOfExecutionBuckets)].reduce(
|
||||
(totalFired, _, i) =>
|
||||
totalFired +
|
||||
(results.every((result) => {
|
||||
const shouldFire = result[item].shouldFire as boolean[];
|
||||
return shouldFire[Math.floor(i * alertResultsPerExecution)];
|
||||
})
|
||||
? 1
|
||||
: 0),
|
||||
0
|
||||
);
|
||||
});
|
||||
|
||||
return previewResults;
|
||||
} catch (e) {
|
||||
if (!isTooManyBucketsPreviewException(e)) throw e;
|
||||
const { maxBuckets } = e;
|
||||
throw new Error(`${TOO_MANY_BUCKETS_PREVIEW_EXCEPTION}:${maxBuckets}`);
|
||||
}
|
||||
};
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import { mapValues, first, last, isNaN } from 'lodash';
|
||||
import {
|
||||
TooManyBucketsPreviewExceptionMetadata,
|
||||
isTooManyBucketsPreviewException,
|
||||
TOO_MANY_BUCKETS_PREVIEW_EXCEPTION,
|
||||
} from '../../../../../common/alerting/metrics';
|
||||
|
@ -58,22 +57,19 @@ export const evaluateAlert = (
|
|||
);
|
||||
const { threshold, comparator } = criterion;
|
||||
const comparisonFunction = comparatorMap[comparator];
|
||||
return mapValues(
|
||||
currentValues,
|
||||
(values: number | number[] | null | TooManyBucketsPreviewExceptionMetadata) => {
|
||||
if (isTooManyBucketsPreviewException(values)) throw values;
|
||||
return {
|
||||
...criterion,
|
||||
metric: criterion.metric ?? DOCUMENT_COUNT_I18N,
|
||||
currentValue: Array.isArray(values) ? last(values) : NaN,
|
||||
shouldFire: Array.isArray(values)
|
||||
? values.map((value) => comparisonFunction(value, threshold))
|
||||
: [false],
|
||||
isNoData: values === null,
|
||||
isError: isNaN(values),
|
||||
};
|
||||
}
|
||||
);
|
||||
return mapValues(currentValues, (values: number | number[] | null) => {
|
||||
if (isTooManyBucketsPreviewException(values)) throw values;
|
||||
return {
|
||||
...criterion,
|
||||
metric: criterion.metric ?? DOCUMENT_COUNT_I18N,
|
||||
currentValue: Array.isArray(values) ? last(values) : NaN,
|
||||
shouldFire: Array.isArray(values)
|
||||
? values.map((value) => comparisonFunction(value, threshold))
|
||||
: [false],
|
||||
isNoData: values === null,
|
||||
isError: isNaN(values),
|
||||
};
|
||||
});
|
||||
})
|
||||
);
|
||||
};
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue