Skip to content

Commit

Permalink
[Dataset quality] remove usage of dataStreamStats API in serverless (#…
Browse files Browse the repository at this point in the history
…192839)

Closes #192166.

This PR aims to remove the usage of `DataStreamStats` API in serverless.
Aditionally we now make use of the new verbose mode for `DataStream` API
which allow us to still show the `lastActivity` for serverless projects.
The change was implemented for both flavours.

Visually it won't represent any changes at the moment, but here are the
two flavours still working as before

https://github.com/user-attachments/assets/18cc9b14-24f1-438a-ac07-565c3e728991
(cherry picked from commit 444f95e)
  • Loading branch information
yngrdyn committed Sep 19, 2024
1 parent 647ac36 commit d138ad1
Show file tree
Hide file tree
Showing 11 changed files with 121 additions and 157 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
* 2.0.
*/

import React, { useCallback } from 'react';
import React from 'react';
import useToggle from 'react-use/lib/useToggle';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
Expand Down Expand Up @@ -54,7 +54,7 @@ export const PrivilegesWarningIconWrapper = ({
}) => {
const [isPopoverOpen, togglePopover] = useToggle(false);

const handleButtonClick = useCallback(() => togglePopover(true), [togglePopover]);
const handleButtonClick = togglePopover;

if (hasPrivileges) {
return <>{children}</>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import {
import { DataPlaceholder } from './data_placeholder';

export function DatasetsActivity() {
const { datasetsActivity, isDatasetsActivityLoading, isUserAuthorizedForDataset } =
useSummaryPanelContext();
const { datasetsActivity, isDatasetsActivityLoading } = useSummaryPanelContext();
const text = `${datasetsActivity.active} ${tableSummaryOfText} ${datasetsActivity.total}`;

return (
Expand All @@ -26,7 +25,7 @@ export function DatasetsActivity() {
tooltip={summaryPanelDatasetsActivityTooltipText}
value={text}
isLoading={isDatasetsActivityLoading}
isUserAuthorizedForDataset={isUserAuthorizedForDataset}
isUserAuthorizedForDataset={true}
/>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -300,46 +300,37 @@ export const getDatasetQualityTableColumns = ({
),
width: '140px',
},
...(canUserMonitorDataset && canUserMonitorAnyDataStream
? [
{
name: (
<EuiTableHeader data-test-subj="datasetQualityLastActivityColumn">
{lastActivityColumnName}
</EuiTableHeader>
),
field: 'lastActivity',
render: (timestamp: number, { userPrivileges, title }: DataStreamStat) => (
<PrivilegesWarningIconWrapper
title={`lastActivity-${title}`}
hasPrivileges={userPrivileges?.canMonitor ?? true}
>
<EuiSkeletonRectangle
width="200px"
height="20px"
borderRadius="m"
isLoading={loadingDataStreamStats}
>
{!isActiveDataset(timestamp) ? (
<EuiFlexGroup gutterSize="xs" alignItems="center">
<EuiText size="s">{inactiveDatasetActivityColumnDescription}</EuiText>
<EuiToolTip position="top" content={inactiveDatasetActivityColumnTooltip}>
<EuiIcon tabIndex={0} type="iInCircle" size="s" />
</EuiToolTip>
</EuiFlexGroup>
) : (
fieldFormats
.getDefaultInstance(KBN_FIELD_TYPES.DATE, [ES_FIELD_TYPES.DATE])
.convert(timestamp)
)}
</EuiSkeletonRectangle>
</PrivilegesWarningIconWrapper>
),
width: '300px',
sortable: true,
},
]
: []),
{
name: (
<EuiTableHeader data-test-subj="datasetQualityLastActivityColumn">
{lastActivityColumnName}
</EuiTableHeader>
),
field: 'lastActivity',
render: (timestamp: number) => (
<EuiSkeletonRectangle
width="200px"
height="20px"
borderRadius="m"
isLoading={loadingDataStreamStats}
>
{!isActiveDataset(timestamp) ? (
<EuiFlexGroup gutterSize="xs" alignItems="center">
<EuiText size="s">{inactiveDatasetActivityColumnDescription}</EuiText>
<EuiToolTip position="top" content={inactiveDatasetActivityColumnTooltip}>
<EuiIcon tabIndex={0} type="iInCircle" size="s" />
</EuiToolTip>
</EuiFlexGroup>
) : (
fieldFormats
.getDefaultInstance(KBN_FIELD_TYPES.DATE, [ES_FIELD_TYPES.DATE])
.convert(timestamp)
)}
</EuiSkeletonRectangle>
),
width: '300px',
sortable: true,
},
{
name: actionsColumnName,
render: (dataStreamStat: DataStreamStat) => (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { _IGNORED } from '../../../../common/es_fields';
import { DataStreamDetails, DataStreamSettings } from '../../../../common/api_types';
import { createDatasetQualityESClient } from '../../../utils';
import { dataStreamService, datasetQualityPrivileges } from '../../../services';
import { getDataStreamsStats } from '../get_data_streams_stats';
import { getDataStreams } from '../get_data_streams';

export async function getDataStreamSettings({
esClient,
Expand Down Expand Up @@ -49,28 +49,27 @@ export async function getDataStreamDetails({
dataStream,
start,
end,
sizeStatsAvailable = true,
isServerless,
}: {
esClient: ElasticsearchClient;
dataStream: string;
start: number;
end: number;
sizeStatsAvailable?: boolean; // Only Needed to determine whether `_stats` endpoint is available https://github.com/elastic/kibana/issues/178954
isServerless: boolean;
}): Promise<DataStreamDetails> {
throwIfInvalidDataStreamParams(dataStream);

const hasAccessToDataStream = (
await datasetQualityPrivileges.getHasIndexPrivileges(esClient, [dataStream], ['monitor'])
)[dataStream];

const lastActivity = hasAccessToDataStream
const esDataStream = hasAccessToDataStream
? (
await getDataStreamsStats({
await getDataStreams({
esClient,
dataStreams: [dataStream],
sizeStatsAvailable,
datasetQuery: dataStream,
})
).items[0]?.lastActivity
).dataStreams[0]
: undefined;

try {
Expand All @@ -82,17 +81,17 @@ export async function getDataStreamDetails({
);

const whenSizeStatsNotAvailable = NaN; // This will indicate size cannot be calculated
const avgDocSizeInBytes = sizeStatsAvailable
? hasAccessToDataStream && dataStreamSummaryStats.docsCount > 0
? await getAvgDocSizeInBytes(esClient, dataStream)
: 0
: whenSizeStatsNotAvailable;
const avgDocSizeInBytes = isServerless
? whenSizeStatsNotAvailable
: hasAccessToDataStream && dataStreamSummaryStats.docsCount > 0
? await getAvgDocSizeInBytes(esClient, dataStream)
: 0;
const sizeBytes = Math.ceil(avgDocSizeInBytes * dataStreamSummaryStats.docsCount);

return {
...dataStreamSummaryStats,
sizeBytes,
lastActivity,
lastActivity: esDataStream?.lastActivity,
userPrivileges: {
canMonitor: hasAccessToDataStream,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,22 @@ describe('getDataStreams', () => {
const esClientMock = elasticsearchServiceMock.createElasticsearchClient();
const result = await getDataStreams({
esClient: esClientMock,
types: ['logs'],
datasetQuery: 'nginx-*',
types: ['logs', 'metrics'],
uncategorisedOnly: true,
});
expect(dataStreamService.getMatchingDataStreams).toHaveBeenCalledWith(
expect.anything(),
'logs-*-*,metrics-*-*'
);

expect(result.datasetUserPrivileges.canMonitor).toBe(true);
});

it('Passes datasetQuery parameter to the DataStreamService', async () => {
const esClientMock = elasticsearchServiceMock.createElasticsearchClient();
const result = await getDataStreams({
esClient: esClientMock,
datasetQuery: 'logs-nginx-*',
uncategorisedOnly: true,
});
expect(dataStreamService.getMatchingDataStreams).toHaveBeenCalledWith(
Expand All @@ -58,20 +72,18 @@ describe('getDataStreams', () => {
const results = await getDataStreams({
esClient: esClientMock,
types: ['logs'],
datasetQuery: 'nginx-*',
uncategorisedOnly: true,
});
expect(results.items.length).toBe(1);
expect(results.dataStreams.length).toBe(1);
});
it('Returns the correct number of results when false', async () => {
const esClientMock = elasticsearchServiceMock.createElasticsearchClient();
const results = await getDataStreams({
esClient: esClientMock,
types: ['logs'],
datasetQuery: 'nginx-*',
uncategorisedOnly: false,
});
expect(results.items.length).toBe(5);
expect(results.dataStreams.length).toBe(5);
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,20 @@ import { dataStreamService, datasetQualityPrivileges } from '../../../services';

export async function getDataStreams(options: {
esClient: ElasticsearchClient;
types: DataStreamType[];
types?: DataStreamType[];
datasetQuery?: string;
uncategorisedOnly: boolean;
uncategorisedOnly?: boolean;
}) {
const { esClient, types, datasetQuery, uncategorisedOnly } = options;
const { esClient, types = [], datasetQuery, uncategorisedOnly } = options;

const datasetNames = types.map((type) =>
streamPartsToIndexPattern({
typePattern: type,
datasetPattern: datasetQuery ? `${datasetQuery}` : '*-*',
})
);
const datasetNames = datasetQuery
? [datasetQuery]
: types.map((type) =>
streamPartsToIndexPattern({
typePattern: type,
datasetPattern: '*-*',
})
);

const datasetUserPrivileges = await datasetQualityPrivileges.getDatasetPrivileges(
esClient,
Expand All @@ -32,7 +34,7 @@ export async function getDataStreams(options: {

if (!datasetUserPrivileges.canMonitor) {
return {
items: [],
dataStreams: [],
datasetUserPrivileges,
};
}
Expand All @@ -59,13 +61,15 @@ export async function getDataStreams(options: {
const mappedDataStreams = filteredDataStreams.map((dataStream) => ({
name: dataStream.name,
integration: dataStream._meta?.package?.name,
// @ts-expect-error
lastActivity: dataStream.maximum_timestamp,
userPrivileges: {
canMonitor: dataStreamsPrivileges[dataStream.name],
},
}));

return {
items: mappedDataStreams,
dataStreams: mappedDataStreams,
datasetUserPrivileges,
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,42 +12,31 @@ import { indexStatsService } from '../../../services';
export async function getDataStreamsStats({
esClient,
dataStreams,
sizeStatsAvailable = true,
}: {
esClient: ElasticsearchClient;
dataStreams: string[];
sizeStatsAvailable?: boolean; // Only Needed to determine whether `_stats` endpoint is available https://github.com/elastic/kibana/issues/178954
}) {
}): Promise<Record<string, { size: string; sizeBytes: number; totalDocs: number }>> {
if (!dataStreams.length) {
return {
items: [],
};
return {};
}

const matchingDataStreamsStats = dataStreamService.getStreamsStats(esClient, dataStreams);

const indicesDocsCount = sizeStatsAvailable
? indexStatsService.getIndicesDocCounts(esClient, dataStreams)
: Promise.resolve(null);
const indicesDocsCount = indexStatsService.getIndicesDocCounts(esClient, dataStreams);

const [indicesDocsCountStats, dataStreamsStats] = await Promise.all([
indicesDocsCount,
matchingDataStreamsStats,
]);

const mappedDataStreams = dataStreamsStats.map((dataStream) => {
return {
name: dataStream.data_stream,
size: dataStream.store_size?.toString(),
sizeBytes: dataStream.store_size_bytes,
lastActivity: dataStream.maximum_timestamp,
totalDocs: sizeStatsAvailable
? indicesDocsCountStats!.docsCountPerDataStream[dataStream.data_stream] || 0
: null,
};
});

return {
items: mappedDataStreams,
};
return dataStreamsStats.reduce(
(acc, dataStream) => ({
...acc,
[dataStream.data_stream]: {
size: dataStream.store_size!.toString(),
sizeBytes: dataStream.store_size_bytes,
totalDocs: indicesDocsCountStats!.docsCountPerDataStream[dataStream.data_stream],
},
}),
{}
);
}
Loading

0 comments on commit d138ad1

Please sign in to comment.