diff --git a/src/containers/Header/breadcrumbs.tsx b/src/containers/Header/breadcrumbs.tsx index a5730d2c41..d347dbab04 100644 --- a/src/containers/Header/breadcrumbs.tsx +++ b/src/containers/Header/breadcrumbs.tsx @@ -4,6 +4,7 @@ import { Database as DatabaseIcon, HardDrive as StorageNodeIcon, } from '@gravity-ui/icons'; +import {isNil} from 'lodash'; import {TabletIcon} from '../../components/TabletIcon/TabletIcon'; import routes, {getPDiskPagePath, getStorageGroupPath} from '../../routes'; @@ -172,13 +173,13 @@ const getStorageGroupBreadcrumbs: GetBreadcrumbs : getClusterBreadcrumbs(options, query); let text = headerKeyset('breadcrumbs.storageGroup'); - if (groupId) { + if (!isNil(groupId)) { text += ` ${groupId}`; } const lastItem = { text, - link: groupId ? getStorageGroupPath(groupId, {database}) : undefined, + link: isNil(groupId) ? undefined : getStorageGroupPath(groupId, {database}), }; breadcrumbs.push(lastItem); diff --git a/src/containers/VDiskPage/VDiskPage.tsx b/src/containers/VDiskPage/VDiskPage.tsx index 414b3501e0..77a1285e1a 100644 --- a/src/containers/VDiskPage/VDiskPage.tsx +++ b/src/containers/VDiskPage/VDiskPage.tsx @@ -65,14 +65,12 @@ export function VDiskPage() { const isUserAllowedToMakeChanges = useIsUserAllowedToMakeChanges(); const newDiskApiAvailable = useDiskPagesAvailable(); - const [{nodeId, pDiskId, vDiskId: vDiskIdParam, activeTab, database: databaseParam}] = - useQueryParams({ - nodeId: StringParam, - pDiskId: StringParam, - vDiskId: StringParam, - activeTab: StringParam, - database: StringParam, - }); + const [{nodeId, vDiskId: vDiskIdParam, activeTab, database: databaseParam}] = useQueryParams({ + nodeId: StringParam, + vDiskId: StringParam, + activeTab: StringParam, + database: StringParam, + }); const database = databaseParam ?? undefined; const vDiskTab = vDiskTabSchema.parse(activeTab); @@ -107,8 +105,17 @@ export function VDiskPage() { }, [dispatch, database, vDiskData?.VDiskId?.GroupID, vDiskData?.StringifiedId]); const loading = isFetching && vDiskData === undefined; - const {NodeHost, NodeId, NodeType, NodeDC, PDiskId, PDiskType, Severity, VDiskId} = - vDiskData || {}; + const { + NodeHost, + NodeId, + NodeType, + NodeDC, + PDiskId, + PDiskType, + Severity, + VDiskId, + StringifiedId, + } = vDiskData || {}; const {GroupID, GroupGeneration, Ring, Domain, VDisk} = VDiskId || (!loading && getVDiskIdFromString(vDiskIdParam)) || {}; @@ -169,8 +176,8 @@ export function VDiskPage() { ? `${vDiskPageKeyset('vdisk')} ${vDiskSlotId}` : vDiskPageKeyset('vdisk'); - const pDiskPagePart = pDiskId - ? `${vDiskPageKeyset('pdisk')} ${pDiskId}` + const pDiskPagePart = PDiskId + ? `${vDiskPageKeyset('pdisk')} ${PDiskId}` : vDiskPageKeyset('pdisk'); const nodePagePart = NodeHost ? NodeHost : vDiskPageKeyset('node'); @@ -275,8 +282,9 @@ export function VDiskPage() { ); @@ -293,12 +301,12 @@ export function VDiskPage() { database={database} groupId={GroupID} nodeId={nodeId ?? undefined} - pDiskId={pDiskId ?? undefined} + pDiskId={PDiskId} scrollContainerRef={containerRef} viewContext={{ groupId: GroupID?.toString(), nodeId: nodeId?.toString(), - pDiskId: pDiskId?.toString(), + pDiskId: PDiskId?.toString(), vDiskSlotId: vDiskSlotId?.toString(), }} /> diff --git a/src/containers/VDiskPage/VDiskTablets/VDiskTablets.tsx b/src/containers/VDiskPage/VDiskTablets/VDiskTablets.tsx index 5375a7578b..e64e93d1fc 100644 --- a/src/containers/VDiskPage/VDiskTablets/VDiskTablets.tsx +++ b/src/containers/VDiskPage/VDiskTablets/VDiskTablets.tsx @@ -7,6 +7,7 @@ import {isNil} from 'lodash'; import {PageError} from '../../../components/Errors/PageError/PageError'; import {ResizeableDataTable} from '../../../components/ResizeableDataTable/ResizeableDataTable'; import {TableWithControlsLayout} from '../../../components/TableWithControlsLayout/TableWithControlsLayout'; +import {useBlobIndexStatWithVdiskId} from '../../../store/reducers/capabilities/hooks'; import {vDiskApi} from '../../../store/reducers/vdisk/vdisk'; import type {VDiskBlobIndexItem} from '../../../types/api/vdiskBlobIndex'; import {DEFAULT_TABLE_SETTINGS} from '../../../utils/constants'; @@ -22,6 +23,7 @@ const columns = getColumns(); interface VDiskTabletsProps { nodeId?: string | number; + vDiskId?: string; pDiskId?: string | number; vDiskSlotId?: string | number; className?: string; @@ -32,19 +34,26 @@ export function VDiskTablets({ nodeId, pDiskId, vDiskSlotId, + vDiskId, className, scrollContainerRef, }: VDiskTabletsProps) { const [autoRefreshInterval] = useAutoRefreshInterval(); + const useVdiskId = useBlobIndexStatWithVdiskId(); - const {currentData, isFetching, error} = vDiskApi.useGetVDiskBlobIndexStatQuery( - !isNil(nodeId) && !isNil(pDiskId) && !isNil(vDiskSlotId) - ? {nodeId, pDiskId, vDiskSlotId} - : skipToken, - { - pollingInterval: autoRefreshInterval, - }, - ); + const params = React.useMemo(() => { + if (useVdiskId && !isNil(vDiskId)) { + return {vDiskId}; + } + if (!isNil(nodeId) && !isNil(vDiskSlotId) && !isNil(pDiskId)) { + return {nodeId, pDiskId, vDiskSlotId}; + } + return skipToken; + }, [nodeId, pDiskId, vDiskSlotId, vDiskId, useVdiskId]); + + const {currentData, isFetching, error} = vDiskApi.useGetVDiskBlobIndexStatQuery(params, { + pollingInterval: autoRefreshInterval, + }); const loading = isFetching && currentData === undefined; diff --git a/src/services/api/viewer.ts b/src/services/api/viewer.ts index a2196d5529..2c22773020 100644 --- a/src/services/api/viewer.ts +++ b/src/services/api/viewer.ts @@ -1,4 +1,5 @@ import type {PlanToSvgQueryParams} from '../../store/reducers/planToSvg'; +import type {VDiskBlobIndexStatParams} from '../../store/reducers/vdisk/vdisk'; import type { AccessRightsUpdateRequest, AvailablePermissionsResponse, @@ -548,25 +549,21 @@ export class ViewerAPI extends BaseYdbAPI { } getVDiskBlobIndexStat( - { - vDiskSlotId, - pDiskId, - nodeId, - database, - }: { - vDiskSlotId: string | number; - pDiskId: string | number; - nodeId: string | number; - database?: string; - }, + {database, ...rest}: VDiskBlobIndexStatParams, {concurrentId, signal}: AxiosOptions = {}, ) { + const params = + 'vDiskId' in rest + ? {vdisk_id: rest.vDiskId} + : { + node_id: rest.nodeId, + pdisk_id: rest.pDiskId, + vslot_id: rest.vDiskSlotId, + }; return this.get( this.getPath('/vdisk/blobindexstat'), { - node_id: nodeId, - pdisk_id: pDiskId, - vslot_id: vDiskSlotId, + ...params, database, }, {concurrentId, requestConfig: {signal}}, diff --git a/src/store/reducers/capabilities/hooks.ts b/src/store/reducers/capabilities/hooks.ts index 54d5658441..12a3cddc2c 100644 --- a/src/store/reducers/capabilities/hooks.ts +++ b/src/store/reducers/capabilities/hooks.ts @@ -54,6 +54,10 @@ export const useStorageGroupsHandlerAvailable = () => { return useGetFeatureVersion('/storage/groups') > 2; }; +export const useBlobIndexStatWithVdiskId = () => { + return useGetFeatureVersion('/vdisk/blobindexstat') > 1; +}; + export const useStorageGroupsHandlerHasGrouping = () => { return useGetFeatureVersion('/storage/groups') > 4; }; diff --git a/src/store/reducers/vdisk/vdisk.ts b/src/store/reducers/vdisk/vdisk.ts index 7a39b8335f..8f6efd1695 100644 --- a/src/store/reducers/vdisk/vdisk.ts +++ b/src/store/reducers/vdisk/vdisk.ts @@ -1,6 +1,6 @@ import type {StorageGroupsResponse} from '../../../types/api/storage'; import type {TEvSystemStateResponse} from '../../../types/api/systemState'; -import {getVDiskSlotBasedId} from '../../../utils/disks/helpers'; +import {getVDiskId} from '../../../utils/disks/helpers'; import {api} from '../api'; import {prepareVDiskDataResponse} from './utils'; @@ -11,6 +11,14 @@ type VDiskDataRequestParams = { database?: string; }; +type VDiskBlobIndexStatBasicParams = + | {nodeId: string | number; pDiskId: string | number; vDiskSlotId: string | number} + | {vDiskId: string}; + +export type VDiskBlobIndexStatParams = VDiskBlobIndexStatBasicParams & { + database?: string; +}; + export const vDiskApi = api.injectEndpoints({ endpoints: (build) => ({ getVDiskData: build.query({ @@ -52,25 +60,11 @@ export const vDiskApi = api.injectEndpoints({ ], }), getVDiskBlobIndexStat: build.query({ - queryFn: async ( - { - nodeId, - pDiskId, - vDiskSlotId, - database, - }: { - nodeId: string | number; - pDiskId: string | number; - vDiskSlotId: string | number; - database?: string; - }, - {signal}, - ) => { + queryFn: async (params: VDiskBlobIndexStatParams, {signal}) => { try { - const response = await window.api.viewer.getVDiskBlobIndexStat( - {nodeId, pDiskId, vDiskSlotId, database}, - {signal}, - ); + const response = await window.api.viewer.getVDiskBlobIndexStat(params, { + signal, + }); return {data: response}; } catch (error) { return {error}; @@ -80,7 +74,7 @@ export const vDiskApi = api.injectEndpoints({ 'All', { type: 'VDiskBlobIndexStat', - id: getVDiskSlotBasedId(arg.nodeId, arg.pDiskId, arg.vDiskSlotId), + id: getVDiskId(arg), }, ], }), diff --git a/src/types/api/capabilities.ts b/src/types/api/capabilities.ts index d28c1453ed..1bef50c9a2 100644 --- a/src/types/api/capabilities.ts +++ b/src/types/api/capabilities.ts @@ -23,6 +23,7 @@ export type Capability = | '/viewer/feature_flags' | '/viewer/config' | '/viewer/cluster' + | '/vdisk/blobindexstat' | '/viewer/nodes' | '/viewer/acl' | '/viewer/topic_data'; diff --git a/src/utils/disks/helpers.ts b/src/utils/disks/helpers.ts index 047a721d99..da39644c45 100644 --- a/src/utils/disks/helpers.ts +++ b/src/utils/disks/helpers.ts @@ -1,4 +1,5 @@ import {valueIsDefined} from '..'; +import type {VDiskBlobIndexStatParams} from '../../store/reducers/vdisk/vdisk'; import {EFlag} from '../../types/api/enums'; import type {TVDiskStateInfo, TVSlotId} from '../../types/api/vdisk'; import {generateEvaluator} from '../generateEvaluator'; @@ -47,10 +48,10 @@ export function getPDiskId({ return undefined; } -export function getVDiskSlotBasedId( - nodeId: string | number, - pDiskId: string | number, - vDiskSlotId: string | number, -) { - return [nodeId, pDiskId, vDiskSlotId].join('-'); +export function getVDiskId(params: VDiskBlobIndexStatParams) { + const parts = + 'vDiskId' in params + ? [params.vDiskId] + : [params.nodeId, params.pDiskId, params.vDiskSlotId]; + return parts.join('-'); }