Skip to content

Commit 0a49720

Browse files
fix(PDiskSpaceDistribution): update slots severity calculation (#1907)
1 parent 9ab321b commit 0a49720

File tree

7 files changed

+263
-25
lines changed

7 files changed

+263
-25
lines changed

src/containers/PDiskPage/PDiskPage.tsx

+5-5
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,15 @@ import './PDiskPage.scss';
3737
const pdiskPageCn = cn('ydb-pdisk-page');
3838

3939
const PDISK_TABS_IDS = {
40-
diskDistribution: 'diskDistribution',
40+
spaceDistribution: 'spaceDistribution',
4141
storage: 'storage',
4242
} as const;
4343

4444
const PDISK_PAGE_TABS = [
4545
{
46-
id: PDISK_TABS_IDS.diskDistribution,
46+
id: PDISK_TABS_IDS.spaceDistribution,
4747
get title() {
48-
return pDiskPageKeyset('disk-distribution');
48+
return pDiskPageKeyset('space-distribution');
4949
},
5050
},
5151
{
@@ -56,7 +56,7 @@ const PDISK_PAGE_TABS = [
5656
},
5757
];
5858

59-
const pDiskTabSchema = z.nativeEnum(PDISK_TABS_IDS).catch(PDISK_TABS_IDS.diskDistribution);
59+
const pDiskTabSchema = z.nativeEnum(PDISK_TABS_IDS).catch(PDISK_TABS_IDS.spaceDistribution);
6060

6161
export function PDiskPage() {
6262
const dispatch = useTypedDispatch();
@@ -237,7 +237,7 @@ export function PDiskPage() {
237237

238238
const renderTabsContent = () => {
239239
switch (pDiskTab) {
240-
case 'diskDistribution': {
240+
case 'spaceDistribution': {
241241
return pDiskData ? (
242242
<div className={pdiskPageCn('disk-distribution')}>
243243
<PDiskSpaceDistribution data={pDiskData} />

src/containers/PDiskPage/i18n/en.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"node": "Node",
55

66
"storage": "Storage",
7-
"disk-distribution": "Disk distribution",
7+
"space-distribution": "Space distribution",
88

99
"empty-slot": "Empty slot",
1010
"log": "Log",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
import type {TPDiskInfoResponse} from '../../../../types/api/pdisk';
2+
import {preparePDiskDataResponse} from '../utils';
3+
4+
describe('preparePDiskDataResponse', () => {
5+
const rawData = {
6+
Whiteboard: {
7+
PDisk: {
8+
PDiskId: 1,
9+
ChangeTime: '1738670321505',
10+
Path: '/ydb_data/pdisk1tmbtrl7c.data',
11+
Guid: '1',
12+
Category: '0',
13+
AvailableSize: '66454552576',
14+
TotalSize: '100000000000',
15+
State: 'Normal',
16+
Device: 'Green',
17+
Realtime: 'Green',
18+
SerialNumber: '',
19+
SystemSize: '2000000000',
20+
LogUsedSize: '2000000000',
21+
LogTotalSize: '60000000000',
22+
EnforcedDynamicSlotSize: '20000000000',
23+
NumActiveSlots: 1,
24+
},
25+
VDisks: [
26+
{
27+
VDiskId: {
28+
GroupID: 2181038081,
29+
GroupGeneration: 1,
30+
Ring: 0,
31+
Domain: 0,
32+
VDisk: 0,
33+
},
34+
ChangeTime: '1738672482849',
35+
PDiskId: 1,
36+
VDiskSlotId: 1001,
37+
Guid: '1',
38+
Kind: '0',
39+
VDiskState: 'OK',
40+
DiskSpace: 'Green',
41+
SatisfactionRank: {
42+
FreshRank: {
43+
Flag: 'Green',
44+
},
45+
LevelRank: {
46+
Flag: 'Green',
47+
},
48+
},
49+
Replicated: true,
50+
ReplicationProgress: 1,
51+
ReplicationSecondsRemaining: 0,
52+
AllocatedSize: '5000000000',
53+
AvailableSize: '20000000000',
54+
HasUnreadableBlobs: false,
55+
IncarnationGuid: '13331041715376219418',
56+
InstanceGuid: '18177015420302975983',
57+
FrontQueues: 'Green',
58+
StoragePoolName: 'dynamic_storage_pool:1',
59+
ReadThroughput: '0',
60+
WriteThroughput: '0',
61+
},
62+
],
63+
},
64+
BSC: {
65+
PDisk: {
66+
Type: 'ROT',
67+
Kind: '0',
68+
Path: '/ydb_data/pdisk1tmbtrl7c.data',
69+
Guid: '1',
70+
BoxId: '1',
71+
SharedWithOs: false,
72+
ReadCentric: false,
73+
AvailableSize: '66454552576',
74+
TotalSize: '68719476736',
75+
StatusV2: 'ACTIVE',
76+
StatusChangeTimestamp: '1737458853219782',
77+
EnforcedDynamicSlotSize: '20000000000',
78+
ExpectedSlotCount: 16,
79+
NumActiveSlots: 1,
80+
Category: '0',
81+
DecommitStatus: 'DECOMMIT_NONE',
82+
State: 'Normal',
83+
},
84+
VDisks: [
85+
{
86+
Key: {
87+
NodeId: 1,
88+
PDiskId: 1,
89+
VSlotId: 0,
90+
},
91+
Info: {
92+
GroupId: 0,
93+
GroupGeneration: 1,
94+
FailRealm: 0,
95+
FailDomain: 0,
96+
VDisk: 0,
97+
AllocatedSize: '1000000000',
98+
AvailableSize: '20000000000',
99+
StatusV2: 'READY',
100+
Kind: 'Default',
101+
DiskSpace: 'Green',
102+
Replicated: true,
103+
State: 'OK',
104+
},
105+
},
106+
],
107+
},
108+
} as unknown as TPDiskInfoResponse;
109+
110+
it('Should correctly retrieve slots', () => {
111+
const preparedData = preparePDiskDataResponse([rawData, {}]);
112+
113+
expect(preparedData.SlotItems?.length).toEqual(17);
114+
expect(preparedData.SlotItems?.filter((slot) => slot.SlotType === 'log').length).toEqual(1);
115+
expect(preparedData.SlotItems?.filter((slot) => slot.SlotType === 'vDisk').length).toEqual(
116+
1,
117+
);
118+
expect(preparedData.SlotItems?.filter((slot) => slot.SlotType === 'empty').length).toEqual(
119+
15,
120+
);
121+
});
122+
it('Should correctly calculate empty slots size if EnforcedDynamicSlotSize is provided', () => {
123+
const preparedData = preparePDiskDataResponse([rawData, {}]);
124+
125+
expect(preparedData.SlotItems?.find((slot) => slot.SlotType === 'empty')?.Total).toEqual(
126+
20_000_000_000,
127+
);
128+
});
129+
it('Should correctly calculate empty slots size if EnforcedDynamicSlotSize is undefined', () => {
130+
const data: TPDiskInfoResponse = {
131+
...rawData,
132+
Whiteboard: {
133+
...rawData.Whiteboard,
134+
PDisk: {
135+
...rawData.Whiteboard?.PDisk,
136+
EnforcedDynamicSlotSize: undefined,
137+
},
138+
},
139+
BSC: {
140+
...rawData.BSC,
141+
PDisk: {
142+
...rawData.BSC?.PDisk,
143+
EnforcedDynamicSlotSize: undefined,
144+
},
145+
},
146+
};
147+
const preparedData = preparePDiskDataResponse([data, {}]);
148+
149+
expect(preparedData.SlotItems?.find((slot) => slot.SlotType === 'empty')?.Total).toEqual(
150+
1_000_000_000,
151+
);
152+
});
153+
it('Should return yellow or red severity for log if its size exceeds thresholds', () => {
154+
const dataWarning: TPDiskInfoResponse = {
155+
...rawData,
156+
Whiteboard: {
157+
...rawData.Whiteboard,
158+
PDisk: {
159+
...rawData.Whiteboard?.PDisk,
160+
LogUsedSize: '90',
161+
LogTotalSize: '100',
162+
},
163+
},
164+
};
165+
const preparedDataWarning = preparePDiskDataResponse([dataWarning, {}]);
166+
167+
expect(
168+
preparedDataWarning.SlotItems?.find((slot) => slot.SlotType === 'log')?.Severity,
169+
).toEqual(3);
170+
171+
const dataDanger: TPDiskInfoResponse = {
172+
...rawData,
173+
Whiteboard: {
174+
...rawData.Whiteboard,
175+
PDisk: {
176+
...rawData.Whiteboard?.PDisk,
177+
LogUsedSize: '99',
178+
LogTotalSize: '100',
179+
},
180+
},
181+
};
182+
const preparedDataDanger = preparePDiskDataResponse([dataDanger, {}]);
183+
184+
expect(
185+
preparedDataDanger.SlotItems?.find((slot) => slot.SlotType === 'log')?.Severity,
186+
).toEqual(5);
187+
});
188+
it('Should return yellow or red severity for vdisk if its size exceeds thresholds', () => {
189+
const dataWarning: TPDiskInfoResponse = {
190+
...rawData,
191+
Whiteboard: {
192+
...rawData.Whiteboard,
193+
VDisks: [
194+
{
195+
...rawData.Whiteboard?.VDisks?.[0],
196+
AllocatedSize: '90',
197+
AvailableSize: '10',
198+
},
199+
],
200+
},
201+
};
202+
const preparedDataWarning = preparePDiskDataResponse([dataWarning, {}]);
203+
204+
expect(
205+
preparedDataWarning.SlotItems?.find((slot) => slot.SlotType === 'vDisk')?.Severity,
206+
).toEqual(3);
207+
208+
const dataDanger: TPDiskInfoResponse = {
209+
...rawData,
210+
Whiteboard: {
211+
...rawData.Whiteboard,
212+
VDisks: [
213+
{
214+
...rawData.Whiteboard?.VDisks?.[0],
215+
AllocatedSize: '99',
216+
AvailableSize: '1',
217+
},
218+
],
219+
},
220+
};
221+
const preparedDataDanger = preparePDiskDataResponse([dataDanger, {}]);
222+
223+
expect(
224+
preparedDataDanger.SlotItems?.find((slot) => slot.SlotType === 'vDisk')?.Severity,
225+
).toEqual(5);
226+
});
227+
});

src/store/reducers/pdisk/utils.ts

+16-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {TPDiskInfoResponse} from '../../../types/api/pdisk';
22
import type {TEvSystemStateResponse} from '../../../types/api/systemState';
33
import {getArray, valueIsDefined} from '../../../utils';
4+
import {getSpaceSeverity} from '../../../utils/disks/helpers';
45
import {
56
prepareWhiteboardPDiskData,
67
prepareWhiteboardVDiskData,
@@ -34,18 +35,20 @@ export function preparePDiskDataResponse([pdiskResponse = {}, nodeResponse]: [
3435
TotalSize: PDiskTotalSize,
3536
SystemSize,
3637
ExpectedSlotCount,
37-
EnforcedDynamicSlotSize,
38+
SlotSize,
3839
} = preparedPDisk;
3940

4041
let logSlot: SlotItem<'log'> | undefined;
4142

4243
if (valueIsDefined(LogTotalSize)) {
44+
const usagePercent = (Number(LogUsedSize) * 100) / Number(LogTotalSize);
45+
4346
logSlot = {
4447
SlotType: 'log',
4548
Used: Number(LogUsedSize),
4649
Total: Number(LogTotalSize),
47-
UsagePercent: (Number(LogUsedSize) * 100) / Number(LogTotalSize),
48-
Severity: 1,
50+
UsagePercent: usagePercent,
51+
Severity: getSpaceSeverity(usagePercent),
4952
SlotData: {
5053
LogUsedSize,
5154
LogTotalSize,
@@ -60,11 +63,19 @@ export function preparePDiskDataResponse([pdiskResponse = {}, nodeResponse]: [
6063
preparedVDisks.sort((disk1, disk2) => Number(disk2.VDiskSlotId) - Number(disk1.VDiskSlotId));
6164

6265
const vdisksSlots: SlotItem<'vDisk'>[] = preparedVDisks.map((preparedVDisk) => {
66+
// VDisks do not have strict limits and can be bigger than they should
67+
// In most storage views we don't colorize them depending on space usage
68+
// Colorize them inside PDiskSpaceDistribution so overused slots will be visible
69+
const slotSeverity = Math.max(
70+
getSpaceSeverity(preparedVDisk.AllocatedPercent),
71+
preparedVDisk.Severity || 0,
72+
);
73+
6374
return {
6475
SlotType: 'vDisk',
6576
Id: preparedVDisk.VDiskId?.GroupID,
6677
Title: preparedVDisk.StoragePoolName,
67-
Severity: preparedVDisk.Severity,
78+
Severity: slotSeverity,
6879
Used: Number(preparedVDisk.AllocatedSize),
6980
Total: Number(preparedVDisk.TotalSize),
7081
UsagePercent: preparedVDisk.AllocatedPercent,
@@ -78,7 +89,7 @@ export function preparePDiskDataResponse([pdiskResponse = {}, nodeResponse]: [
7889
if (ExpectedSlotCount && ExpectedSlotCount > vdisksSlots.length) {
7990
const emptySlotsCount = ExpectedSlotCount - vdisksSlots.length;
8091

81-
let emptySlotSize = Number(EnforcedDynamicSlotSize);
92+
let emptySlotSize = Number(SlotSize);
8293

8394
if (isNaN(emptySlotSize)) {
8495
const vDisksTotalSize = vdisksSlots.reduce((sum, item) => {

src/utils/disks/calculatePDiskSeverity.ts

+5-12
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
1-
import {EFlag} from '../../types/api/enums';
21
import type {TPDiskState} from '../../types/api/pdisk';
3-
import {generateEvaluator} from '../generateEvaluator';
42

5-
import {
6-
DISK_COLOR_STATE_TO_NUMERIC_SEVERITY,
7-
NOT_AVAILABLE_SEVERITY,
8-
PDISK_STATE_SEVERITY,
9-
} from './constants';
10-
11-
const getUsageSeverityForPDisk = generateEvaluator([EFlag.Green, EFlag.Yellow, EFlag.Red]);
3+
import {NOT_AVAILABLE_SEVERITY, PDISK_STATE_SEVERITY} from './constants';
4+
import {getSpaceSeverity} from './helpers';
125

136
export function calculatePDiskSeverity<
147
T extends {
@@ -17,13 +10,13 @@ export function calculatePDiskSeverity<
1710
},
1811
>(pDisk: T) {
1912
const stateSeverity = getStateSeverity(pDisk.State);
20-
const spaceSeverityFlag = getUsageSeverityForPDisk(pDisk.AllocatedPercent || 0);
13+
const spaceSeverity = getSpaceSeverity(pDisk.AllocatedPercent);
2114

22-
if (stateSeverity === NOT_AVAILABLE_SEVERITY || !spaceSeverityFlag) {
15+
if (stateSeverity === NOT_AVAILABLE_SEVERITY || !spaceSeverity) {
2316
return stateSeverity;
2417
}
2518

26-
return Math.max(stateSeverity, DISK_COLOR_STATE_TO_NUMERIC_SEVERITY[spaceSeverityFlag]);
19+
return Math.max(stateSeverity, spaceSeverity);
2720
}
2821

2922
function getStateSeverity(pDiskState?: TPDiskState) {

src/utils/disks/constants.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export const DISK_COLOR_STATE_TO_NUMERIC_SEVERITY: Record<EFlag, number> = {
1010
Yellow: 3,
1111
Orange: 4,
1212
Red: 5,
13-
};
13+
} as const;
1414

1515
type SeverityToColor = Record<number, keyof typeof DISK_COLOR_STATE_TO_NUMERIC_SEVERITY>;
1616

0 commit comments

Comments
 (0)