Skip to content

Commit f50686c

Browse files
CopilotantonkovalenkoadameatRaubzeug
authored
feat(VDisk): add replication progress, remaining time, donors info (#2588)
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: antonkovalenko <692649+antonkovalenko@users.noreply.github.com> Co-authored-by: adameat <34044711+adameat@users.noreply.github.com> Co-authored-by: Alexey Efimov <xeno@ydb.tech> Co-authored-by: Elena Makarova <el-makarova@yandex-team.ru>
1 parent 1b5edfb commit f50686c

File tree

4 files changed

+122
-3
lines changed

4 files changed

+122
-3
lines changed

src/components/VDiskInfo/VDiskInfo.tsx

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,20 @@ import React from 'react';
33
import {Flex} from '@gravity-ui/uikit';
44

55
import {getVDiskPagePath} from '../../routes';
6+
import {EVDiskState} from '../../types/api/vdisk';
67
import {valueIsDefined} from '../../utils';
78
import {cn} from '../../utils/cn';
8-
import {formatStorageValuesToGb} from '../../utils/dataFormatters/dataFormatters';
9+
import {
10+
formatStorageValuesToGb,
11+
formatUptimeInSeconds,
12+
} from '../../utils/dataFormatters/dataFormatters';
913
import {createVDiskDeveloperUILink} from '../../utils/developerUI/developerUI';
1014
import {getSeverityColor} from '../../utils/disks/helpers';
1115
import type {PreparedVDisk} from '../../utils/disks/types';
1216
import {useIsUserAllowedToMakeChanges} from '../../utils/hooks/useIsUserAllowedToMakeChanges';
1317
import {bytesToSpeed} from '../../utils/utils';
1418
import {InfoViewer} from '../InfoViewer';
19+
import {InternalLink} from '../InternalLink';
1520
import {LinkWithIcon} from '../LinkWithIcon/LinkWithIcon';
1621
import {ProgressViewer} from '../ProgressViewer/ProgressViewer';
1722
import {StatusIcon} from '../StatusIcon/StatusIcon';
@@ -46,6 +51,9 @@ export function VDiskInfo<T extends PreparedVDisk>({
4651
FrontQueues,
4752
Guid,
4853
Replicated,
54+
ReplicationProgress,
55+
ReplicationSecondsRemaining,
56+
Donors,
4957
VDiskState,
5058
VDiskSlotId,
5159
Kind,
@@ -130,6 +138,31 @@ export function VDiskInfo<T extends PreparedVDisk>({
130138
value: Replicated ? vDiskInfoKeyset('yes') : vDiskInfoKeyset('no'),
131139
});
132140
}
141+
// Only show replication progress and time remaining when disk is not replicated and state is OK
142+
if (Replicated === false && VDiskState === EVDiskState.OK) {
143+
if (valueIsDefined(ReplicationProgress)) {
144+
rightColumn.push({
145+
label: vDiskInfoKeyset('replication-progress'),
146+
value: (
147+
<ProgressViewer
148+
value={Math.round(ReplicationProgress * 100)}
149+
percents
150+
colorizeProgress={true}
151+
capacity={100}
152+
/>
153+
),
154+
});
155+
}
156+
if (valueIsDefined(ReplicationSecondsRemaining)) {
157+
const timeRemaining = formatUptimeInSeconds(ReplicationSecondsRemaining);
158+
if (timeRemaining) {
159+
rightColumn.push({
160+
label: vDiskInfoKeyset('replication-time-remaining'),
161+
value: timeRemaining,
162+
});
163+
}
164+
}
165+
}
133166
if (valueIsDefined(VDiskSlotId)) {
134167
rightColumn.push({label: vDiskInfoKeyset('slot-id'), value: VDiskSlotId});
135168
}
@@ -153,6 +186,45 @@ export function VDiskInfo<T extends PreparedVDisk>({
153186
});
154187
}
155188

189+
// Show donors list when replication is in progress
190+
if (Replicated === false && VDiskState === EVDiskState.OK && Donors?.length) {
191+
const donorLinks = Donors.map((donor, index) => {
192+
const {
193+
StringifiedId: id,
194+
NodeId: dNodeId,
195+
PDiskId: dPDiskId,
196+
VDiskSlotId: dVSlotId,
197+
} = donor;
198+
199+
if (!id || !dVSlotId || !dNodeId || !dPDiskId) {
200+
return null;
201+
}
202+
203+
const vDiskPath = getVDiskPagePath({
204+
nodeId: dNodeId,
205+
pDiskId: dPDiskId,
206+
vDiskSlotId: dVSlotId,
207+
});
208+
209+
return (
210+
<InternalLink key={index} to={vDiskPath}>
211+
{id}
212+
</InternalLink>
213+
);
214+
}).filter(Boolean);
215+
216+
if (donorLinks.length) {
217+
rightColumn.push({
218+
label: vDiskInfoKeyset('donors'),
219+
value: (
220+
<Flex direction="column" gap={1}>
221+
{donorLinks}
222+
</Flex>
223+
),
224+
});
225+
}
226+
}
227+
156228
const diskParamsDefined =
157229
valueIsDefined(PDiskId) && valueIsDefined(NodeId) && valueIsDefined(VDiskSlotId);
158230

src/components/VDiskInfo/i18n/en.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
"instance-guid": "Instance GUID",
99

1010
"replication-status": "Replicated",
11+
"replication-progress": "Replication Progress",
12+
"replication-time-remaining": "Time Remaining",
13+
"donors": "Donors",
1114
"state-status": "VDisk State",
1215
"space-status": "Disk Space",
1316

src/components/VDiskPopup/VDiskPopup.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,11 @@ import {Flex, Label} from '@gravity-ui/uikit';
44

55
import {selectNodesMap} from '../../store/reducers/nodesList';
66
import {EFlag} from '../../types/api/enums';
7+
import {EVDiskState} from '../../types/api/vdisk';
78
import {valueIsDefined} from '../../utils';
89
import {cn} from '../../utils/cn';
910
import {EMPTY_DATA_PLACEHOLDER} from '../../utils/constants';
11+
import {formatUptimeInSeconds} from '../../utils/dataFormatters/dataFormatters';
1012
import {createVDiskDeveloperUILink} from '../../utils/developerUI/developerUI';
1113
import {isFullVDiskData} from '../../utils/disks/helpers';
1214
import type {PreparedVDisk, UnavailableDonor} from '../../utils/disks/types';
@@ -73,6 +75,8 @@ const prepareVDiskData = (data: PreparedVDisk, withDeveloperUILink?: boolean) =>
7375
DiskSpace,
7476
FrontQueues,
7577
Replicated,
78+
ReplicationProgress,
79+
ReplicationSecondsRemaining,
7680
UnsyncedVDisks,
7781
AllocatedSize,
7882
ReadThroughput,
@@ -125,8 +129,27 @@ const prepareVDiskData = (data: PreparedVDisk, withDeveloperUILink?: boolean) =>
125129
vdiskData.push({label: 'FrontQueues', value: FrontQueues});
126130
}
127131

