Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -3,7 +3,6 @@ import React from 'react';
import {ResponseError} from '../../../../../components/Errors/ResponseError';
import {Loader} from '../../../../../components/Loader';
import type {IssuesTree} from '../../../../../store/reducers/healthcheckInfo/types';
import type {IResponseError} from '../../../../../types/api/error';
import {cn} from '../../../../../utils/cn';

import IssueTree from './IssuesViewer/IssueTree';
Expand All @@ -16,19 +15,18 @@ const b = cn('healthcheck');
interface HealthcheckDetailsProps {
issueTrees?: IssuesTree[];
loading?: boolean;
wasLoaded?: boolean;
error?: IResponseError;
error?: unknown;
}

export function HealthcheckDetails(props: HealthcheckDetailsProps) {
const {issueTrees, loading, wasLoaded, error} = props;
const {issueTrees, loading, error} = props;

const renderContent = () => {
if (error) {
return <ResponseError error={error} defaultMessage={i18n('no-data')} />;
}

if (loading && !wasLoaded) {
if (loading) {
return <Loader size="m" />;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import {EntityStatus} from '../../../../../components/EntityStatus/EntityStatus'
import {ResponseError} from '../../../../../components/Errors/ResponseError';
import {Loader} from '../../../../../components/Loader';
import {hcStatusToColorFlag} from '../../../../../store/reducers/healthcheckInfo/utils';
import type {IResponseError} from '../../../../../types/api/error';
import type {SelfCheckResult, StatusFlag} from '../../../../../types/api/healthcheck';
import {cn} from '../../../../../utils/cn';

Expand All @@ -23,19 +22,18 @@ interface HealthcheckPreviewProps {
selfCheckResult: SelfCheckResult;
issuesStatistics?: [StatusFlag, number][];
loading?: boolean;
wasLoaded?: boolean;
onUpdate: VoidFunction;
error?: IResponseError;
error?: unknown;
active?: boolean;
}

export function HealthcheckPreview(props: HealthcheckPreviewProps) {
const {selfCheckResult, issuesStatistics, loading, wasLoaded, onUpdate, error, active} = props;
const {selfCheckResult, issuesStatistics, loading, onUpdate, error, active} = props;

const renderHeader = () => {
const modifier = selfCheckResult.toLowerCase();

if (loading && !wasLoaded) {
if (loading) {
return null;
}

Expand All @@ -59,7 +57,7 @@ export function HealthcheckPreview(props: HealthcheckPreviewProps) {
return <ResponseError error={error} defaultMessage={i18n('no-data')} />;
}

if (loading && !wasLoaded) {
if (loading) {
return <Loader size="m" />;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import {
memoryUsageToStatus,
storageUsageToStatus,
} from '../../../../../store/reducers/tenants/utils';
import type {IResponseError} from '../../../../../types/api/error';
import type {SelfCheckResult, StatusFlag} from '../../../../../types/api/healthcheck';
import {cn} from '../../../../../utils/cn';
import {formatStorageValues} from '../../../../../utils/dataFormatters/dataFormatters';
Expand Down Expand Up @@ -60,7 +59,7 @@ interface MetricsCardsProps {
fetchHealthcheck: VoidFunction;
healthcheckLoading?: boolean;
healthCheckWasLoaded?: boolean;
healthcheckError?: IResponseError;
healthcheckError?: unknown;
}

export function MetricsCards({
Expand All @@ -72,7 +71,6 @@ export function MetricsCards({
selfCheckResult,
fetchHealthcheck,
healthcheckLoading,
healthCheckWasLoaded,
healthcheckError,
}: MetricsCardsProps) {
const location = useLocation();
Expand Down Expand Up @@ -136,7 +134,6 @@ export function MetricsCards({
issuesStatistics={issuesStatistics}
onUpdate={fetchHealthcheck}
loading={healthcheckLoading}
wasLoaded={healthCheckWasLoaded}
error={healthcheckError}
active={metricsTab === TENANT_METRICS_TABS_IDS.healthcheck}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,10 @@ export function TenantOverview({
issueTrees,
issuesStatistics,
selfCheckResult,
fetchHealthcheck,
loading: healthcheckLoading,
wasLoaded: healthCheckWasLoaded,
error: healthcheckError,
} = useHealthcheck(tenantName);
refetch: fetchHealthcheck,
} = useHealthcheck(tenantName, {autorefresh});

const fetchTenant = React.useCallback(
(isBackground = true) => {
Expand All @@ -66,9 +65,8 @@ export function TenantOverview({
useAutofetcher(
(isBackground) => {
fetchTenant(isBackground);
fetchHealthcheck(isBackground);
},
[fetchTenant, fetchHealthcheck],
[fetchTenant],
autorefresh,
);

Expand Down Expand Up @@ -125,7 +123,6 @@ export function TenantOverview({
<HealthcheckDetails
issueTrees={issueTrees}
loading={healthcheckLoading}
wasLoaded={healthCheckWasLoaded}
error={healthcheckError}
/>
);
Expand Down Expand Up @@ -161,7 +158,6 @@ export function TenantOverview({
selfCheckResult={selfCheckResult}
fetchHealthcheck={fetchHealthcheck}
healthcheckLoading={healthcheckLoading}
healthCheckWasLoaded={healthCheckWasLoaded}
healthcheckError={healthcheckError}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,53 +1,45 @@
import React from 'react';

import {DEFAULT_POLLING_INTERVAL} from '../../../../lib';
import {
getHealthcheckInfo,
healthcheckApi,
selectIssuesStatistics,
selectIssuesTrees,
setDataWasNotLoaded,
} from '../../../../store/reducers/healthcheckInfo/healthcheckInfo';
import type {IssuesTree} from '../../../../store/reducers/healthcheckInfo/types';
import type {IResponseError} from '../../../../types/api/error';
import {SelfCheckResult} from '../../../../types/api/healthcheck';
import type {StatusFlag} from '../../../../types/api/healthcheck';
import {useTypedDispatch, useTypedSelector} from '../../../../utils/hooks';
import {useTypedSelector} from '../../../../utils/hooks';

interface HealthcheckParams {
issueTrees: IssuesTree[];
issuesStatistics: [StatusFlag, number][];
fetchHealthcheck: (isBackground?: boolean) => void;
loading: boolean;
wasLoaded: boolean;
error?: IResponseError;
error?: unknown;
refetch: () => void;
selfCheckResult: SelfCheckResult;
}

export const useHealthcheck = (tenantName: string): HealthcheckParams => {
const dispatch = useTypedDispatch();

const {data, loading, wasLoaded, error} = useTypedSelector((state) => state.healthcheckInfo);
export const useHealthcheck = (
tenantName: string,
{autorefresh}: {autorefresh?: boolean} = {},
): HealthcheckParams => {
const {
currentData: data,
isFetching,
error,
refetch,
} = healthcheckApi.useGetHealthcheckInfoQuery(tenantName, {
pollingInterval: autorefresh ? DEFAULT_POLLING_INTERVAL : 0,
});
const selfCheckResult = data?.self_check_result || SelfCheckResult.UNSPECIFIED;
const issuesStatistics = useTypedSelector(selectIssuesStatistics);
const issueTrees = useTypedSelector(selectIssuesTrees);

const fetchHealthcheck = React.useCallback(
(isBackground = true) => {
if (!isBackground) {
dispatch(setDataWasNotLoaded());
}

dispatch(getHealthcheckInfo(tenantName));
},
[dispatch, tenantName],
);
const issuesStatistics = useTypedSelector((state) => selectIssuesStatistics(state, tenantName));
const issueTrees = useTypedSelector((state) => selectIssuesTrees(state, tenantName));

return {
issueTrees,
issuesStatistics,
fetchHealthcheck,
loading,
wasLoaded,
loading: data === undefined && isFetching,
error,
refetch,
selfCheckResult,
};
};
4 changes: 2 additions & 2 deletions src/services/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -385,11 +385,11 @@ export class YdbEmbeddedAPI extends AxiosWrapper {
{concurrentId: concurrentId || 'getHotKeys'},
);
}
getHealthcheckInfo(database: string, {concurrentId}: AxiosOptions = {}) {
getHealthcheckInfo(database: string, {concurrentId, signal}: AxiosOptions = {}) {
return this.get<HealthCheckAPIResponse>(
this.getPath('/viewer/json/healthcheck?merge_records=true'),
{tenant: database},
{concurrentId},
{concurrentId, requestConfig: {signal}},
);
}
evictVDisk({
Expand Down
129 changes: 41 additions & 88 deletions src/store/reducers/healthcheckInfo/healthcheckInfo.ts
Original file line number Diff line number Diff line change
@@ -1,68 +1,26 @@
import {createSelector} from '@reduxjs/toolkit';
import type {Reducer, Selector} from '@reduxjs/toolkit';

import type {IssueLog, StatusFlag} from '../../../types/api/healthcheck';
import {createApiRequest, createRequestActionTypes} from '../../utils';

import type {
HealthCheckInfoAction,
HealthcheckInfoRootStateSlice,
HealthcheckInfoState,
IssuesTree,
} from './types';

export const FETCH_HEALTHCHECK = createRequestActionTypes('cluster', 'FETCH_HEALTHCHECK');

const SET_DATA_WAS_NOT_LOADED = 'healthcheckInfo/SET_DATA_WAS_NOT_LOADED';

const initialState = {loading: false, wasLoaded: false};

const healthcheckInfo: Reducer<HealthcheckInfoState, HealthCheckInfoAction> = function (
state = initialState,
action,
) {
switch (action.type) {
case FETCH_HEALTHCHECK.REQUEST: {
return {
...state,
loading: true,
};
}
case FETCH_HEALTHCHECK.SUCCESS: {
const {data} = action;

return {
...state,
data,
wasLoaded: true,
loading: false,
error: undefined,
};
}
case FETCH_HEALTHCHECK.FAILURE: {
if (action.error?.isCancelled) {
return state;
}

return {
...state,
error: action.error,
loading: false,
wasLoaded: true,
data: undefined,
};
}

case SET_DATA_WAS_NOT_LOADED: {
return {
...state,
wasLoaded: false,
};
}
default:
return state;
}
};
import type {RootState} from '../../defaultStore';
import {api} from '../api';

import type {IssuesTree} from './types';

export const healthcheckApi = api.injectEndpoints({
endpoints: (builder) => ({
getHealthcheckInfo: builder.query({
queryFn: async (database: string, {signal}) => {
try {
const data = await window.api.getHealthcheckInfo(database, {signal});
return {data};
} catch (error) {
return {error};
}
},
providesTags: ['All'],
}),
}),
});

const mapStatusToPriority: Partial<Record<StatusFlag, number>> = {
RED: 0,
Expand Down Expand Up @@ -133,33 +91,28 @@ const getIssuesStatistics = (data: IssueLog[]): [StatusFlag, number][] => {
});
};

const getIssuesLog = (state: HealthcheckInfoRootStateSlice) =>
state.healthcheckInfo.data?.issue_log;
const createGetHealthcheckInfoSelector = createSelector(
(database: string) => database,
(database) => healthcheckApi.endpoints.getHealthcheckInfo.select(database),
);

export const selectIssuesTreesRoots: Selector<HealthcheckInfoRootStateSlice, IssueLog[]> =
createSelector(getIssuesLog, (issues = []) => getRoots(issues));
const getIssuesLog = createSelector(
(state: RootState) => state,
(_state: RootState, database: string) => createGetHealthcheckInfoSelector(database),
(state: RootState, selectGetPost) => selectGetPost(state).data?.issue_log || [],
);

export const selectIssuesTrees: Selector<HealthcheckInfoRootStateSlice, IssuesTree[]> =
createSelector([getIssuesLog, selectIssuesTreesRoots], (data = [], roots = []) => {
return getInvertedConsequencesTree({data, roots});
});
export const selectIssuesTreesRoots = createSelector(getIssuesLog, (issues = []) =>
getRoots(issues),
);

export const selectIssuesStatistics: Selector<
HealthcheckInfoRootStateSlice,
[StatusFlag, number][]
> = createSelector(getIssuesLog, (issues = []) => getIssuesStatistics(issues));

export function getHealthcheckInfo(database: string) {
return createApiRequest({
request: window.api.getHealthcheckInfo(database, {concurrentId: 'getHealthcheckInfo'}),
actions: FETCH_HEALTHCHECK,
});
}

export const setDataWasNotLoaded = () => {
return {
type: SET_DATA_WAS_NOT_LOADED,
} as const;
};
export const selectIssuesTrees = createSelector(
[getIssuesLog, selectIssuesTreesRoots],
(data = [], roots = []) => {
return getInvertedConsequencesTree({data, roots});
},
);

export default healthcheckInfo;
export const selectIssuesStatistics = createSelector(getIssuesLog, (issues = []) =>
getIssuesStatistics(issues),
);
Loading