From d3e69bc91dad5c0eab73ee494c84e2f204aa815b Mon Sep 17 00:00:00 2001 From: mufazalov Date: Tue, 10 Sep 2024 18:27:04 +0300 Subject: [PATCH 1/2] feat: use groups handler for storage --- .../PDiskPage/PDiskGroups/PDiskGroups.tsx | 15 ++++- src/containers/Storage/Storage.tsx | 16 ++++- .../StorageGroups/PaginatedStorageGroups.tsx | 38 +++++++---- .../Storage/StorageGroups/getGroups.ts | 65 ++++++++++--------- .../TenantStorage/TopGroups.tsx | 14 +++- src/containers/VDiskPage/VDiskPage.tsx | 35 ++++++---- src/services/api.ts | 2 +- src/store/reducers/capabilities/hooks.ts | 12 +++- .../reducers/storage/requestStorageData.ts | 21 ++++++ src/store/reducers/storage/storage.ts | 15 +++-- 10 files changed, 160 insertions(+), 73 deletions(-) create mode 100644 src/store/reducers/storage/requestStorageData.ts diff --git a/src/containers/PDiskPage/PDiskGroups/PDiskGroups.tsx b/src/containers/PDiskPage/PDiskGroups/PDiskGroups.tsx index be0c79f7a1..694d553644 100644 --- a/src/containers/PDiskPage/PDiskGroups/PDiskGroups.tsx +++ b/src/containers/PDiskPage/PDiskGroups/PDiskGroups.tsx @@ -2,6 +2,10 @@ import React from 'react'; import {ResizeableDataTable} from '../../../components/ResizeableDataTable/ResizeableDataTable'; import {TableSkeleton} from '../../../components/TableSkeleton/TableSkeleton'; +import { + useCapabilitiesLoaded, + useStorageGroupsHandlerAvailable, +} from '../../../store/reducers/capabilities/hooks'; import {storageApi} from '../../../store/reducers/storage/storage'; import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants'; import {useAutoRefreshInterval} from '../../../utils/hooks'; @@ -16,11 +20,16 @@ interface PDiskGroupsProps { } export function PDiskGroups({pDiskId, nodeId}: PDiskGroupsProps) { + const capabilitiesLoaded = useCapabilitiesLoaded(); + const groupsHandlerAvailable = useStorageGroupsHandlerAvailable(); const [autoRefreshInterval] = useAutoRefreshInterval(); const {currentData, isFetching} = storageApi.useGetStorageGroupsInfoQuery( - {pDiskId, nodeId}, - {pollingInterval: autoRefreshInterval}, + {pDiskId, nodeId, useGroupsHandler: groupsHandlerAvailable}, + { + pollingInterval: autoRefreshInterval, + skip: !capabilitiesLoaded, + }, ); const loading = isFetching && currentData === undefined; @@ -30,7 +39,7 @@ export function PDiskGroups({pDiskId, nodeId}: PDiskGroupsProps) { const pDiskStorageColumns = useGetDiskStorageColumns(); - if (loading) { + if (loading || !capabilitiesLoaded) { return ; } diff --git a/src/containers/Storage/Storage.tsx b/src/containers/Storage/Storage.tsx index 825fdf5e94..63d0195841 100644 --- a/src/containers/Storage/Storage.tsx +++ b/src/containers/Storage/Storage.tsx @@ -6,6 +6,10 @@ import {AccessDenied} from '../../components/Errors/403'; import {isAccessError} from '../../components/Errors/PageError/PageError'; import {ResponseError} from '../../components/Errors/ResponseError'; import {TableWithControlsLayout} from '../../components/TableWithControlsLayout/TableWithControlsLayout'; +import { + useCapabilitiesLoaded, + useStorageGroupsHandlerAvailable, +} from '../../store/reducers/capabilities/hooks'; import type {NodesSortParams} from '../../store/reducers/nodes/types'; import {STORAGE_TYPES, VISIBLE_ENTITIES} from '../../store/reducers/storage/constants'; import { @@ -57,7 +61,10 @@ interface StorageProps { } export const Storage = ({additionalNodesProps, database, nodeId}: StorageProps) => { + const capabilitiesLoaded = useCapabilitiesLoaded(); + const groupsHandlerAvailable = useStorageGroupsHandlerAvailable(); const [autoRefreshInterval] = useAutoRefreshInterval(); + const [queryParams, setQueryParams] = useQueryParams({ type: StringParam, visible: StringParam, @@ -95,9 +102,9 @@ export const Storage = ({additionalNodesProps, database, nodeId}: StorageProps) }, ); const groupsQuery = storageApi.useGetStorageGroupsInfoQuery( - {database, with: visibleEntities, nodeId}, + {database, with: visibleEntities, nodeId, useGroupsHandler: groupsHandlerAvailable}, { - skip: storageType !== STORAGE_TYPES.groups, + skip: storageType !== STORAGE_TYPES.groups || !capabilitiesLoaded, pollingInterval: autoRefreshInterval, }, ); @@ -220,7 +227,10 @@ export const Storage = ({additionalNodesProps, database, nodeId}: StorageProps) {renderControls()} {error ? : null} - + {currentData ? renderDataTable() : null} diff --git a/src/containers/Storage/StorageGroups/PaginatedStorageGroups.tsx b/src/containers/Storage/StorageGroups/PaginatedStorageGroups.tsx index 0942116270..09a55b0658 100644 --- a/src/containers/Storage/StorageGroups/PaginatedStorageGroups.tsx +++ b/src/containers/Storage/StorageGroups/PaginatedStorageGroups.tsx @@ -1,14 +1,19 @@ import React from 'react'; +import {LoaderWrapper} from '../../../components/LoaderWrapper/LoaderWrapper'; import type {RenderControls, RenderErrorMessage} from '../../../components/PaginatedTable'; import {ResizeablePaginatedTable} from '../../../components/PaginatedTable'; +import { + useCapabilitiesLoaded, + useStorageGroupsHandlerAvailable, +} from '../../../store/reducers/capabilities/hooks'; import {VISIBLE_ENTITIES} from '../../../store/reducers/storage/constants'; import type {VisibleEntities} from '../../../store/reducers/storage/types'; import {StorageGroupsEmptyDataMessage} from './StorageGroupsEmptyDataMessage'; import {STORAGE_GROUPS_COLUMNS_WIDTH_LS_KEY} from './columns/getStorageGroupsColumns'; import {useGetStorageGroupsColumns} from './columns/hooks'; -import {getStorageGroups} from './getGroups'; +import {useGroupsGetter} from './getGroups'; import i18n from './i18n'; interface PaginatedStorageGroupsProps { @@ -36,6 +41,11 @@ export const PaginatedStorageGroups = ({ }: PaginatedStorageGroupsProps) => { const columns = useGetStorageGroupsColumns(visibleEntities); + const capabilitiesLoaded = useCapabilitiesLoaded(); + const groupsHandlerAvailable = useStorageGroupsHandlerAvailable(); + + const fetchData = useGroupsGetter(groupsHandlerAvailable); + const tableFilters = React.useMemo(() => { return {searchValue, visibleEntities, database, nodeId}; }, [searchValue, visibleEntities, database, nodeId]); @@ -54,17 +64,19 @@ export const PaginatedStorageGroups = ({ }; return ( - + + + ); }; diff --git a/src/containers/Storage/StorageGroups/getGroups.ts b/src/containers/Storage/StorageGroups/getGroups.ts index e218727484..381d14b3a6 100644 --- a/src/containers/Storage/StorageGroups/getGroups.ts +++ b/src/containers/Storage/StorageGroups/getGroups.ts @@ -1,9 +1,11 @@ +import React from 'react'; + import type {FetchData} from '../../../components/PaginatedTable'; +import {requestStorageData} from '../../../store/reducers/storage/requestStorageData'; import type { PreparedStorageGroup, PreparedStorageGroupFilters, } from '../../../store/reducers/storage/types'; -import {prepareStorageResponse} from '../../../store/reducers/storage/utils'; import type {StorageV2Sort} from '../../../types/api/storage'; import {prepareSortValue} from '../../../utils/filters'; @@ -11,34 +13,39 @@ const getConcurrentId = (limit?: number, offset?: number) => { return `getStorageGroups|offset${offset}|limit${limit}`; }; -export const getStorageGroups: FetchData< - PreparedStorageGroup, - PreparedStorageGroupFilters -> = async (params) => { - const {limit, offset, sortParams, filters} = params; - const {sortOrder, columnId} = sortParams ?? {}; - const {searchValue, visibleEntities, database, nodeId} = filters ?? {}; - - const sort = prepareSortValue(columnId, sortOrder) as StorageV2Sort; - - const response = await window.api.getStorageInfo( - { - version: 'v2', - limit, - offset, - sort, - filter: searchValue, - with: visibleEntities, - database, - nodeId, +type GetStorageGroups = FetchData; + +export function useGroupsGetter(useGroupsHandler: boolean) { + const fetchData: GetStorageGroups = React.useCallback( + async (params) => { + const {limit, offset, sortParams, filters} = params; + const {sortOrder, columnId} = sortParams ?? {}; + const {searchValue, visibleEntities, database, nodeId} = filters ?? {}; + + const sort = prepareSortValue(columnId, sortOrder) as StorageV2Sort; + + const {groups, found, total} = await requestStorageData( + { + limit, + offset, + sort, + filter: searchValue, + with: visibleEntities, + database, + nodeId, + useGroupsHandler, + }, + {concurrentId: getConcurrentId(limit, offset)}, + ); + + return { + data: groups || [], + found: found || 0, + total: total || 0, + }; }, - {concurrentId: getConcurrentId(limit, offset)}, + [useGroupsHandler], ); - const preparedResponse = prepareStorageResponse(response); - return { - data: preparedResponse.groups || [], - found: preparedResponse.found || 0, - total: preparedResponse.total || 0, - }; -}; + return fetchData; +} diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx index 3355ff9496..0147a4f727 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx @@ -1,5 +1,9 @@ import React from 'react'; +import { + useCapabilitiesLoaded, + useStorageGroupsHandlerAvailable, +} from '../../../../../store/reducers/capabilities/hooks'; import {storageApi} from '../../../../../store/reducers/storage/storage'; import {TENANT_DIAGNOSTICS_TABS_IDS} from '../../../../../store/reducers/tenant/constants'; import {TENANT_OVERVIEW_TABLES_LIMIT} from '../../../../../utils/constants'; @@ -22,6 +26,8 @@ interface TopGroupsProps { export function TopGroups({tenant}: TopGroupsProps) { const query = useSearchQuery(); + const capabilitiesLoaded = useCapabilitiesLoaded(); + const groupsHandlerAvailable = useStorageGroupsHandlerAvailable(); const [autoRefreshInterval] = useAutoRefreshInterval(); const columns = getStorageTopGroupsColumns(); @@ -32,8 +38,12 @@ export function TopGroups({tenant}: TopGroupsProps) { sort: '-Usage', with: 'all', limit: TENANT_OVERVIEW_TABLES_LIMIT, + useGroupsHandler: groupsHandlerAvailable, + }, + { + pollingInterval: autoRefreshInterval, + skip: !capabilitiesLoaded, }, - {pollingInterval: autoRefreshInterval}, ); const loading = isFetching && currentData === undefined; @@ -57,7 +67,7 @@ export function TopGroups({tenant}: TopGroupsProps) { data={preparedGroups} columns={columns} title={title} - loading={loading} + loading={loading || !capabilitiesLoaded} error={error} /> ); diff --git a/src/containers/VDiskPage/VDiskPage.tsx b/src/containers/VDiskPage/VDiskPage.tsx index 77cd655f3d..ebbbb383ab 100644 --- a/src/containers/VDiskPage/VDiskPage.tsx +++ b/src/containers/VDiskPage/VDiskPage.tsx @@ -15,6 +15,10 @@ import {ResizeableDataTable} from '../../components/ResizeableDataTable/Resizeab import {VDiskInfo} from '../../components/VDiskInfo/VDiskInfo'; import {api} from '../../store/reducers/api'; import {selectIsUserAllowedToMakeChanges} from '../../store/reducers/authentication/authentication'; +import { + useCapabilitiesLoaded, + useStorageGroupsHandlerAvailable, +} from '../../store/reducers/capabilities/hooks'; import {setHeaderBreadcrumbs} from '../../store/reducers/header/header'; import {storageApi} from '../../store/reducers/storage/storage'; import {vDiskApi} from '../../store/reducers/vdisk/vdisk'; @@ -180,12 +184,7 @@ export function VDiskPage() { const renderGroupInfo = () => { if (valueIsDefined(GroupID)) { - return ( - -
{vDiskPageKeyset('group')}
- -
- ); + return ; } return null; @@ -217,11 +216,16 @@ export function VDiskPage() { } export function VDiskGroup({groupId}: {groupId: string | number}) { + const capabilitiesLoaded = useCapabilitiesLoaded(); + const groupsHandlerAvailable = useStorageGroupsHandlerAvailable(); const [autoRefreshInterval] = useAutoRefreshInterval(); const {currentData} = storageApi.useGetStorageGroupsInfoQuery( - {groupId}, - {pollingInterval: autoRefreshInterval}, + {groupId, useGroupsHandler: groupsHandlerAvailable}, + { + pollingInterval: autoRefreshInterval, + skip: !capabilitiesLoaded, + }, ); const preparedGroups = React.useMemo(() => { @@ -237,11 +241,14 @@ export function VDiskGroup({groupId}: {groupId: string | number}) { } return ( - + +
{vDiskPageKeyset('group')}
+ +
); } diff --git a/src/services/api.ts b/src/services/api.ts index 739db97e8a..98b9d4b832 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -71,7 +71,7 @@ const TRACE_CHECK_TIMEOUT = 2 * SECOND_IN_MS; const TRACE_API_ERROR_TIMEOUT = 10 * SECOND_IN_MS; const MAX_TRACE_CHECK_RETRIES = 30; -type AxiosOptions = { +export type AxiosOptions = { concurrentId?: string; signal?: AbortSignal; withRetries?: boolean; diff --git a/src/store/reducers/capabilities/hooks.ts b/src/store/reducers/capabilities/hooks.ts index 4abb49f436..f03eafb3ed 100644 --- a/src/store/reducers/capabilities/hooks.ts +++ b/src/store/reducers/capabilities/hooks.ts @@ -1,7 +1,13 @@ import type {Capability} from '../../../types/api/capabilities'; import {useTypedSelector} from '../../../utils/hooks'; -import {selectCapabilityVersion} from './capabilities'; +import {capabilitiesApi, selectCapabilityVersion} from './capabilities'; + +export function useCapabilitiesLoaded() { + const {data, error} = capabilitiesApi.useGetClusterCapabilitiesQuery(undefined); + + return Boolean(data || error); +} const useGetFeatureVersion = (feature: Capability) => { return useTypedSelector((state) => selectCapabilityVersion(state, feature) || 0); @@ -20,3 +26,7 @@ export const useDiskPagesAvailable = () => { export const useTracingLevelOptionAvailable = () => { return useGetFeatureVersion('/viewer/query') > 2; }; + +export const useStorageGroupsHandlerAvailable = () => { + return useGetFeatureVersion('/storage/groups') > 2; +}; diff --git a/src/store/reducers/storage/requestStorageData.ts b/src/store/reducers/storage/requestStorageData.ts new file mode 100644 index 0000000000..98108cfbf9 --- /dev/null +++ b/src/store/reducers/storage/requestStorageData.ts @@ -0,0 +1,21 @@ +import type {AxiosOptions} from '../../../services/api'; +import type {StorageRequestParams} from '../../../types/api/storage'; + +import {prepareGroupsResponse, prepareStorageResponse} from './utils'; + +export async function requestStorageData( + { + version = 'v2', + useGroupsHandler, + ...params + }: StorageRequestParams & {useGroupsHandler?: boolean}, + options: AxiosOptions, +) { + if (useGroupsHandler && version !== 'v1') { + const result = await window.api.getStorageGroups({...params}, options); + return prepareGroupsResponse(result); + } else { + const result = await window.api.getStorageInfo({version, ...params}, options); + return prepareStorageResponse(result); + } +} diff --git a/src/store/reducers/storage/storage.ts b/src/store/reducers/storage/storage.ts index 348097c7b0..ed44b11ac3 100644 --- a/src/store/reducers/storage/storage.ts +++ b/src/store/reducers/storage/storage.ts @@ -2,7 +2,8 @@ import type {StorageRequestParams} from '../../../types/api/storage'; import {api} from '../api'; import type {NodesApiRequestParams} from '../nodes/types'; -import {prepareStorageNodesResponse, prepareStorageResponse} from './utils'; +import {requestStorageData} from './requestStorageData'; +import {prepareStorageNodesResponse} from './utils'; export const storageApi = api.injectEndpoints({ endpoints: (builder) => ({ @@ -21,13 +22,13 @@ export const storageApi = api.injectEndpoints({ providesTags: ['All', 'StorageData'], }), getStorageGroupsInfo: builder.query({ - queryFn: async (params: StorageRequestParams, {signal}) => { + queryFn: async ( + params: StorageRequestParams & {useGroupsHandler?: boolean}, + {signal}, + ) => { try { - const result = await window.api.getStorageInfo( - {version: 'v2', ...params}, - {signal}, - ); - return {data: prepareStorageResponse(result)}; + const result = await requestStorageData(params, {signal}); + return {data: result}; } catch (error) { return {error}; } From cd5792b621b4d12296904a47adb11f768d79dd48 Mon Sep 17 00:00:00 2001 From: mufazalov Date: Wed, 11 Sep 2024 11:48:26 +0300 Subject: [PATCH 2/2] fix: review --- src/containers/PDiskPage/PDiskGroups/PDiskGroups.tsx | 2 +- src/containers/Storage/Storage.tsx | 2 +- src/containers/Storage/StorageGroups/getGroups.ts | 6 +++--- .../Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx | 2 +- src/containers/VDiskPage/VDiskPage.tsx | 2 +- src/store/reducers/storage/requestStorageData.ts | 6 +++--- src/store/reducers/storage/storage.ts | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/containers/PDiskPage/PDiskGroups/PDiskGroups.tsx b/src/containers/PDiskPage/PDiskGroups/PDiskGroups.tsx index 694d553644..3b38baa38b 100644 --- a/src/containers/PDiskPage/PDiskGroups/PDiskGroups.tsx +++ b/src/containers/PDiskPage/PDiskGroups/PDiskGroups.tsx @@ -25,7 +25,7 @@ export function PDiskGroups({pDiskId, nodeId}: PDiskGroupsProps) { const [autoRefreshInterval] = useAutoRefreshInterval(); const {currentData, isFetching} = storageApi.useGetStorageGroupsInfoQuery( - {pDiskId, nodeId, useGroupsHandler: groupsHandlerAvailable}, + {pDiskId, nodeId, shouldUseGroupsHandler: groupsHandlerAvailable}, { pollingInterval: autoRefreshInterval, skip: !capabilitiesLoaded, diff --git a/src/containers/Storage/Storage.tsx b/src/containers/Storage/Storage.tsx index 63d0195841..1c68070cbc 100644 --- a/src/containers/Storage/Storage.tsx +++ b/src/containers/Storage/Storage.tsx @@ -102,7 +102,7 @@ export const Storage = ({additionalNodesProps, database, nodeId}: StorageProps) }, ); const groupsQuery = storageApi.useGetStorageGroupsInfoQuery( - {database, with: visibleEntities, nodeId, useGroupsHandler: groupsHandlerAvailable}, + {database, with: visibleEntities, nodeId, shouldUseGroupsHandler: groupsHandlerAvailable}, { skip: storageType !== STORAGE_TYPES.groups || !capabilitiesLoaded, pollingInterval: autoRefreshInterval, diff --git a/src/containers/Storage/StorageGroups/getGroups.ts b/src/containers/Storage/StorageGroups/getGroups.ts index 381d14b3a6..27f7dd1ece 100644 --- a/src/containers/Storage/StorageGroups/getGroups.ts +++ b/src/containers/Storage/StorageGroups/getGroups.ts @@ -15,7 +15,7 @@ const getConcurrentId = (limit?: number, offset?: number) => { type GetStorageGroups = FetchData; -export function useGroupsGetter(useGroupsHandler: boolean) { +export function useGroupsGetter(shouldUseGroupsHandler: boolean) { const fetchData: GetStorageGroups = React.useCallback( async (params) => { const {limit, offset, sortParams, filters} = params; @@ -33,7 +33,7 @@ export function useGroupsGetter(useGroupsHandler: boolean) { with: visibleEntities, database, nodeId, - useGroupsHandler, + shouldUseGroupsHandler, }, {concurrentId: getConcurrentId(limit, offset)}, ); @@ -44,7 +44,7 @@ export function useGroupsGetter(useGroupsHandler: boolean) { total: total || 0, }; }, - [useGroupsHandler], + [shouldUseGroupsHandler], ); return fetchData; diff --git a/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx b/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx index 0147a4f727..0aba40059e 100644 --- a/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx +++ b/src/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/TopGroups.tsx @@ -38,7 +38,7 @@ export function TopGroups({tenant}: TopGroupsProps) { sort: '-Usage', with: 'all', limit: TENANT_OVERVIEW_TABLES_LIMIT, - useGroupsHandler: groupsHandlerAvailable, + shouldUseGroupsHandler: groupsHandlerAvailable, }, { pollingInterval: autoRefreshInterval, diff --git a/src/containers/VDiskPage/VDiskPage.tsx b/src/containers/VDiskPage/VDiskPage.tsx index ebbbb383ab..14610f493a 100644 --- a/src/containers/VDiskPage/VDiskPage.tsx +++ b/src/containers/VDiskPage/VDiskPage.tsx @@ -221,7 +221,7 @@ export function VDiskGroup({groupId}: {groupId: string | number}) { const [autoRefreshInterval] = useAutoRefreshInterval(); const {currentData} = storageApi.useGetStorageGroupsInfoQuery( - {groupId, useGroupsHandler: groupsHandlerAvailable}, + {groupId, shouldUseGroupsHandler: groupsHandlerAvailable}, { pollingInterval: autoRefreshInterval, skip: !capabilitiesLoaded, diff --git a/src/store/reducers/storage/requestStorageData.ts b/src/store/reducers/storage/requestStorageData.ts index 98108cfbf9..3a98663a2a 100644 --- a/src/store/reducers/storage/requestStorageData.ts +++ b/src/store/reducers/storage/requestStorageData.ts @@ -6,12 +6,12 @@ import {prepareGroupsResponse, prepareStorageResponse} from './utils'; export async function requestStorageData( { version = 'v2', - useGroupsHandler, + shouldUseGroupsHandler, ...params - }: StorageRequestParams & {useGroupsHandler?: boolean}, + }: StorageRequestParams & {shouldUseGroupsHandler?: boolean}, options: AxiosOptions, ) { - if (useGroupsHandler && version !== 'v1') { + if (shouldUseGroupsHandler && version !== 'v1') { const result = await window.api.getStorageGroups({...params}, options); return prepareGroupsResponse(result); } else { diff --git a/src/store/reducers/storage/storage.ts b/src/store/reducers/storage/storage.ts index ed44b11ac3..74fe914e38 100644 --- a/src/store/reducers/storage/storage.ts +++ b/src/store/reducers/storage/storage.ts @@ -23,7 +23,7 @@ export const storageApi = api.injectEndpoints({ }), getStorageGroupsInfo: builder.query({ queryFn: async ( - params: StorageRequestParams & {useGroupsHandler?: boolean}, + params: StorageRequestParams & {shouldUseGroupsHandler?: boolean}, {signal}, ) => { try {