Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Discover] Enhance Discover loading time by parallel execution of requests #195670

Merged
merged 10 commits into from
Oct 11, 2024
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,
});
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A bit of optimization, so let setState is being called, this will lead to less re-renderings, however those are just small wins

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