Skip to content

Commit b5e4dbe

Browse files
authored
[Search] Reduce polling on Index Details page (#251446)
## Summary This change improves how the Index Details page fetches data by reducing unnecessary polling to backend APIs. Previously, the page repeatedly polled two endpoints to keep index metadata up to date, even when the data was not actively changing or required for user interaction. As part of this update, we reviewed the polling strategy used for: `GET /api/index_management/mapping/{index_name}` `GET /internal/index_management/indices/{index_name}` `POST /internal/search_indices/${indexName}/documents/search` and adjusted the behavior to better align with how the data is used on the page. ### What changed - Polling behavior has been reduced or scoped so that requests are only made on initial load, on window focus, and when requested. - A `Refresh data` button was added to allow users to request up to date data. - `useUserPrivilegesQuery` no longer refetches on window focus, which is the default behavior. - The `queryKey` in `useUserPrivilegesQuery` has been updated to add the `indexName`. ### Screenshots **Index with documents** <img width="700" height="828" alt="Screenshot 2026-02-03 at 13 24 12" src="https://github.com/user-attachments/assets/c8a99e1e-4f67-4bfd-9b7a-4c7490f8cb7d" /> **Index without documents** <img width="700" height="829" alt="Screenshot 2026-02-03 at 13 24 26" src="https://github.com/user-attachments/assets/8d752199-2ecf-47e6-b47e-f1966590f0c2" /> ### Checklist Check the PR satisfies following conditions. Reviewers should verify this PR satisfies this list as well. - [x] Any text added follows [EUI's writing guidelines](https://elastic.github.io/eui/#/guidelines/writing), uses sentence case text and includes [i18n support](https://github.com/elastic/kibana/blob/main/src/platform/packages/shared/kbn-i18n/README.md) - [ ] ~[Documentation](https://www.elastic.co/guide/en/kibana/master/development-documentation.html) was added for features that require explanation or tutorials~ - [ ] ~[Unit or functional tests](https://www.elastic.co/guide/en/kibana/master/development-tests.html) were updated or added to match the most common scenarios~ - [ ] ~If a plugin configuration key changed, check if it needs to be allowlisted in the cloud and added to the [docker list](https://github.com/elastic/kibana/blob/main/src/dev/build/tasks/os_packages/docker_generator/resources/base/bin/kibana-docker)~ - [ ] ~This was checked for breaking HTTP API changes, and any breaking changes have been approved by the breaking-change committee. The `release_note:breaking` label should be applied in these situations.~ - [ ] ~[Flaky Test Runner](https://ci-stats.kibana.dev/trigger_flaky_test_runner/1) was used on any tests changed~ - [x] The PR description includes the appropriate Release Notes section, and the correct `release_note:*` label is applied per the [guidelines](https://www.elastic.co/guide/en/kibana/master/contributing.html#kibana-release-notes-process) - [x] Review the [backport guidelines](https://docs.google.com/document/d/1VyN5k91e5OVumlc0Gb9RPa3h1ewuPE705nRtioPiTvY/edit?usp=sharing) and apply applicable `backport:*` labels. ## Release note Reduced background polling on the Index Details page to avoid unnecessary API requests.
1 parent 2fad388 commit b5e4dbe

9 files changed

Lines changed: 54 additions & 29 deletions

File tree

‎x-pack/solutions/search/packages/kbn-search-api-keys-components/src/components/api_key_form.tsx‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ const API_KEY_MASK = '•'.repeat(60);
2525

2626
interface ApiKeyFormProps {
2727
hasTitle?: boolean;
28+
minWidth?: number;
2829
}
2930

30-
export const ApiKeyForm: React.FC<ApiKeyFormProps> = ({ hasTitle = true }) => {
31+
export const ApiKeyForm: React.FC<ApiKeyFormProps> = ({ hasTitle = true, minWidth }) => {
3132
const [showFlyout, setShowFlyout] = useState(false);
3233
const { apiKey, status, updateApiKey, toggleApiKeyVisibility } = useSearchApiKey();
3334

@@ -43,6 +44,7 @@ export const ApiKeyForm: React.FC<ApiKeyFormProps> = ({ hasTitle = true }) => {
4344
copyValue={apiKey}
4445
dataTestSubj="apiKeyFormAPIKey"
4546
copyValueDataTestSubj="APIKeyButtonCopy"
47+
minWidth={minWidth}
4648
actions={[
4749
<EuiButtonIcon
4850
iconType={status === Status.showPreviewKey ? 'eyeClosed' : 'eye'}

‎x-pack/solutions/search/plugins/search_indices/public/components/connection_details/connection_details.tsx‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ export const ConnectionDetails: React.FC = () => {
2424
copyValue={elasticsearchUrl}
2525
dataTestSubj="connectionDetailsEndpoint"
2626
copyValueDataTestSubj="connectionDetailsEndpointCopy"
27+
minWidth={400}
2728
/>
2829
);
2930
};

‎x-pack/solutions/search/plugins/search_indices/public/components/indices/details_page.tsx‎

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -58,19 +58,23 @@ export const SearchIndexDetailsPage = () => {
5858
} = useKibana().services;
5959
const {
6060
data: index,
61-
refetch,
61+
refetch: refetchIndex,
6262
isError: isIndexError,
6363
isInitialLoading,
6464
error: indexLoadingError,
6565
} = useIndex(indexName);
6666
const {
6767
data: mappings,
68+
refetch: refetchMappings,
6869
isError: isMappingsError,
6970
isInitialLoading: isMappingsInitialLoading,
7071
error: mappingsError,
7172
} = useIndexMapping(indexName);
72-
const { data: indexDocuments, isInitialLoading: indexDocumentsIsInitialLoading } =
73-
useIndexDocumentSearch(indexName);
73+
const {
74+
data: indexDocuments,
75+
refetch: refetchIndexDocuments,
76+
isInitialLoading: indexDocumentsIsInitialLoading,
77+
} = useIndexDocumentSearch(indexName);
7478
const { data: userPrivileges } = useUserPrivilegesQuery(indexName);
7579

7680
const navigateToPlayground = useCallback(async () => {
@@ -112,6 +116,7 @@ export const SearchIndexDetailsPage = () => {
112116
isInitialLoading={indexDocumentsIsInitialLoading}
113117
userPrivileges={userPrivileges}
114118
navigateToPlayground={navigateToPlayground}
119+
mappingData={mappings}
115120
/>
116121
),
117122
'data-test-subj': `${SearchIndexDetailsTabs.DATA}Tab`,
@@ -141,6 +146,7 @@ export const SearchIndexDetailsPage = () => {
141146
indexDocuments,
142147
indexDocumentsIsInitialLoading,
143148
userPrivileges,
149+
mappings,
144150
navigateToPlayground,
145151
]);
146152
const [selectedTab, setSelectedTab] = useState(detailsPageTabs[0]);
@@ -175,9 +181,12 @@ export const SearchIndexDetailsPage = () => {
175181
application.navigateToApp('management', { deepLinkId: 'index_management' });
176182
}, [application]);
177183

178-
const refetchIndex = useCallback(() => {
179-
refetch();
180-
}, [refetch]);
184+
const refetchAllData = useCallback(() => {
185+
refetchIndex();
186+
refetchMappings();
187+
refetchIndexDocuments();
188+
}, [refetchIndex, refetchMappings, refetchIndexDocuments]);
189+
181190
const indexError = useMemo(
182191
() =>
183192
isIndexError
@@ -197,7 +206,10 @@ export const SearchIndexDetailsPage = () => {
197206
}, [isShowingDeleteModal]);
198207
const { euiTheme } = useEuiTheme();
199208

200-
if (isInitialLoading || isMappingsInitialLoading || indexDocumentsIsInitialLoading) {
209+
const pageDataLoading =
210+
isInitialLoading || isMappingsInitialLoading || indexDocumentsIsInitialLoading;
211+
212+
if (pageDataLoading) {
201213
return (
202214
<SectionLoading>
203215
{i18n.translate('xpack.searchIndices.loadingDescription', {
@@ -221,7 +233,7 @@ export const SearchIndexDetailsPage = () => {
221233
<IndexloadingError
222234
error={indexError}
223235
navigateToIndexListPage={navigateToIndexListPage}
224-
reloadFunction={refetchIndex}
236+
reloadFunction={refetchAllData}
225237
/>
226238
) : (
227239
<>
@@ -295,12 +307,27 @@ export const SearchIndexDetailsPage = () => {
295307
<EuiFlexGroup direction="column">
296308
<EuiFlexGroup direction="column">
297309
<EuiFlexItem>
298-
<EuiFlexGroup css={{ overflow: 'auto' }} wrap>
299-
<EuiFlexItem grow={false} css={{ minWidth: 400 }}>
300-
<ConnectionDetails />
301-
</EuiFlexItem>
302-
<EuiFlexItem grow={false} css={{ minWidth: 400 }}>
303-
<ApiKeyForm />
310+
<EuiFlexGroup justifyContent="spaceBetween" alignItems="flexEnd">
311+
<EuiFlexGroup css={{ overflow: 'auto' }} wrap>
312+
<EuiFlexItem grow={false}>
313+
<ConnectionDetails />
314+
</EuiFlexItem>
315+
<EuiFlexItem grow={false}>
316+
<ApiKeyForm minWidth={400} />
317+
</EuiFlexItem>
318+
</EuiFlexGroup>
319+
<EuiFlexItem css={{ maxWidth: 'fit-content' }}>
320+
<EuiButton
321+
data-test-subj="SearchIndexDetailsPageRefreshDataButton"
322+
isLoading={pageDataLoading}
323+
onClick={refetchAllData}
324+
iconType="refresh"
325+
color="success"
326+
>
327+
{i18n.translate('xpack.searchIndices.indexAction.refreshDataButtonLabel', {
328+
defaultMessage: 'Refresh data',
329+
})}
330+
</EuiButton>
304331
</EuiFlexItem>
305332
</EuiFlexGroup>
306333
</EuiFlexItem>

‎x-pack/solutions/search/plugins/search_indices/public/components/indices/details_page_data.tsx‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import { CLOUD_CONNECT_NAV_ID } from '@kbn/deeplinks-management/constants';
2020

2121
import type { UserStartPrivilegesResponse } from '../../../common';
2222
import { useKibana } from '../../hooks/use_kibana';
23-
import { useIndexMapping } from '../../hooks/api/use_index_mappings';
2423
import type { IndexDocuments as IndexDocumentsType } from '../../hooks/api/use_document_search';
2524
import { IndexDocuments } from '../index_documents/index_documents';
2625
import { IndexSearchExample } from './details_search_example';
@@ -29,13 +28,15 @@ import { UpdateElserMappingsModal } from '../update_elser_mappings/update_elser_
2928
import { flattenMappings, hasElserOnMlNodeSemanticTextField } from '../update_elser_mappings/utils';
3029
import type { NormalizedFields } from '../update_elser_mappings/types';
3130
import { useLicense } from '../../hooks/use_license';
31+
import type { Mappings } from '../../types';
3232

3333
interface IndexDetailsDataProps {
3434
indexName: string;
3535
indexDocuments?: IndexDocumentsType;
3636
isInitialLoading: boolean;
3737
navigateToPlayground: () => void;
3838
userPrivileges?: UserStartPrivilegesResponse;
39+
mappingData: Mappings | undefined;
3940
}
4041

4142
export const IndexDetailsData = ({
@@ -44,9 +45,9 @@ export const IndexDetailsData = ({
4445
isInitialLoading,
4546
navigateToPlayground,
4647
userPrivileges,
48+
mappingData,
4749
}: IndexDetailsDataProps) => {
4850
const { application, cloud } = useKibana().services;
49-
const { data: mappingData } = useIndexMapping(indexName);
5051
const { isAtLeastEnterprise } = useLicense();
5152
const [isUpdatingElserMappings, setIsUpdatingElserMappings] = useState<boolean>(false);
5253

‎x-pack/solutions/search/plugins/search_indices/public/hooks/api/use_delete_document.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import type { SearchHit } from '@elastic/elasticsearch/lib/api/types';
1111
import { MutationKeys, QueryKeys } from '../../constants';
1212
import { useKibana } from '../use_kibana';
1313
import type { IndexDocuments } from './use_document_search';
14-
import { INDEX_SEARCH_POLLING } from './use_document_search';
14+
15+
const INDEX_SEARCH_POLLING = 30000;
1516

1617
interface DeleteDocumentParams {
1718
id: string;

‎x-pack/solutions/search/plugins/search_indices/public/hooks/api/use_document_search.ts‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,12 @@ const DEFAULT_PAGINATION = {
2323
size: DEFAULT_DOCUMENT_PAGE_SIZE,
2424
total: 0,
2525
};
26-
export const INDEX_SEARCH_POLLING = 30000;
2726
export const useIndexDocumentSearch = (indexName: string) => {
2827
const {
2928
services: { http },
3029
} = useKibana();
31-
const { data, isInitialLoading } = useQuery({
30+
const { data, isInitialLoading, refetch } = useQuery({
3231
queryKey: [QueryKeys.SearchDocuments, indexName],
33-
refetchInterval: INDEX_SEARCH_POLLING,
34-
refetchIntervalInBackground: true,
3532
refetchOnWindowFocus: 'always',
3633
queryFn: async ({ signal }) =>
3734
http.post<IndexDocuments>(`/internal/search_indices/${indexName}/documents/search`, {
@@ -48,6 +45,7 @@ export const useIndexDocumentSearch = (indexName: string) => {
4845
});
4946
return {
5047
data,
48+
refetch,
5149
isInitialLoading,
5250
meta: pageToPagination(data?.results?._meta?.page ?? DEFAULT_PAGINATION),
5351
};

‎x-pack/solutions/search/plugins/search_indices/public/hooks/api/use_index.ts‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@ import { useQuery } from '@kbn/react-query';
1010
import { QueryKeys } from '../../constants';
1111
import { useKibana } from '../use_kibana';
1212

13-
const POLLING_INTERVAL = 15 * 1000;
1413
export const useIndex = (indexName: string) => {
1514
const { http } = useKibana().services;
1615
const queryKey = [QueryKeys.FetchIndex, indexName];
1716
return useQuery<Index, { body: { statusCode: number; message: string; error: string } }>({
1817
queryKey,
19-
refetchInterval: POLLING_INTERVAL,
20-
refetchIntervalInBackground: true,
2118
refetchOnWindowFocus: 'always',
2219
retry: (failureCount, error) => {
2320
return !(error?.body?.statusCode === 404 || failureCount === 3);

‎x-pack/solutions/search/plugins/search_indices/public/hooks/api/use_index_mappings.ts‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,11 @@ import { useKibana } from '../use_kibana';
1010
import type { Mappings } from '../../types';
1111
import { QueryKeys } from '../../constants';
1212

13-
const POLLING_INTERVAL = 15 * 1000;
1413
export const useIndexMapping = (indexName: string) => {
1514
const { http } = useKibana().services;
1615
const queryKey = [QueryKeys.FetchMapping, indexName];
1716
return useQuery<Mappings, { body: { message: string; error: string } }>({
1817
queryKey,
19-
refetchInterval: POLLING_INTERVAL,
20-
refetchIntervalInBackground: true,
2118
refetchOnWindowFocus: 'always',
2219
queryFn: () =>
2320
http.fetch<Mappings>(`/api/index_management/mapping/${encodeURIComponent(indexName)}`),

‎x-pack/solutions/search/plugins/search_indices/public/hooks/api/use_user_permissions.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ import { useKibana } from '../use_kibana';
1515
export const useUserPrivilegesQuery = (indexName: string) => {
1616
const { http } = useKibana().services;
1717
return useQuery({
18-
queryKey: [QueryKeys.FetchUserStartPrivileges],
18+
queryKey: [QueryKeys.FetchUserStartPrivileges, indexName],
19+
refetchOnWindowFocus: false,
1920
queryFn: () =>
2021
http.get<UserStartPrivilegesResponse>(
2122
`/internal/search_indices/start_privileges/${indexName}`

0 commit comments

Comments
 (0)