Skip to content

Commit 5c0f684

Browse files
authored
fix(anomaly detection): handle anomaly detection edge case (#92596)
Follow-up to #91782. Moves the time window consistency check before making the request to Seer. We should also follow-up with a fix to the underlying problem: async requests should be tracked against the time windows and we should be able to match historical and current datasets based on those time windows.
1 parent 383be82 commit 5c0f684

File tree

1 file changed

+26
-20
lines changed

1 file changed

+26
-20
lines changed

static/app/views/alerts/rules/metric/ruleForm.tsx

Lines changed: 26 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,21 +1030,10 @@ class RuleFormContainer extends DeprecatedAsyncComponent<Props, State> {
10301030
this.setState({historicalData}, () => this.fetchAnomalies());
10311031
}
10321032

1033-
TimeWindowsAreConsistent() {
1034-
const {currentData, historicalData, timeWindow} = this.state;
1035-
const currentDataPoint1 = currentData[1];
1036-
const currentDataPoint0 = currentData[0];
1037-
if (!currentDataPoint0 || !currentDataPoint1) {
1038-
return false;
1039-
}
1040-
const historicalDataPoint1 = historicalData[1];
1041-
const historicalDataPoint0 = historicalData[0];
1042-
if (!historicalDataPoint0 || !historicalDataPoint1) {
1043-
return false;
1044-
}
1045-
1046-
const currentTimeWindow = (currentDataPoint1[0] - currentDataPoint0[0]) / 60;
1047-
const historicalTimeWindow = (historicalDataPoint1[0] - historicalDataPoint0[0]) / 60;
1033+
timeWindowsAreConsistent() {
1034+
const {currentData = [], historicalData = [], timeWindow} = this.state;
1035+
const currentTimeWindow = getTimeWindowFromDataset(currentData, timeWindow);
1036+
const historicalTimeWindow = getTimeWindowFromDataset(historicalData, timeWindow);
10481037
return currentTimeWindow === historicalTimeWindow && currentTimeWindow === timeWindow;
10491038
}
10501039

@@ -1054,7 +1043,8 @@ class RuleFormContainer extends DeprecatedAsyncComponent<Props, State> {
10541043
comparisonType !== AlertRuleComparisonType.DYNAMIC ||
10551044
!(Array.isArray(currentData) && Array.isArray(historicalData)) ||
10561045
currentData.length === 0 ||
1057-
historicalData.length === 0
1046+
historicalData.length === 0 ||
1047+
!this.timeWindowsAreConsistent()
10581048
) {
10591049
return;
10601050
}
@@ -1096,10 +1086,7 @@ class RuleFormContainer extends DeprecatedAsyncComponent<Props, State> {
10961086
`/organizations/${organization.slug}/events/anomalies/`,
10971087
{method: 'POST', data: params}
10981088
);
1099-
// don't set the anomalies if historical and current data have incorrect time windows
1100-
if (!this.TimeWindowsAreConsistent()) {
1101-
this.setState({anomalies});
1102-
}
1089+
this.setState({anomalies});
11031090
} catch (e) {
11041091
let chartErrorMessage: string | undefined;
11051092
if (e.responseJSON) {
@@ -1468,6 +1455,25 @@ function formatStatsToHistoricalDataset(
14681455
: [];
14691456
}
14701457

1458+
function getTimeWindowFromDataset(
1459+
data: ReturnType<typeof formatStatsToHistoricalDataset>,
1460+
defaultWindow: TimeWindow
1461+
): number {
1462+
for (let i = 0; i < data.length; i++) {
1463+
const [timestampA] = data[i] ?? [];
1464+
const [timestampB] = data[i + 1] ?? [];
1465+
if (!timestampA || !timestampB) {
1466+
break;
1467+
}
1468+
// ignore duplicate timestamps
1469+
if (timestampA === timestampB) {
1470+
continue;
1471+
}
1472+
return Math.abs(timestampB - timestampA) / 60;
1473+
}
1474+
return defaultWindow;
1475+
}
1476+
14711477
const Main = styled(Layout.Main)`
14721478
max-width: 1000px;
14731479
`;

0 commit comments

Comments
 (0)