Skip to content

Commit

Permalink
[DataViews] Fix excessive resolve_index requests in create data vie…
Browse files Browse the repository at this point in the history
…w flyout (elastic#109500)
  • Loading branch information
Dosant authored and kibanamachine committed Aug 23, 2021
1 parent bf9413d commit 23ff5a4
Showing 1 changed file with 112 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import React, { useState, useEffect, useCallback, useRef } from 'react';
import { EuiTitle, EuiFlexGroup, EuiFlexItem, EuiSpacer, EuiLoadingSpinner } from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import memoizeOne from 'memoize-one';

import {
IndexPatternSpec,
Expand Down Expand Up @@ -105,12 +106,22 @@ const IndexPatternEditorFlyoutContentComponent = ({

const { getFields } = form;

const [{ title, allowHidden, type }] = useFormData<FormInternal>({ form });
// `useFormData` initially returns `undefined`,
// we override `undefined` with real default values from `schema`
// to get a stable reference to avoid hooks re-run and reduce number of excessive requests
const [
{
title = schema.title.defaultValue,
allowHidden = schema.allowHidden.defaultValue,
type = schema.type.defaultValue,
},
] = useFormData<FormInternal>({ form });
const [isLoadingSources, setIsLoadingSources] = useState<boolean>(true);

const [timestampFieldOptions, setTimestampFieldOptions] = useState<TimestampOption[]>([]);
const [isLoadingTimestampFields, setIsLoadingTimestampFields] = useState<boolean>(false);
const [isLoadingMatchedIndices, setIsLoadingMatchedIndices] = useState<boolean>(false);
const currentLoadingMatchedIndicesRef = useRef(0);
const [allSources, setAllSources] = useState<MatchedItem[]>([]);
const [isLoadingIndexPatterns, setIsLoadingIndexPatterns] = useState<boolean>(true);
const [existingIndexPatterns, setExistingIndexPatterns] = useState<string[]>([]);
Expand Down Expand Up @@ -165,7 +176,9 @@ const IndexPatternEditorFlyoutContentComponent = ({
try {
const response = await http.get('/api/rollup/indices');
if (isMounted.current) {
setRollupIndicesCapabilities(response || {});
if (response) {
setRollupIndicesCapabilities(response);
}
}
} catch (e) {
// Silently swallow failure responses such as expired trials
Expand Down Expand Up @@ -225,52 +238,31 @@ const IndexPatternEditorFlyoutContentComponent = ({
let newRollupIndexName: string | undefined;

const fetchIndices = async (query: string = '') => {
setIsLoadingMatchedIndices(true);
const indexRequests = [];

if (query?.endsWith('*')) {
const exactMatchedQuery = getIndices({
http,
isRollupIndex,
pattern: query,
showAllIndices: allowHidden,
searchClient,
});
indexRequests.push(exactMatchedQuery);
// provide default value when not making a request for the partialMatchQuery
indexRequests.push(Promise.resolve([]));
} else {
const exactMatchQuery = getIndices({
http,
isRollupIndex,
pattern: query,
showAllIndices: allowHidden,
searchClient,
});
const partialMatchQuery = getIndices({
http,
isRollupIndex,
pattern: `${query}*`,
showAllIndices: allowHidden,
searchClient,
});

indexRequests.push(exactMatchQuery);
indexRequests.push(partialMatchQuery);
}

const [exactMatched, partialMatched] = (await ensureMinimumTime(
indexRequests
)) as MatchedItem[][];
const currentLoadingMatchedIndicesIdx = ++currentLoadingMatchedIndicesRef.current;

const matchedIndicesResult = getMatchedIndices(
allSources,
partialMatched,
exactMatched,
allowHidden
);
setIsLoadingMatchedIndices(true);

if (isMounted.current) {
const { matchedIndicesResult, exactMatched } = !isLoadingSources
? await loadMatchedIndices(query, allowHidden, allSources, {
isRollupIndex,
http,
searchClient,
})
: {
matchedIndicesResult: {
exactMatchedIndices: [],
allIndices: [],
partialMatchedIndices: [],
visibleIndices: [],
},
exactMatched: [],
};

if (
currentLoadingMatchedIndicesIdx === currentLoadingMatchedIndicesRef.current &&
isMounted.current
) {
// we are still interested in this result
if (type === INDEX_PATTERN_TYPE.ROLLUP) {
const rollupIndices = exactMatched.filter((index) => isRollupIndex(index.name));
newRollupIndexName = rollupIndices.length === 1 ? rollupIndices[0].name : undefined;
Expand All @@ -288,7 +280,7 @@ const IndexPatternEditorFlyoutContentComponent = ({

return fetchIndices(newTitle);
},
[http, allowHidden, allSources, type, rollupIndicesCapabilities, searchClient]
[http, allowHidden, allSources, type, rollupIndicesCapabilities, searchClient, isLoadingSources]
);

useEffect(() => {
Expand Down Expand Up @@ -396,3 +388,76 @@ const IndexPatternEditorFlyoutContentComponent = ({
};

export const IndexPatternEditorFlyoutContent = React.memo(IndexPatternEditorFlyoutContentComponent);

// loadMatchedIndices is called both as an side effect inside of a parent component and the inside forms validation functions
// that are challenging to synchronize without a larger refactor
// Use memoizeOne as a caching layer to avoid excessive network requests on each key type
// TODO: refactor to remove `memoize` when https://github.com/elastic/kibana/pull/109238 is done
const loadMatchedIndices = memoizeOne(
async (
query: string,
allowHidden: boolean,
allSources: MatchedItem[],
{
isRollupIndex,
http,
searchClient,
}: {
isRollupIndex: (index: string) => boolean;
http: IndexPatternEditorContext['http'];
searchClient: IndexPatternEditorContext['searchClient'];
}
): Promise<{
matchedIndicesResult: MatchedIndicesSet;
exactMatched: MatchedItem[];
partialMatched: MatchedItem[];
}> => {
const indexRequests = [];

if (query?.endsWith('*')) {
const exactMatchedQuery = getIndices({
http,
isRollupIndex,
pattern: query,
showAllIndices: allowHidden,
searchClient,
});
indexRequests.push(exactMatchedQuery);
// provide default value when not making a request for the partialMatchQuery
indexRequests.push(Promise.resolve([]));
} else {
const exactMatchQuery = getIndices({
http,
isRollupIndex,
pattern: query,
showAllIndices: allowHidden,
searchClient,
});
const partialMatchQuery = getIndices({
http,
isRollupIndex,
pattern: `${query}*`,
showAllIndices: allowHidden,
searchClient,
});

indexRequests.push(exactMatchQuery);
indexRequests.push(partialMatchQuery);
}

const [exactMatched, partialMatched] = (await ensureMinimumTime(
indexRequests
)) as MatchedItem[][];

const matchedIndicesResult = getMatchedIndices(
allSources,
partialMatched,
exactMatched,
allowHidden
);

return { matchedIndicesResult, exactMatched, partialMatched };
},
// compare only query and allowHidden
(newArgs, oldArgs) => newArgs[0] === oldArgs[0] && newArgs[1] === oldArgs[1]
);

0 comments on commit 23ff5a4

Please sign in to comment.