diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckDetails.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckDetails.tsx
index bd40b3662a..55fbbed220 100644
--- a/src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckDetails.tsx
+++ b/src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckDetails.tsx
@@ -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';
@@ -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 ;
}
- if (loading && !wasLoaded) {
+ if (loading) {
return ;
}
diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckPreview.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckPreview.tsx
index f4ccaa0cd9..1f117115b9 100644
--- a/src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckPreview.tsx
+++ b/src/containers/Tenant/Diagnostics/TenantOverview/Healthcheck/HealthcheckPreview.tsx
@@ -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';
@@ -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;
}
@@ -59,7 +57,7 @@ export function HealthcheckPreview(props: HealthcheckPreviewProps) {
return ;
}
- if (loading && !wasLoaded) {
+ if (loading) {
return ;
}
diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx
index e4b479bdff..18ff295f4e 100644
--- a/src/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx
+++ b/src/containers/Tenant/Diagnostics/TenantOverview/MetricsCards/MetricsCards.tsx
@@ -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';
@@ -60,7 +59,7 @@ interface MetricsCardsProps {
fetchHealthcheck: VoidFunction;
healthcheckLoading?: boolean;
healthCheckWasLoaded?: boolean;
- healthcheckError?: IResponseError;
+ healthcheckError?: unknown;
}
export function MetricsCards({
@@ -72,7 +71,6 @@ export function MetricsCards({
selfCheckResult,
fetchHealthcheck,
healthcheckLoading,
- healthCheckWasLoaded,
healthcheckError,
}: MetricsCardsProps) {
const location = useLocation();
@@ -136,7 +134,6 @@ export function MetricsCards({
issuesStatistics={issuesStatistics}
onUpdate={fetchHealthcheck}
loading={healthcheckLoading}
- wasLoaded={healthCheckWasLoaded}
error={healthcheckError}
active={metricsTab === TENANT_METRICS_TABS_IDS.healthcheck}
/>
diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx
index 5484683cf1..49215a9fdb 100644
--- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx
+++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.tsx
@@ -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) => {
@@ -66,9 +65,8 @@ export function TenantOverview({
useAutofetcher(
(isBackground) => {
fetchTenant(isBackground);
- fetchHealthcheck(isBackground);
},
- [fetchTenant, fetchHealthcheck],
+ [fetchTenant],
autorefresh,
);
@@ -125,7 +123,6 @@ export function TenantOverview({
);
@@ -161,7 +158,6 @@ export function TenantOverview({
selfCheckResult={selfCheckResult}
fetchHealthcheck={fetchHealthcheck}
healthcheckLoading={healthcheckLoading}
- healthCheckWasLoaded={healthCheckWasLoaded}
healthcheckError={healthcheckError}
/>
diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/useHealthcheck.ts b/src/containers/Tenant/Diagnostics/TenantOverview/useHealthcheck.ts
index be54290c06..43331c703e 100644
--- a/src/containers/Tenant/Diagnostics/TenantOverview/useHealthcheck.ts
+++ b/src/containers/Tenant/Diagnostics/TenantOverview/useHealthcheck.ts
@@ -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,
};
};
diff --git a/src/services/api.ts b/src/services/api.ts
index 4c1930ca21..0fd42a3e9f 100644
--- a/src/services/api.ts
+++ b/src/services/api.ts
@@ -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(
this.getPath('/viewer/json/healthcheck?merge_records=true'),
{tenant: database},
- {concurrentId},
+ {concurrentId, requestConfig: {signal}},
);
}
evictVDisk({
diff --git a/src/store/reducers/healthcheckInfo/healthcheckInfo.ts b/src/store/reducers/healthcheckInfo/healthcheckInfo.ts
index a9c8ab396d..d982730cf9 100644
--- a/src/store/reducers/healthcheckInfo/healthcheckInfo.ts
+++ b/src/store/reducers/healthcheckInfo/healthcheckInfo.ts
@@ -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 = 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> = {
RED: 0,
@@ -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 =
- 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 =
- 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),
+);
diff --git a/src/store/reducers/healthcheckInfo/types.ts b/src/store/reducers/healthcheckInfo/types.ts
index 538b48ae1b..ad0db05dd3 100644
--- a/src/store/reducers/healthcheckInfo/types.ts
+++ b/src/store/reducers/healthcheckInfo/types.ts
@@ -1,30 +1,5 @@
-import type {IResponseError} from '../../../types/api/error';
-import type {HealthCheckAPIResponse, IssueLog} from '../../../types/api/healthcheck';
-import type {ApiRequestAction} from '../../utils';
-
-import type {FETCH_HEALTHCHECK, setDataWasNotLoaded} from './healthcheckInfo';
+import type {IssueLog} from '../../../types/api/healthcheck';
export interface IssuesTree extends IssueLog {
reasonsItems?: IssuesTree[];
}
-
-export interface HealthcheckInfoState {
- loading: boolean;
- wasLoaded: boolean;
- data?: HealthCheckAPIResponse;
- error?: IResponseError;
-}
-
-type HealthCheckApiRequestAction = ApiRequestAction<
- typeof FETCH_HEALTHCHECK,
- HealthCheckAPIResponse,
- IResponseError
->;
-
-export type HealthCheckInfoAction =
- | HealthCheckApiRequestAction
- | ReturnType;
-
-export interface HealthcheckInfoRootStateSlice {
- healthcheckInfo: HealthcheckInfoState;
-}
diff --git a/src/store/reducers/index.ts b/src/store/reducers/index.ts
index 926c0c5053..44b235d132 100644
--- a/src/store/reducers/index.ts
+++ b/src/store/reducers/index.ts
@@ -10,7 +10,6 @@ import executeTopQueries from './executeTopQueries/executeTopQueries';
import explainQuery from './explainQuery';
import fullscreen from './fullscreen';
import header from './header/header';
-import healthcheckInfo from './healthcheckInfo/healthcheckInfo';
import heatmap from './heatmap';
import host from './host';
import hotKeys from './hotKeys/hotKeys';
@@ -82,7 +81,6 @@ export const rootReducer = {
executeTopQueries,
executeTopTables,
tenantOverviewTopQueries,
- healthcheckInfo,
shardsWorkload,
tenantOverviewTopShards,
hotKeys,