128-
if (Replicated === false) {
132+
if (Replicated === false && VDiskState === EVDiskState.OK) {
129133
vdiskData.push({label: 'Replicated', value: 'NO'});
134+
135+
// Only show replication progress and time remaining when disk is not replicated and state is OK
136+
if (valueIsDefined(ReplicationProgress)) {
137+
const progressPercent = Math.round(ReplicationProgress * 100);
138+
vdiskData.push({
139+
label: 'Progress',
140+
value: `${progressPercent}%`,
141+
});
142+
}
143+
144+
if (valueIsDefined(ReplicationSecondsRemaining)) {
145+
const timeRemaining = formatUptimeInSeconds(ReplicationSecondsRemaining);
146+
if (timeRemaining) {
147+
vdiskData.push({
148+
label: 'Remaining',
149+
value: timeRemaining,
150+
});
151+
}
152+
}
130153
}
131154

132155
if (UnsyncedVDisks) {

src/utils/disks/prepareDisks.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import {isNil} from 'lodash';
2+
13
import {valueIsDefined} from '..';
24
import type {TPDiskStateInfo} from '../../types/api/pdisk';
35
import type {TVDiskStateInfo, TVSlotId} from '../../types/api/vdisk';
@@ -63,7 +65,26 @@ export function prepareWhiteboardVDiskData(
6365
const StringifiedId = stringifyVdiskId(VDiskId);
6466

6567
const preparedDonors = Donors?.map((donor) => {
66-
return prepareWhiteboardVDiskData({...donor, DonorMode: true});
68+
// Handle both TVDiskStateInfo and TVSlotId donor types
69+
if (isFullVDiskData(donor)) {
70+
// Full VDisk data
71+
return prepareWhiteboardVDiskData({...donor, DonorMode: true});
72+
} else {
73+
// TVSlotId data - create a minimal PreparedVDisk
74+
const {NodeId: dNodeId, PDiskId: dPDiskId, VSlotId: vSlotId} = donor;
75+
const stringifiedId =
76+
!isNil(dNodeId) && !isNil(dPDiskId) && !isNil(vSlotId)
77+
? `${dNodeId}-${dPDiskId}-${vSlotId}`
78+
: '';
79+
80+
return {
81+
NodeId: dNodeId,
82+
PDiskId: dPDiskId,
83+
VDiskSlotId: vSlotId,
84+
StringifiedId: stringifiedId,
85+
DonorMode: true,
86+
};
87+
}
6788
});
6889

6990
return {

0 commit comments

Comments
 (0)