Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,6 @@ export const Expressions: React.FC<Props> = (props) => {
[onFilterChange]
);

const areAllAggsRate = useMemo(
() => ruleParams.criteria?.every((c) => c.aggType === Aggregators.RATE),
[ruleParams.criteria]
);

const hasGroupBy = useMemo(
() => ruleParams.groupBy && ruleParams.groupBy.length > 0,
[ruleParams.groupBy]
Expand Down Expand Up @@ -387,31 +382,6 @@ export const Expressions: React.FC<Props> = (props) => {
checked={ruleParams.alertOnNoData}
onChange={(e) => setRuleParams('alertOnNoData', e.target.checked)}
/>
<EuiCheckbox
id="metrics-alert-partial-buckets-toggle"
label={
<>
{i18n.translate('xpack.infra.metrics.alertFlyout.shouldDropPartialBuckets', {
defaultMessage: 'Drop partial buckets when evaluating data',
})}{' '}
<EuiToolTip
content={i18n.translate(
'xpack.infra.metrics.alertFlyout.dropPartialBucketsHelpText',
{
defaultMessage:
"Enable this to drop the most recent bucket of evaluation data if it's less than {timeSize}{timeUnit}.",
values: { timeSize, timeUnit },
}
)}
>
<EuiIcon type="questionInCircle" color="subdued" />
</EuiToolTip>
</>
}
checked={areAllAggsRate || ruleParams.shouldDropPartialBuckets}
disabled={areAllAggsRate}
onChange={(e) => setRuleParams('shouldDropPartialBuckets', e.target.checked)}
/>
</EuiPanel>
</EuiAccordion>
<EuiSpacer size={'m'} />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { InfraTimerangeInput } from '../../../../../common/http_api';

export const calculateRateTimeranges = (timerange: InfraTimerangeInput) => {
export const calculateRateTimeranges = (timerange: { to: number; from: number }) => {
// This is the total number of milliseconds for the entire timerange
const totalTime = timerange.to - timerange.from;
// Halfway is the to minus half the total time;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import {
Aggregators,
Comparator,
MetricExpressionParams,
} from '../../../../../common/alerting/metrics';
import { createConditionScript } from './create_condition_script';

const EMPTY_SHOULD_WARN = {
bucket_script: {
buckets_path: {},
script: '0',
},
};

export const createBucketSelector = (
condition: MetricExpressionParams,
alertOnGroupDisappear: boolean = false
) => {
const hasWarn = condition.warningThreshold != null && condition.warningComparator != null;
const isPercentile = [Aggregators.P95, Aggregators.P99].includes(condition.aggType);
const bucketPath = isPercentile
? `aggregatedValue[${condition.aggType === Aggregators.P95 ? '95' : '99'}]`
: 'aggregatedValue';

const shouldWarn = hasWarn
? {
bucket_script: {
buckets_path: {
value: bucketPath,
},
script: createConditionScript(
condition.warningThreshold as number[],
condition.warningComparator as Comparator
),
},
}
: EMPTY_SHOULD_WARN;

const shouldTrigger = {
bucket_script: {
buckets_path: {
value: bucketPath,
},
script: createConditionScript(condition.threshold, condition.comparator),
},
};

const aggs: any = {
shouldWarn,
shouldTrigger,
};

if (!alertOnGroupDisappear) {
aggs.selectedBucket = {
bucket_selector: {
buckets_path: {
shouldWarn: 'shouldWarn',
shouldTrigger: 'shouldTrigger',
},
script: 'params.shouldWarn > 0 || params.shouldTrigger > 0',
},
};
}
return aggs;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { Comparator } from '../../../../../common/alerting/metrics';

export const createConditionScript = (threshold: number[], comparator: Comparator) => {
if (comparator === Comparator.BETWEEN && threshold.length === 2) {
return `params.value > ${threshold[0]} && params.value < ${threshold[1]} ? 1 : 0`;
}
if (comparator === Comparator.OUTSIDE_RANGE && threshold.length === 2) {
return `params.value < ${threshold[0]} && params.value > ${threshold[1]} ? 1 : 0`;
}
return `params.value ${comparator} ${threshold[0]} ? 1 : 0`;
};
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export const createPercentileAggregation = (
percentiles: {
field,
percents: [value],
keyed: false,
keyed: true,
},
},
};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { calculateRateTimeranges } from '../../inventory_metric_threshold/lib/calculate_rate_timeranges';

export const createRateAggs = (
timeframe: { start: number; end: number },
id: string,
field: string
) => {
const { firstBucketRange, secondBucketRange, intervalInSeconds } = calculateRateTimeranges({
to: timeframe.end,
from: timeframe.start,
});

return {
[`${id}_first_bucket`]: {
filter: {
range: {
'@timestamp': {
gte: firstBucketRange.from,
lt: firstBucketRange.to,
format: 'epoch_millis',
},
},
},
aggs: { maxValue: { max: { field } } },
},
[`${id}_second_bucket`]: {
filter: {
range: {
'@timestamp': {
gte: secondBucketRange.from,
lt: secondBucketRange.to,
format: 'epoch_millis',
},
},
},
aggs: { maxValue: { max: { field } } },
},
[id]: {
bucket_script: {
buckets_path: {
first: `${id}_first_bucket.maxValue`,
second: `${id}_second_bucket.maxValue`,
},
script: `params.second > 0.0 && params.first > 0.0 && params.second > params.first ? (params.second - params.first) / ${intervalInSeconds}: null`,
},
},
};
};
Original file line number Diff line number Diff line change
Expand Up @@ -36,23 +36,23 @@ describe('createTimerange(interval, aggType, timeframe)', () => {
describe('Rate Aggs', () => {
it('should return a 20 second range for last 1 second', () => {
const subject = createTimerange(1000, Aggregators.RATE);
expect(subject.end - subject.start).toEqual(1000 * 5);
expect(subject.end - subject.start).toEqual(1000 * 2);
});
it('should return a 5 minute range for last 1 minute', () => {
const subject = createTimerange(60000, Aggregators.RATE);
expect(subject.end - subject.start).toEqual(60000 * 5);
expect(subject.end - subject.start).toEqual(60000 * 2);
});
it('should return 25 minute range for last 5 minutes', () => {
const subject = createTimerange(300000, Aggregators.RATE);
expect(subject.end - subject.start).toEqual(300000 * 5);
expect(subject.end - subject.start).toEqual(300000 * 2);
});
it('should return 5 hour range for last hour', () => {
const subject = createTimerange(3600000, Aggregators.RATE);
expect(subject.end - subject.start).toEqual(3600000 * 5);
expect(subject.end - subject.start).toEqual(3600000 * 2);
});
it('should return a 5 day range for last day', () => {
const subject = createTimerange(86400000, Aggregators.RATE);
expect(subject.end - subject.start).toEqual(86400000 * 5);
expect(subject.end - subject.start).toEqual(86400000 * 2);
});
});
});
Expand All @@ -78,23 +78,23 @@ describe('createTimerange(interval, aggType, timeframe)', () => {
});
});
describe('Rate Aggs', () => {
it('should return 25 minute range when given 4 minute timeframe', () => {
it('should return 8 minute range when given 4 minute timeframe', () => {
const end = moment();
const timeframe = {
start: end.clone().subtract(4, 'minutes').valueOf(),
end: end.valueOf(),
};
const subject = createTimerange(300000, Aggregators.RATE, timeframe);
expect(subject.end - subject.start).toEqual(300000 * 5);
expect(subject.end - subject.start).toEqual(300000 * 2);
});
it('should return 25 minute range when given 6 minute timeframe', () => {
it('should return 12 minute range when given 6 minute timeframe', () => {
const end = moment();
const timeframe = {
start: end.clone().subtract(6, 'minutes').valueOf(),
end: end.valueOf(),
};
const subject = createTimerange(300000, Aggregators.RATE, timeframe);
expect(subject.end - subject.start).toEqual(300000 * 5);
expect(subject.end - subject.start).toEqual(300000 * 2);
});
});
});
Expand All @@ -113,7 +113,7 @@ describe('createTimerange(interval, aggType, timeframe)', () => {
});
});
describe('Rate Aggs', () => {
it('should return 25 minute range for last 5 minutes', () => {
it('should return 10 minute range for last 5 minutes', () => {
const end = moment();
const timeframe = {
end: end.valueOf(),
Expand All @@ -122,7 +122,7 @@ describe('createTimerange(interval, aggType, timeframe)', () => {
expect(subject).toEqual({
start: end
.clone()
.subtract(300 * 5, 'seconds')
.subtract(300 * 2, 'seconds')
.valueOf(),
end: end.valueOf(),
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export const createTimerange = (
const to = moment(timeframe ? timeframe.end : Date.now()).valueOf();

// Rate aggregations need 5 buckets worth of data
const minimumBuckets = aggType === Aggregators.RATE ? 5 : 1;
const minimumBuckets = aggType === Aggregators.RATE ? 2 : 1;

const calculatedFrom = to - interval * minimumBuckets;

Expand Down
Loading