mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[ML] Fix Anomaly Explorer continuously re-render on swim lane cell selection (#98196)
Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
56bce08611
commit
fd66e075ac
8 changed files with 50 additions and 33 deletions
|
@ -37,7 +37,6 @@ import { TimefilterContract } from '../../../../../../../src/plugins/data/public
|
|||
import { AnomalyExplorerChartsService } from '../../services/anomaly_explorer_charts_service';
|
||||
import { CombinedJob } from '../../../../common/types/anomaly_detection_jobs';
|
||||
import { InfluencersFilterQuery } from '../../../../common/types/es_client';
|
||||
import { ExplorerChartsData } from '../explorer_charts/explorer_charts_container_service';
|
||||
import { mlJobService } from '../../services/job_service';
|
||||
import { TimeBucketsInterval } from '../../util/time_buckets';
|
||||
|
||||
|
@ -156,7 +155,6 @@ const loadExplorerDataProvider = (
|
|||
const dateFormatTz = getDateFormatTz();
|
||||
|
||||
const interval = swimlaneBucketInterval.asSeconds();
|
||||
|
||||
// First get the data where we have all necessary args at hand using forkJoin:
|
||||
// annotationsData, anomalyChartRecords, influencers, overallState, tableData, topFieldValues
|
||||
return forkJoin({
|
||||
|
@ -225,7 +223,21 @@ const loadExplorerDataProvider = (
|
|||
// show the view-by loading indicator
|
||||
// and pass on the data we already fetched.
|
||||
tap(explorerService.setViewBySwimlaneLoading),
|
||||
tap(explorerService.setChartsDataLoading),
|
||||
tap(({ anomalyChartRecords, topFieldValues }) => {
|
||||
memoizedAnomalyDataChange(
|
||||
lastRefresh,
|
||||
explorerService,
|
||||
combinedJobRecords,
|
||||
swimlaneContainerWidth,
|
||||
selectedCells !== undefined && Array.isArray(anomalyChartRecords)
|
||||
? anomalyChartRecords
|
||||
: [],
|
||||
timerange.earliestMs,
|
||||
timerange.latestMs,
|
||||
timefilter,
|
||||
tableSeverity
|
||||
);
|
||||
}),
|
||||
mergeMap(
|
||||
({
|
||||
overallAnnotations,
|
||||
|
@ -237,18 +249,6 @@ const loadExplorerDataProvider = (
|
|||
tableData,
|
||||
}) =>
|
||||
forkJoin({
|
||||
anomalyChartsData: memoizedAnomalyDataChange(
|
||||
lastRefresh,
|
||||
combinedJobRecords,
|
||||
swimlaneContainerWidth,
|
||||
selectedCells !== undefined && Array.isArray(anomalyChartRecords)
|
||||
? anomalyChartRecords
|
||||
: [],
|
||||
timerange.earliestMs,
|
||||
timerange.latestMs,
|
||||
timefilter,
|
||||
tableSeverity
|
||||
),
|
||||
filteredTopInfluencers:
|
||||
(selectionInfluencers.length > 0 || influencersFilterQuery !== undefined) &&
|
||||
anomalyChartRecords !== undefined &&
|
||||
|
@ -281,9 +281,6 @@ const loadExplorerDataProvider = (
|
|||
influencersFilterQuery
|
||||
),
|
||||
}).pipe(
|
||||
tap(({ anomalyChartsData }) => {
|
||||
explorerService.setCharts(anomalyChartsData as ExplorerChartsData);
|
||||
}),
|
||||
map(({ viewBySwimlaneState, filteredTopInfluencers }) => {
|
||||
return {
|
||||
overallAnnotations,
|
||||
|
|
|
@ -66,7 +66,7 @@ export const AnomalyContextMenu: FC<AnomalyContextMenuProps> = ({
|
|||
|
||||
return (
|
||||
<>
|
||||
{menuItems.length > 0 && (
|
||||
{menuItems.length > 0 && chartsCount > 0 && (
|
||||
<EuiFlexItem grow={false} style={{ marginLeft: 'auto', alignSelf: 'baseline' }}>
|
||||
<EuiPopover
|
||||
button={
|
||||
|
|
|
@ -90,6 +90,8 @@ export const AnomalyTimeline: FC<AnomalyTimelineProps> = React.memo(
|
|||
overallAnnotations,
|
||||
} = explorerState;
|
||||
|
||||
const annotations = useMemo(() => overallAnnotations.annotationsData, [overallAnnotations]);
|
||||
|
||||
const menuItems = useMemo(() => {
|
||||
const items = [];
|
||||
if (canEditDashboards) {
|
||||
|
@ -241,7 +243,7 @@ export const AnomalyTimeline: FC<AnomalyTimelineProps> = React.memo(
|
|||
isLoading={loading}
|
||||
noDataWarning={<NoOverallData />}
|
||||
showTimeline={false}
|
||||
annotationsData={overallAnnotations.annotationsData}
|
||||
annotationsData={annotations}
|
||||
/>
|
||||
|
||||
<EuiSpacer size="m" />
|
||||
|
|
|
@ -143,7 +143,7 @@ export const SwimlaneAnnotationContainer: FC<SwimlaneAnnotationContainerProps> =
|
|||
.on('mouseout', () => tooltipService.hide());
|
||||
});
|
||||
}
|
||||
}, [chartWidth, domain, annotationsData]);
|
||||
}, [chartWidth, domain, annotationsData, tooltipService]);
|
||||
|
||||
return <div ref={canvasRef} />;
|
||||
};
|
||||
|
|
|
@ -369,13 +369,17 @@ export const SwimlaneContainer: FC<SwimlaneProps> = ({
|
|||
[swimlaneData?.fieldName]
|
||||
);
|
||||
|
||||
const xDomain = swimlaneData
|
||||
? {
|
||||
min: swimlaneData.earliest * 1000,
|
||||
max: swimlaneData.latest * 1000,
|
||||
minInterval: swimlaneData.interval * 1000,
|
||||
}
|
||||
: undefined;
|
||||
const xDomain = useMemo(
|
||||
() =>
|
||||
swimlaneData
|
||||
? {
|
||||
min: swimlaneData.earliest * 1000,
|
||||
max: swimlaneData.latest * 1000,
|
||||
minInterval: swimlaneData.interval * 1000,
|
||||
}
|
||||
: undefined,
|
||||
[swimlaneData]
|
||||
);
|
||||
|
||||
// A resize observer is required to compute the bucket span based on the chart width to fetch the data accordingly
|
||||
return (
|
||||
|
@ -392,6 +396,7 @@ export const SwimlaneContainer: FC<SwimlaneProps> = ({
|
|||
style={{
|
||||
width: '100%',
|
||||
overflowY: 'auto',
|
||||
overflowX: 'hidden',
|
||||
}}
|
||||
grow={false}
|
||||
>
|
||||
|
@ -403,11 +408,7 @@ export const SwimlaneContainer: FC<SwimlaneProps> = ({
|
|||
onElementClick={onElementClick}
|
||||
showLegend={showLegend}
|
||||
legendPosition={Position.Top}
|
||||
xDomain={{
|
||||
min: swimlaneData.earliest * 1000,
|
||||
max: swimlaneData.latest * 1000,
|
||||
minInterval: swimlaneData.interval * 1000,
|
||||
}}
|
||||
xDomain={xDomain}
|
||||
tooltip={tooltipOptions}
|
||||
debugState={window._echDebugStateFlag ?? false}
|
||||
/>
|
||||
|
|
|
@ -102,6 +102,7 @@ describe('AnomalyExplorerChartsService', () => {
|
|||
|
||||
test('should return anomaly data without explorer service', async () => {
|
||||
const anomalyData = (await anomalyExplorerService.getAnomalyData(
|
||||
undefined,
|
||||
(combinedJobRecords as unknown) as Record<string, CombinedJob>,
|
||||
1000,
|
||||
mockAnomalyChartRecords,
|
||||
|
@ -116,6 +117,7 @@ describe('AnomalyExplorerChartsService', () => {
|
|||
|
||||
test('call anomalyChangeListener with empty series config', async () => {
|
||||
const anomalyData = (await anomalyExplorerService.getAnomalyData(
|
||||
undefined,
|
||||
// @ts-ignore
|
||||
(combinedJobRecords as unknown) as Record<string, CombinedJob>,
|
||||
1000,
|
||||
|
@ -137,6 +139,7 @@ describe('AnomalyExplorerChartsService', () => {
|
|||
mockAnomalyChartRecordsClone[1].partition_field_value = 'AAL.';
|
||||
|
||||
const anomalyData = (await anomalyExplorerService.getAnomalyData(
|
||||
undefined,
|
||||
(combinedJobRecords as unknown) as Record<string, CombinedJob>,
|
||||
1000,
|
||||
mockAnomalyChartRecordsClone,
|
||||
|
|
|
@ -40,6 +40,7 @@ import { TimeRangeBounds } from '../util/time_buckets';
|
|||
import { isDefined } from '../../../common/types/guards';
|
||||
import { AppStateSelectedCells } from '../explorer/explorer_utils';
|
||||
import { InfluencersFilterQuery } from '../../../common/types/es_client';
|
||||
import { ExplorerService } from '../explorer/explorer_dashboard_service';
|
||||
const CHART_MAX_POINTS = 500;
|
||||
const ANOMALIES_MAX_RESULTS = 500;
|
||||
const MAX_SCHEDULED_EVENTS = 10; // Max number of scheduled events displayed per bucket.
|
||||
|
@ -427,6 +428,7 @@ export class AnomalyExplorerChartsService {
|
|||
}
|
||||
|
||||
public async getAnomalyData(
|
||||
explorerService: ExplorerService | undefined,
|
||||
combinedJobRecords: Record<string, CombinedJob>,
|
||||
chartsContainerWidth: number,
|
||||
anomalyRecords: ChartRecord[] | undefined,
|
||||
|
@ -534,6 +536,11 @@ export class AnomalyExplorerChartsService {
|
|||
data.errorMessages = errorMessages;
|
||||
}
|
||||
|
||||
// TODO: replace this temporary fix for flickering issue
|
||||
// https://github.com/elastic/kibana/issues/97266
|
||||
if (explorerService) {
|
||||
explorerService.setCharts({ ...data });
|
||||
}
|
||||
if (seriesConfigs.length === 0) {
|
||||
return data;
|
||||
}
|
||||
|
@ -895,6 +902,12 @@ export class AnomalyExplorerChartsService {
|
|||
// push map data in if it's available
|
||||
data.seriesToPlot.push(...mapData);
|
||||
}
|
||||
|
||||
// TODO: replace this temporary fix for flickering issue
|
||||
if (explorerService) {
|
||||
explorerService.setCharts({ ...data });
|
||||
}
|
||||
|
||||
return Promise.resolve(data);
|
||||
})
|
||||
.catch((error) => {
|
||||
|
|
|
@ -133,6 +133,7 @@ export function useAnomalyChartsInputResolver(
|
|||
return forkJoin({
|
||||
chartsData: from(
|
||||
anomalyExplorerService.getAnomalyData(
|
||||
undefined,
|
||||
combinedJobRecords,
|
||||
embeddableContainerWidth,
|
||||
anomalyChartRecords,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue