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

[Dataset quality] Support failure store #199806

Draft
wants to merge 27 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
81ad3fa
Fetching failed docs and showing them in the table
yngrdyn Nov 12, 2024
301a53a
Merge branch 'elastic:main' into main
yngrdyn Nov 12, 2024
2ad742f
fixing tests
yngrdyn Nov 12, 2024
4fe638e
Addressing copies across table and page
yngrdyn Nov 12, 2024
56a1793
Merge branch 'main' into dataset-quality-support-for-failure-store
yngrdyn Nov 12, 2024
e11fabf
Column names changed in tests
yngrdyn Nov 13, 2024
a9f173c
Merge branch 'main' into dataset-quality-support-for-failure-store
yngrdyn Nov 13, 2024
41d249a
Adding failed info to dataset details
yngrdyn Nov 14, 2024
91e2c3e
Addresing Pr cmments
yngrdyn Nov 18, 2024
f753581
extracting percentage calculator to an util
yngrdyn Nov 19, 2024
74df05c
merging useDocsChart hooks for quality issues + removing unnecesary u…
yngrdyn Nov 19, 2024
73a90be
Added qualityIssuesChart to the store + syncing URL
yngrdyn Nov 19, 2024
5ff14d6
totalDocs should include also failedDocs
yngrdyn Nov 19, 2024
1ba2535
Merge branch 'main' into dataset-quality-support-for-failure-store
yngrdyn Nov 19, 2024
b70c960
fixing i18n problems
yngrdyn Nov 19, 2024
36131aa
fixing build
yngrdyn Nov 19, 2024
ad7d150
fixing copies
yngrdyn Nov 20, 2024
5f1b389
Added e2e tests to fialure store support
yngrdyn Nov 21, 2024
2e47a6b
Merge branch 'main' into dataset-quality-support-for-failure-store
yngrdyn Nov 21, 2024
3715579
fixing i18n id duplicated
yngrdyn Nov 21, 2024
dd9816e
fixing some e2e tests
yngrdyn Nov 22, 2024
076cb6c
fixing e2e tests
yngrdyn Nov 25, 2024
0057ecb
Merge branch 'main' into dataset-quality-support-for-failure-store
yngrdyn Nov 26, 2024
f10abac
fixing tests
yngrdyn Nov 26, 2024
f9ba989
fixing some tests
yngrdyn Nov 27, 2024
c6846eb
Merge branch 'elastic:main' into main
yngrdyn Nov 27, 2024
895110f
Merge remote-tracking branch 'origin/main' into dataset-quality-suppo…
yngrdyn Nov 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ export const getDataStreamDegradedDocsResponseRt = rt.type({

export type DataStreamDegradedDocsResponse = rt.TypeOf<typeof getDataStreamDegradedDocsResponseRt>;

export const getDataStreamFailedDocsResponseRt = rt.type({
failedDocs: rt.array(dataStreamDocsStatRt),
});

export type DataStreamFailedDocsResponse = rt.TypeOf<typeof getDataStreamFailedDocsResponseRt>;

export const integrationDashboardRT = rt.type({
id: rt.string,
title: rt.string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export const NONE = 'none';
export const DEFAULT_TIME_RANGE = { from: 'now-24h', to: 'now' };
export const DEFAULT_DATEPICKER_REFRESH = { value: 60000, pause: false };

export const DEFAULT_DEGRADED_DOCS = {
export const DEFAULT_QUALITY_DOC_STATS = {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note

This is now reused by degradedDocs and failedDocs.

count: 0,
percentage: 0,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
*/

import { DataStreamDocsStat } from '../api_types';
import { DEFAULT_DATASET_QUALITY, DEFAULT_DEGRADED_DOCS } from '../constants';
import { DEFAULT_DATASET_QUALITY, DEFAULT_QUALITY_DOC_STATS } from '../constants';
import { DataStreamType, QualityIndicators } from '../types';
import { indexNameToDataStreamParts, mapPercentageToQuality } from '../utils';
import { Integration } from './integration';
import { DataStreamStatType } from './types';

type QualityStat = Omit<DataStreamDocsStat, 'dataset'> & { percentage: number };

export class DataStreamStat {
rawName: string;
type: DataStreamType;
Expand All @@ -30,6 +32,10 @@ export class DataStreamStat {
percentage: number;
count: number;
};
failedDocs: {
percentage: number;
count: number;
};

private constructor(dataStreamStat: DataStreamStat) {
this.rawName = dataStreamStat.rawName;
Expand All @@ -46,6 +52,7 @@ export class DataStreamStat {
this.quality = dataStreamStat.quality;
this.docsInTimeRange = dataStreamStat.docsInTimeRange;
this.degradedDocs = dataStreamStat.degradedDocs;
this.failedDocs = dataStreamStat.failedDocs;
}

public static create(dataStreamStat: DataStreamStatType) {
Expand All @@ -63,36 +70,45 @@ export class DataStreamStat {
userPrivileges: dataStreamStat.userPrivileges,
totalDocs: dataStreamStat.totalDocs,
quality: DEFAULT_DATASET_QUALITY,
degradedDocs: DEFAULT_DEGRADED_DOCS,
degradedDocs: DEFAULT_QUALITY_DOC_STATS,
failedDocs: DEFAULT_QUALITY_DOC_STATS,
};

return new DataStreamStat(dataStreamStatProps);
}

public static fromDegradedDocStat({
public static fromQualityStats({
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Note

We can extend this method with more qualityStats and construct a Dataset from them

datasetName,
degradedDocStat,
failedDocStat,
datasetIntegrationMap,
totalDocs,
}: {
degradedDocStat: DataStreamDocsStat & { percentage: number };
datasetName: string;
degradedDocStat: QualityStat;
failedDocStat: QualityStat;
datasetIntegrationMap: Record<string, { integration: Integration; title: string }>;
totalDocs: number;
}) {
const { type, dataset, namespace } = indexNameToDataStreamParts(degradedDocStat.dataset);
const { type, dataset, namespace } = indexNameToDataStreamParts(datasetName);

const dataStreamStatProps = {
rawName: degradedDocStat.dataset,
rawName: datasetName,
type,
name: dataset,
title: datasetIntegrationMap[dataset]?.title || dataset,
title: datasetIntegrationMap[datasetName]?.title || dataset,
namespace,
integration: datasetIntegrationMap[dataset]?.integration,
quality: mapPercentageToQuality(degradedDocStat.percentage),
integration: datasetIntegrationMap[datasetName]?.integration,
quality: mapPercentageToQuality([degradedDocStat.percentage, failedDocStat.percentage]),
docsInTimeRange: totalDocs,
degradedDocs: {
percentage: degradedDocStat.percentage,
count: degradedDocStat.count,
},
failedDocs: {
percentage: failedDocStat.percentage,
count: failedDocStat.count,
},
};

return new DataStreamStat(dataStreamStatProps);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ export type DataStreamStatServiceResponse = GetDataStreamsStatsResponse;
export type GetDataStreamsDegradedDocsStatsParams =
APIClientRequestParamsOf<`GET /internal/dataset_quality/data_streams/degraded_docs`>['params'];
export type GetDataStreamsDegradedDocsStatsQuery = GetDataStreamsDegradedDocsStatsParams['query'];
export type GetDataStreamsFailedDocsStatsParams =
APIClientRequestParamsOf<`GET /internal/dataset_quality/data_streams/failed_docs`>['params'];
export type GetDataStreamsFailedDocsStatsQuery = GetDataStreamsFailedDocsStatsParams['query'];

/*
Types for stats based in documents inside a DataStream
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,11 @@ describe('dataset_name', () => {
extractIndexNameFromBackingIndex('.ds-metrics-apm.app.adservice-default-2024.04.29-000001')
).toEqual('metrics-apm.app.adservice-default');
});

it('returns the correct index name if backing index is a failure store index', () => {
expect(
extractIndexNameFromBackingIndex('.fs-logs-elastic_agent-default-2024.11.11-000001')
).toEqual('logs-elastic_agent-default');
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const indexNameToDataStreamParts = (dataStreamName: string) => {
};

export const extractIndexNameFromBackingIndex = (indexString: string): string => {
const pattern = /.ds-(.*?)-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-[0-9]{6}/;
const pattern = /.(?:ds|fs)-(.*?)-[0-9]{4}\.[0-9]{2}\.[0-9]{2}-[0-9]{6}/;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Important

Failure store is at the moment a backing index that starts with .fs

const match = indexString.match(pattern);

return match ? match[1] : indexString;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@
import { POOR_QUALITY_MINIMUM_PERCENTAGE, DEGRADED_QUALITY_MINIMUM_PERCENTAGE } from '../constants';
import { QualityIndicators } from '../types';

export const mapPercentageToQuality = (percentage: number): QualityIndicators => {
return percentage > POOR_QUALITY_MINIMUM_PERCENTAGE
? 'poor'
: percentage > DEGRADED_QUALITY_MINIMUM_PERCENTAGE
? 'degraded'
: 'good';
export const mapPercentageToQuality = (percentages: number[]): QualityIndicators => {
yngrdyn marked this conversation as resolved.
Show resolved Hide resolved
if (percentages.some((percentage) => percentage > POOR_QUALITY_MINIMUM_PERCENTAGE)) {
return 'poor';
}

if (percentages.some((percentage) => percentage > DEGRADED_QUALITY_MINIMUM_PERCENTAGE)) {
return 'degraded';
}

return 'good';
};
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,11 @@ import {
summaryPanelQualityText,
summaryPanelQualityTooltipText,
} from '../../../../common/translations';
import { mapPercentagesToQualityCounts } from '../../quality_indicator';

export function DatasetsQualityIndicators() {
const { onPageReady } = usePerformanceContext();
const {
datasetsQuality,
isDatasetsQualityLoading,
datasetsActivity,
numberOfDatasets,
numberOfDocuments,
} = useSummaryPanelContext();
const qualityCounts = mapPercentagesToQualityCounts(datasetsQuality.percentages);
const datasetsWithoutIgnoredField =
datasetsActivity.total > 0 ? datasetsActivity.total - datasetsQuality.percentages.length : 0;
const { datasetsQuality, isDatasetsQualityLoading, numberOfDatasets, numberOfDocuments } =
useSummaryPanelContext();

if (!isDatasetsQualityLoading && (numberOfDatasets || numberOfDocuments)) {
onPageReady({
Expand All @@ -66,21 +57,21 @@ export function DatasetsQualityIndicators() {
</EuiFlexGroup>
<EuiFlexGroup gutterSize="m" alignItems="flexEnd">
<QualityIndicator
value={qualityCounts.poor}
value={datasetsQuality.poor}
quality="danger"
description={summaryPanelQualityPoorText}
isLoading={isDatasetsQualityLoading}
/>
<span css={verticalRule} />
<QualityIndicator
value={qualityCounts.degraded}
value={datasetsQuality.degraded}
quality="warning"
description={summaryPanelQualityDegradedText}
isLoading={isDatasetsQualityLoading}
/>
<span css={verticalRule} />
<QualityIndicator
value={qualityCounts.good + datasetsWithoutIgnoredField}
value={datasetsQuality.good}
quality="success"
description={summaryPanelQualityGoodText}
isLoading={isDatasetsQualityLoading}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { useDatasetRedirectLinkTelemetry, useRedirectLink } from '../../../hooks
import { DegradedDocsPercentageLink } from './degraded_docs_percentage_link';
import { TimeRangeConfig } from '../../../../common/types';
import { DatasetQualityDetailsLink } from './dataset_quality_details_link';
import { FailedDocsPercentageLink } from './failed_docs_percentage_link';

const nameColumnName = i18n.translate('xpack.datasetQuality.nameColumnName', {
defaultMessage: 'Data Set Name',
Expand All @@ -58,6 +59,10 @@ const degradedDocsColumnName = i18n.translate('xpack.datasetQuality.degradedDocs
defaultMessage: 'Degraded Docs (%)',
});

const failedDocsColumnName = i18n.translate('xpack.datasetQuality.failedDocsColumnName', {
defaultMessage: 'Failed Docs (%)',
});

const datasetQualityColumnName = i18n.translate('xpack.datasetQuality.datasetQualityColumnName', {
defaultMessage: 'Data Set Quality',
});
Expand Down Expand Up @@ -112,10 +117,17 @@ const degradedDocsColumnTooltip = (
/>
);

const failedDocsColumnTooltip = (
<FormattedMessage
id="xpack.datasetQuality.failedDocsColumnTooltip"
defaultMessage="The percentage of documents in the failure store for your data set."
/>
);

const datasetQualityColumnTooltip = (
<FormattedMessage
id="xpack.datasetQuality.datasetQualityColumnTooltip"
defaultMessage="Quality is based on the percentage of degraded docs in a data set. {visualQueue}"
defaultMessage="Quality is based on the percentage of degraded and failed docs in a data set. {visualQueue}"
values={{
visualQueue: (
<EuiFlexGroup direction="column" gutterSize="xs">
Expand Down Expand Up @@ -273,7 +285,7 @@ export const getDatasetQualityTableColumns = ({
</EuiToolTip>
</EuiTableHeader>
),
field: 'degradedDocs.percentage',
field: 'quality',
sortable: true,
render: (_, dataStreamStat: DataStreamStat) => (
<DatasetQualityIndicator isLoading={loadingDegradedStats} dataStreamStat={dataStreamStat} />
Expand Down Expand Up @@ -304,35 +316,61 @@ export const getDatasetQualityTableColumns = ({
},
{
name: (
<EuiTableHeader data-test-subj="datasetQualityLastActivityColumn">
{lastActivityColumnName}
<EuiTableHeader data-test-subj="datasetQualityFailedPercentageColumn">
<EuiToolTip content={failedDocsColumnTooltip}>
<span>
{`${failedDocsColumnName} `}
<EuiIcon size="s" color="subdued" type="questionInCircle" className="eui-alignTop" />
</span>
</EuiToolTip>
</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',
field: 'failedDocs.percentage',
sortable: true,
render: (_, dataStreamStat: DataStreamStat) => (
<FailedDocsPercentageLink
isLoading={loadingDegradedStats}
yngrdyn marked this conversation as resolved.
Show resolved Hide resolved
dataStreamStat={dataStreamStat}
timeRange={timeRange}
/>
),
width: '140px',
},
...(canUserMonitorDataset && canUserMonitorAnyDataStream
? [
{
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
@@ -0,0 +1,34 @@
/*
mohamedhamed-ahmed marked this conversation as resolved.
Show resolved Hide resolved
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { EuiSkeletonRectangle, EuiFlexGroup } from '@elastic/eui';
import React from 'react';
import { QualityPercentageIndicator } from '../../quality_indicator';
import { DataStreamStat } from '../../../../common/data_streams_stats/data_stream_stat';
import { TimeRangeConfig } from '../../../../common/types';

export const FailedDocsPercentageLink = ({
isLoading,
dataStreamStat,
timeRange,
}: {
isLoading: boolean;
dataStreamStat: DataStreamStat;
timeRange: TimeRangeConfig;
}) => {
const {
failedDocs: { percentage },
} = dataStreamStat;

return (
<EuiSkeletonRectangle width="50px" height="20px" borderRadius="m" isLoading={isLoading}>
<EuiFlexGroup alignItems="center" gutterSize="s">
<QualityPercentageIndicator percentage={percentage} />
</EuiFlexGroup>
</EuiSkeletonRectangle>
);
};

This file was deleted.

Loading