From a3d0c74e9341e52b03f46767e72493aec7d4968c Mon Sep 17 00:00:00 2001 From: mufazalov Date: Tue, 11 Feb 2025 14:22:23 +0300 Subject: [PATCH 1/2] fix(Storage): fix disks view for degraded group --- src/containers/Storage/Disks/Disks.tsx | 16 ++++---- .../Storage/StorageGroups/columns/columns.tsx | 12 +++++- src/containers/Storage/VDisks/VDisks.tsx | 6 ++- src/containers/Storage/utils/index.ts | 14 ++++++- .../__tests__/prepareGroupsDisks.test.ts | 41 ++++++++++++++++++- .../reducers/storage/prepareGroupsDisks.ts | 4 +- src/store/reducers/storage/types.ts | 4 +- src/types/api/cluster.ts | 3 +- src/types/api/storage.ts | 6 ++- 9 files changed, 83 insertions(+), 23 deletions(-) diff --git a/src/containers/Storage/Disks/Disks.tsx b/src/containers/Storage/Disks/Disks.tsx index 39b515cc70..15a3f3579f 100644 --- a/src/containers/Storage/Disks/Disks.tsx +++ b/src/containers/Storage/Disks/Disks.tsx @@ -3,9 +3,10 @@ import React from 'react'; import {Flex, useLayoutContext} from '@gravity-ui/uikit'; import {VDisk} from '../../../components/VDisk/VDisk'; -import {valueIsDefined} from '../../../utils'; +import type {Erasure} from '../../../types/api/storage'; import {cn} from '../../../utils/cn'; import type {PreparedVDisk} from '../../../utils/disks/types'; +import {isNumeric} from '../../../utils/utils'; import {PDisk} from '../PDisk'; import type {StorageViewContext} from '../types'; import {isVdiskActive, useVDisksWithDCMargins} from '../utils'; @@ -19,12 +20,13 @@ const VDISKS_CONTAINER_WIDTH = 300; interface DisksProps { vDisks?: PreparedVDisk[]; viewContext?: StorageViewContext; + erasure?: Erasure; } -export function Disks({vDisks = [], viewContext}: DisksProps) { +export function Disks({vDisks = [], viewContext, erasure}: DisksProps) { const [highlightedVDisk, setHighlightedVDisk] = React.useState(); - const vDisksWithDCMargins = useVDisksWithDCMargins(vDisks); + const vDisksWithDCMargins = useVDisksWithDCMargins(vDisks, erasure); const { theme: {spaceBaseSize}, @@ -40,9 +42,9 @@ export function Disks({vDisks = [], viewContext}: DisksProps) { return (
- {vDisks?.map((vDisk) => ( + {vDisks?.map((vDisk, index) => ( {vDisks?.map((vDisk, index) => ( ( name: STORAGE_GROUPS_COLUMNS_IDS.VDisks, header: STORAGE_GROUPS_COLUMNS_TITLES.VDisks, className: b('vdisks-column'), - render: ({row}) => , + render: ({row}) => ( + + ), align: DataTable.CENTER, width: 780, // usually 8-9 vdisks, this width corresponds to 8 vdisks, column is expanded if more resizeable: false, @@ -248,7 +250,13 @@ const getDisksColumn = (data?: GetStorageColumnsData): StorageGroupsColumn => ({ header: STORAGE_GROUPS_COLUMNS_TITLES.VDisksPDisks, className: b('disks-column'), render: ({row}) => { - return ; + return ( + + ); }, align: DataTable.CENTER, width: 900, diff --git a/src/containers/Storage/VDisks/VDisks.tsx b/src/containers/Storage/VDisks/VDisks.tsx index 221613ff73..71cac27ed2 100644 --- a/src/containers/Storage/VDisks/VDisks.tsx +++ b/src/containers/Storage/VDisks/VDisks.tsx @@ -1,4 +1,5 @@ import {VDiskWithDonorsStack} from '../../../components/VDisk/VDiskWithDonorsStack'; +import type {Erasure} from '../../../types/api/storage'; import {cn} from '../../../utils/cn'; import type {PreparedVDisk} from '../../../utils/disks/types'; import type {StorageViewContext} from '../types'; @@ -11,10 +12,11 @@ const b = cn('ydb-storage-vdisks'); interface VDisksProps { vDisks?: PreparedVDisk[]; viewContext?: StorageViewContext; + erasure?: Erasure; } -export function VDisks({vDisks, viewContext}: VDisksProps) { - const vDisksWithDCMargins = useVDisksWithDCMargins(vDisks); +export function VDisks({vDisks, viewContext, erasure}: VDisksProps) { + const vDisksWithDCMargins = useVDisksWithDCMargins(vDisks, erasure); return (
diff --git a/src/containers/Storage/utils/index.ts b/src/containers/Storage/utils/index.ts index 8fdf274288..9d29d97d39 100644 --- a/src/containers/Storage/utils/index.ts +++ b/src/containers/Storage/utils/index.ts @@ -2,6 +2,7 @@ import React from 'react'; import {selectNodesMap} from '../../../store/reducers/nodesList'; import type {PreparedStorageGroup} from '../../../store/reducers/storage/types'; +import type {Erasure} from '../../../types/api/storage'; import {valueIsDefined} from '../../../utils'; import type {PreparedVDisk} from '../../../utils/disks/types'; import {generateEvaluator} from '../../../utils/generateEvaluator'; @@ -84,12 +85,21 @@ export function getStorageGroupsInitialEntitiesCount( return DEFAULT_ENTITIES_COUNT; } -export function useVDisksWithDCMargins(vDisks: PreparedVDisk[] = []) { +function isErasureWithDifferentDC(erasure?: Erasure) { + return erasure === 'mirror-3-dc' || erasure === 'mirror-3of4'; +} + +export function useVDisksWithDCMargins(vDisks: PreparedVDisk[] = [], erasure?: Erasure) { const nodesMap = useTypedSelector(selectNodesMap); return React.useMemo(() => { const disksWithMargins: number[] = []; + // If single-dc erasure, all disks are in the same DC + if (!isErasureWithDifferentDC(erasure)) { + return disksWithMargins; + } + // Backend returns disks sorted by DC, so we don't need to apply any additional sorting vDisks.forEach((disk, index) => { const dc1 = nodesMap?.get(Number(disk?.NodeId))?.DC; @@ -101,5 +111,5 @@ export function useVDisksWithDCMargins(vDisks: PreparedVDisk[] = []) { }); return disksWithMargins; - }, [vDisks, nodesMap]); + }, [erasure, vDisks, nodesMap]); } diff --git a/src/store/reducers/storage/__tests__/prepareGroupsDisks.test.ts b/src/store/reducers/storage/__tests__/prepareGroupsDisks.test.ts index b957a0f853..8ba4543559 100644 --- a/src/store/reducers/storage/__tests__/prepareGroupsDisks.test.ts +++ b/src/store/reducers/storage/__tests__/prepareGroupsDisks.test.ts @@ -95,7 +95,19 @@ describe('prepareGroupsVDisk', () => { AllocatedPercent: 12, Donors: undefined, - PDisk: undefined, + + PDisk: { + AllocatedPercent: NaN, + AllocatedSize: NaN, + AvailableSize: NaN, + NodeId: 224, + PDiskId: undefined, + Severity: 0, + SlotSize: undefined, + StringifiedId: undefined, + TotalSize: NaN, + Type: undefined, + }, }; const preparedData = prepareGroupsVDisk(vDiksDataWithoutPDisk); @@ -124,6 +136,19 @@ describe('prepareGroupsVDisk', () => { AvailableSize: 234461593600, TotalSize: 265405071360, AllocatedPercent: 12, + + PDisk: { + AllocatedPercent: NaN, + AllocatedSize: NaN, + AvailableSize: NaN, + NodeId: 224, + PDiskId: undefined, + Severity: 0, + SlotSize: undefined, + StringifiedId: undefined, + TotalSize: NaN, + Type: undefined, + }, }; const preparedData = prepareGroupsVDisk(vDiksDataWithoutPDisk); @@ -215,7 +240,19 @@ describe('prepareGroupsVDisk', () => { AllocatedPercent: 12, Donors: undefined, - PDisk: undefined, + + PDisk: { + AllocatedPercent: NaN, + AllocatedSize: NaN, + AvailableSize: NaN, + NodeId: undefined, + PDiskId: undefined, + Severity: 0, + SlotSize: undefined, + StringifiedId: undefined, + TotalSize: NaN, + Type: undefined, + }, }; const preparedData = prepareGroupsVDisk(vDiksDataWithoutPDisk); diff --git a/src/store/reducers/storage/prepareGroupsDisks.ts b/src/store/reducers/storage/prepareGroupsDisks.ts index 1494f1723d..4b92ce967c 100644 --- a/src/store/reducers/storage/prepareGroupsDisks.ts +++ b/src/store/reducers/storage/prepareGroupsDisks.ts @@ -16,9 +16,7 @@ export function prepareGroupsVDisk(data: TStorageVDisk = {}): PreparedVDisk { VDiskId: whiteboardVDisk.VDiskId, }; - const preparedPDisk = PDisk - ? prepareGroupsPDisk({...PDisk, NodeId: mergedVDiskData.NodeId}) - : undefined; + const preparedPDisk = prepareGroupsPDisk({...PDisk, NodeId: mergedVDiskData.NodeId}); const PDiskId = preparedPDisk?.PDiskId ?? whiteboardVDisk?.PDiskId; diff --git a/src/store/reducers/storage/types.ts b/src/store/reducers/storage/types.ts index b2c750fd96..28522207cf 100644 --- a/src/store/reducers/storage/types.ts +++ b/src/store/reducers/storage/types.ts @@ -2,7 +2,7 @@ import {z} from 'zod'; import type {EFlag} from '../../../types/api/enums'; import type {NodesGroupByField} from '../../../types/api/nodes'; -import type {GroupsGroupByField} from '../../../types/api/storage'; +import type {Erasure, GroupsGroupByField} from '../../../types/api/storage'; import type {PreparedPDisk, PreparedVDisk} from '../../../utils/disks/types'; import type {NodesUptimeFilterValues, PreparedNodeSystemState} from '../../../utils/nodes'; @@ -56,7 +56,7 @@ export interface PreparedStorageGroup { PoolName?: string; MediaType?: string; Encryption?: boolean; - ErasureSpecies?: string; + ErasureSpecies?: Erasure; Degraded: number; Overall?: EFlag; diff --git a/src/types/api/cluster.ts b/src/types/api/cluster.ts index a3102d01c4..3f0b10ee78 100644 --- a/src/types/api/cluster.ts +++ b/src/types/api/cluster.ts @@ -1,4 +1,5 @@ import type {EFlag} from './enums'; +import type {Erasure} from './storage'; import type {TTabletStateInfo} from './tablet'; /** @@ -38,7 +39,7 @@ export interface TClusterInfoV1 { export interface TStorageStats { PDiskFilter?: string; - ErasureSpecies?: string; + ErasureSpecies?: Erasure; CurrentAvailableSize?: string; /** uint64 */ CurrentAllocatedSize?: string; diff --git a/src/types/api/storage.ts b/src/types/api/storage.ts index 0fb87d8573..c8e418d76d 100644 --- a/src/types/api/storage.ts +++ b/src/types/api/storage.ts @@ -66,7 +66,7 @@ export type TStorageGroupInfo = TBSGroupStateInfo & interface TBSGroupStateInfo { GroupID?: number; - ErasureSpecies?: string; + ErasureSpecies?: Erasure; VDisks?: TVDiskStateInfo[]; /** uint64 */ ChangeTime?: string; @@ -137,7 +137,7 @@ export interface TGroupsStorageGroupInfo { DiskSpace?: EFlag; Kind?: string; MediaType?: string; - ErasureSpecies?: string; + ErasureSpecies?: Erasure; /** uint64 */ AllocationUnits?: string; /** @@ -228,6 +228,8 @@ export interface TStoragePDisk { Whiteboard?: TPDiskStateInfo; } +export type Erasure = 'none' | 'block-4-2' | 'mirror-3-dc' | 'mirror-3of4'; + // ==== Request types ==== export type EVersion = 'v1' | 'v2'; // only v2 versions works with sorting From cc4a5c96ebcde6d4579fade6096f1e65bdf8c953 Mon Sep 17 00:00:00 2001 From: mufazalov Date: Tue, 11 Feb 2025 15:11:18 +0300 Subject: [PATCH 2/2] fix: add comments --- src/containers/Storage/utils/index.ts | 1 + src/types/api/storage.ts | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/containers/Storage/utils/index.ts b/src/containers/Storage/utils/index.ts index 9d29d97d39..331e86475c 100644 --- a/src/containers/Storage/utils/index.ts +++ b/src/containers/Storage/utils/index.ts @@ -86,6 +86,7 @@ export function getStorageGroupsInitialEntitiesCount( } function isErasureWithDifferentDC(erasure?: Erasure) { + // These erasure types suppose the data distributed across 3 different DC return erasure === 'mirror-3-dc' || erasure === 'mirror-3of4'; } diff --git a/src/types/api/storage.ts b/src/types/api/storage.ts index c8e418d76d..6a23ad6055 100644 --- a/src/types/api/storage.ts +++ b/src/types/api/storage.ts @@ -228,6 +228,9 @@ export interface TStoragePDisk { Whiteboard?: TPDiskStateInfo; } +/** + * https://ydb.tech/docs/en/concepts/topology#cluster-config + */ export type Erasure = 'none' | 'block-4-2' | 'mirror-3-dc' | 'mirror-3of4'; // ==== Request types ====