Skip to content

Commit

Permalink
[Discover] Enhance Discover loading time by parallel execution of req…
Browse files Browse the repository at this point in the history
…uests (elastic#195670)

In the loading process of Discover, there were 4 sequential requests. This commit refactors the code to execute them in a parallel way. Before theduration of requests were adding up, now it takes as long as the longest
request. This can speed up the loading Discover significantly, depending on how long those requests take.

Co-authored-by: Julia Rechkunova <[email protected]>
  • Loading branch information
kertal and jughosta authored Oct 11, 2024
1 parent e822571 commit dc1aced
Showing 1 changed file with 58 additions and 53 deletions.
111 changes: 58 additions & 53 deletions src/plugins/discover/public/application/main/discover_main_route.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,11 @@ export function DiscoverMainRoute({
});
const [error, setError] = useState<Error>();
const [loading, setLoading] = useState(true);
const [hasESData, setHasESData] = useState(false);
const [hasUserDataView, setHasUserDataView] = useState(false);
const [showNoDataPage, setShowNoDataPage] = useState<boolean>(false);
const [noDataState, setNoDataState] = useState({
hasESData: false,
hasUserDataView: false,
showNoDataPage: false,
});
const hasCustomBranding = useObservable(core.customBranding.hasCustomBranding$, false);

/**
Expand All @@ -109,46 +111,49 @@ export function DiscoverMainRoute({
page: 'app',
id: savedSearchId || 'new',
});
/**
* Helper function to determine when to skip the no data page
*/
const skipNoDataPage = useCallback(
async (nextDataView?: DataView) => {
try {
const { dataSource } = stateContainer.appState.getState();
const isEsqlQuery = isDataSourceType(dataSource, DataSourceType.Esql);

const checkData = useCallback(async () => {
try {
if (savedSearchId) {
return true; // bypass NoData screen
}

if (isDataSourceType(stateContainer.appState.getState().dataSource, DataSourceType.Esql)) {
return true;
}

const hasUserDataViewValue = await data.dataViews.hasData
.hasUserDataView()
.catch(() => false);
const hasESDataValue = await data.dataViews.hasData.hasESData().catch(() => false);
setHasUserDataView(hasUserDataViewValue);
setHasESData(hasESDataValue);
// ES|QL should work without data views
// Given we have a saved search id, we can skip the data/data view check, too
// A given nextDataView is provided by the user, and therefore we can skip the data/data view check

if (!hasUserDataViewValue) {
setShowNoDataPage(true);
return false;
}
if (savedSearchId || isEsqlQuery || nextDataView) {
if (!isEsqlQuery) {
await stateContainer.actions.loadDataViewList();
}
return true;
}

let defaultDataViewExists: boolean = false;
try {
defaultDataViewExists = await data.dataViews.defaultDataViewExists();
const [hasUserDataViewValue, hasESDataValue, defaultDataViewExists] = await Promise.all([
data.dataViews.hasData.hasUserDataView().catch(() => false),
data.dataViews.hasData.hasESData().catch(() => false),
data.dataViews.defaultDataViewExists().catch(() => false),
stateContainer.actions.loadDataViewList(),
]);

if (!hasUserDataViewValue || !defaultDataViewExists) {
setNoDataState({
showNoDataPage: true,
hasESData: hasESDataValue,
hasUserDataView: hasUserDataViewValue,
});
return false;
}
return true;
} catch (e) {
//
}

if (!defaultDataViewExists) {
setShowNoDataPage(true);
setError(e);
return false;
}
return true;
} catch (e) {
setError(e);
return false;
}
}, [data.dataViews, savedSearchId, stateContainer.appState]);
},
[data.dataViews, savedSearchId, stateContainer]
);

const loadSavedSearch = useCallback(
async ({
Expand All @@ -157,13 +162,12 @@ export function DiscoverMainRoute({
}: { nextDataView?: DataView; initialAppState?: LoadParams['initialAppState'] } = {}) => {
const loadSavedSearchStartTime = window.performance.now();
setLoading(true);
if (!nextDataView && !(await checkData())) {
const skipNoData = await skipNoDataPage(nextDataView);
if (!skipNoData) {
setLoading(false);
return;
}
try {
await stateContainer.actions.loadDataViewList();

const currentSavedSearch = await stateContainer.actions.loadSavedSearch({
savedSearchId,
dataView: nextDataView,
Expand Down Expand Up @@ -214,8 +218,8 @@ export function DiscoverMainRoute({
}
},
[
checkData,
stateContainer.actions,
skipNoDataPage,
stateContainer,
savedSearchId,
historyLocationState?.dataViewSpec,
customizationContext.displayMode,
Expand All @@ -231,11 +235,12 @@ export function DiscoverMainRoute({

useEffect(() => {
if (!isCustomizationServiceInitialized) return;

setLoading(true);
setHasESData(false);
setHasUserDataView(false);
setShowNoDataPage(false);
setNoDataState({
hasESData: false,
hasUserDataView: false,
showNoDataPage: false,
});
setError(undefined);
if (savedSearchId) {
loadSavedSearch();
Expand All @@ -259,7 +264,7 @@ export function DiscoverMainRoute({
async (nextDataView: unknown) => {
if (nextDataView) {
setLoading(true);
setShowNoDataPage(false);
setNoDataState((state) => ({ ...state, showNoDataPage: false }));
setError(undefined);
await loadSavedSearch({ nextDataView: nextDataView as DataView });
}
Expand All @@ -281,15 +286,15 @@ export function DiscoverMainRoute({

// We've already called this, so we can optimize the analytics services to
// use the already-retrieved data to avoid a double-call.
hasESData: () => Promise.resolve(hasESData),
hasUserDataView: () => Promise.resolve(hasUserDataView),
hasESData: () => Promise.resolve(noDataState.hasESData),
hasUserDataView: () => Promise.resolve(noDataState.hasUserDataView),
},
},
share,
dataViewEditor,
noDataPage: services.noDataPage,
}),
[core, data.dataViews, dataViewEditor, hasESData, hasUserDataView, services.noDataPage, share]
[core, data.dataViews, dataViewEditor, noDataState, services.noDataPage, share]
);

const loadingIndicator = useMemo(
Expand All @@ -298,7 +303,7 @@ export function DiscoverMainRoute({
);

const mainContent = useMemo(() => {
if (showNoDataPage) {
if (noDataState.showNoDataPage) {
const importPromise = import('@kbn/shared-ux-page-analytics-no-data');
const AnalyticsNoDataPageKibanaProvider = withSuspense(
React.lazy(() =>
Expand Down Expand Up @@ -336,7 +341,7 @@ export function DiscoverMainRoute({
noDataDependencies,
onDataViewCreated,
onESQLNavigationComplete,
showNoDataPage,
noDataState.showNoDataPage,
stateContainer,
]);

Expand All @@ -357,7 +362,7 @@ export function DiscoverMainRoute({
<>
<DiscoverTopNavInline
stateContainer={stateContainer}
hideNavMenuItems={loading || showNoDataPage}
hideNavMenuItems={loading || noDataState.showNoDataPage}
/>
{mainContent}
</>
Expand Down

0 comments on commit dc1aced

Please sign in to comment.