Search Applications: use msearch to get indices counts (#163699)

We use the index stats API to get the index counts for each index that
belongs to a Search Application.
The index stats API is internal, we should be using the counts API.
`GET <index>/_count` is equivalent to `GET
<index>/_search?size=0&track_total_hits=true`.
In order to avoid doing a request for each index, we do a single msearch
request to get all index counts.

tested locally, the counts are retrieved correctly:

<img width="1507" alt="Screenshot 2023-08-11 at 13 50 08"
src="3a9d2628-5b85-45d4-91be-561b5a326495">
This commit is contained in:
Ioana Tagirta 2023-08-11 16:16:30 +02:00 committed by GitHub
parent ecaa8a7370
commit 34969fd511
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 36 additions and 5 deletions

View file

@ -15,6 +15,7 @@ describe('fetchIndicesStats lib function', () => {
get: jest.fn(),
stats: jest.fn(),
},
msearch: jest.fn(),
},
asInternalUser: {},
};
@ -53,6 +54,18 @@ describe('fetchIndicesStats lib function', () => {
},
];
const msearchResponse = {
responses: [
{
hits: {
total: {
value: 200,
},
},
},
],
};
beforeEach(() => {
jest.clearAllMocks();
});
@ -60,6 +73,7 @@ describe('fetchIndicesStats lib function', () => {
it('should return hydrated indices for all available and open indices', async () => {
mockClient.asCurrentUser.indices.get.mockResolvedValueOnce(getAllAvailableIndexResponse);
mockClient.asCurrentUser.indices.stats.mockResolvedValueOnce(indexStats);
mockClient.asCurrentUser.msearch.mockImplementationOnce(() => msearchResponse);
await expect(
fetchIndicesStats(mockClient as unknown as IScopedClusterClient, indices)
).resolves.toEqual(fetchIndicesStatsResponse);
@ -77,6 +91,7 @@ describe('fetchIndicesStats lib function', () => {
);
mockClient.asCurrentUser.indices.stats.mockImplementationOnce(() => indexStats);
mockClient.asCurrentUser.msearch.mockImplementationOnce(() => msearchResponse);
await expect(
fetchIndicesStats(mockClient as unknown as IScopedClusterClient, [
@ -95,6 +110,7 @@ describe('fetchIndicesStats lib function', () => {
it('should return count : null, health: unknown for deleted index ', async () => {
mockClient.asCurrentUser.indices.get.mockImplementationOnce(() => getAllAvailableIndexResponse);
mockClient.asCurrentUser.indices.stats.mockImplementationOnce(() => indexStats);
mockClient.asCurrentUser.msearch.mockImplementationOnce(() => msearchResponse);
await expect(
fetchIndicesStats(mockClient as unknown as IScopedClusterClient, [

View file

@ -5,6 +5,7 @@
* 2.0.
*/
import { MsearchRequestItem, SearchTotalHits } from '@elastic/elasticsearch/lib/api/types';
import { IScopedClusterClient } from '@kbn/core-elasticsearch-server/src/client/scoped_cluster_client';
import { EnterpriseSearchApplicationIndex } from '../../../common/types/search_applications';
@ -17,15 +18,29 @@ export const fetchIndicesStats = async (
): Promise<EnterpriseSearchApplicationIndex[]> => {
const indicesStats = await client.asCurrentUser.indices.stats({
index: await availableIndices(client, indices),
metric: ['docs'],
});
return indices.map((index) => {
const indexStats = indicesStats.indices?.[index];
const searches: MsearchRequestItem[] = [];
indices.forEach((index) => {
searches.push({ index });
searches.push({ size: 0, track_total_hits: true });
});
const msearchResponse = await client.asCurrentUser.msearch({ searches });
const docCounts = msearchResponse.responses.map((response) => {
if ('error' in response) {
return null;
}
const totalHits = response.hits.total as SearchTotalHits;
return totalHits.value;
});
return indices.map((indexName, number) => {
const indexStats = indicesStats.indices?.[indexName];
return {
count: indexStats?.primaries?.docs?.count ?? null,
count: docCounts[number] ?? null,
health: indexStats?.health ?? 'unknown',
name: index,
name: indexName,
};
});
};