Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,11 @@ export function DiskStateProgressBar({
return <div className={b('fill-bar', mods)} style={{width: '100%'}} />;
}

const fillWidth = inverted ? 100 - diskAllocatedPercent : diskAllocatedPercent;
// diskAllocatedPercent could be more than 100
let fillWidth = Math.min(diskAllocatedPercent, 100);
if (inverted) {
fillWidth = Math.max(100 - diskAllocatedPercent, 0);
}

if (diskAllocatedPercent >= 0) {
return <div className={b('fill-bar', mods)} style={{width: `${fillWidth}%`}} />;
Expand Down
7 changes: 4 additions & 3 deletions src/components/VDiskInfo/VDiskInfo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ export function VDiskInfo<T extends PreparedVDisk>({

const {
AllocatedSize,
SizeLimit,
DiskSpace,
FrontQueues,
Guid,
Expand All @@ -60,7 +61,6 @@ export function VDiskInfo<T extends PreparedVDisk>({
VDiskSlotId,
Kind,
SatisfactionRank,
AvailableSize,
HasUnreadableBlobs,
IncarnationGuid,
InstanceGuid,
Expand All @@ -83,13 +83,14 @@ export function VDiskInfo<T extends PreparedVDisk>({
value: VDiskState,
});
}
if (Number(AllocatedSize) >= 0 && Number(AvailableSize) >= 0) {

if (Number(AllocatedSize) >= 0 && Number(SizeLimit) >= 0) {
leftColumn.push({
label: vDiskInfoKeyset('size'),
value: (
<ProgressViewer
value={AllocatedSize}
capacity={Number(AllocatedSize) + Number(AvailableSize)}
capacity={SizeLimit}
formatValues={formatStorageValuesToGb}
colorizeProgress={true}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,4 +255,38 @@ describe('preparePDiskDataResponse', () => {
1,
);
});

test('Should use used size as total for VDisk slot when used size exceeds size limit', () => {
const dataWithExceededVDiskUsage: TPDiskInfoResponse = {
...rawData,
Whiteboard: {
...rawData.Whiteboard,
PDisk: {
...rawData.Whiteboard?.PDisk,
EnforcedDynamicSlotSize: '15000000000', // 15GB slot size
},
VDisks: [
{
...rawData.Whiteboard?.VDisks?.[0],
AllocatedSize: '20000000000', // 20GB used (exceeds 15GB slot size)
AvailableSize: '0', // 0 available, so slot size should be used as limit
},
],
},
BSC: {
...rawData.BSC,
PDisk: {
...rawData.BSC?.PDisk,
EnforcedDynamicSlotSize: '15000000000', // 15GB slot size
},
},
};
const preparedData = preparePDiskDataResponse([dataWithExceededVDiskUsage, {}]);

const vDiskSlot = preparedData.SlotItems?.find((slot) => slot.SlotType === 'vDisk');

expect(vDiskSlot?.Used).toEqual(20_000_000_000); // 20GB used
// Since used (20GB) > sizeLimit (15GB), total should be set to used size
expect(vDiskSlot?.Total).toEqual(20_000_000_000); // Total equals used when used exceeds limit
});
});
13 changes: 11 additions & 2 deletions src/store/reducers/pdisk/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,22 @@ export function preparePDiskDataResponse([pdiskResponse = {}, nodeResponse]: [
// VDisks with their full statuses can be seen in popup on hover, in Storage table and on vdisks pages
const slotSeverity = getSpaceSeverity(preparedVDisk.AllocatedPercent);

const used = Number(preparedVDisk.AllocatedSize);
let total = Number(preparedVDisk.SizeLimit);

// In case used size is more than limit
// use used size as total to show correct slot relative size
if (used > total) {
total = used;
}

return {
SlotType: 'vDisk',
Id: preparedVDisk.VDiskId?.GroupID,
Title: preparedVDisk.StoragePoolName,
Severity: slotSeverity,
Used: Number(preparedVDisk.AllocatedSize),
Total: Number(preparedVDisk.TotalSize),
Used: used,
Total: total,
UsagePercent: preparedVDisk.AllocatedPercent,

SlotData: preparedVDisk,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ describe('prepareGroupsVDisk', () => {

AllocatedSize: 30943477760,
AvailableSize: 234461593600,
TotalSize: 265405071360,
SizeLimit: 265405071360,
AllocatedPercent: 11,

Donors: undefined,
Expand Down Expand Up @@ -134,7 +134,7 @@ describe('prepareGroupsVDisk', () => {

AllocatedSize: 30943477760,
AvailableSize: 234461593600,
TotalSize: 265405071360,
SizeLimit: 265405071360,
AllocatedPercent: 11,

PDisk: {
Expand Down Expand Up @@ -236,7 +236,7 @@ describe('prepareGroupsVDisk', () => {

AllocatedSize: 30943477760,
AvailableSize: 234461593600,
TotalSize: 265405071360,
SizeLimit: 265405071360,
AllocatedPercent: 11,

Donors: undefined,
Expand Down
3 changes: 2 additions & 1 deletion src/store/reducers/storage/prepareGroupsDisks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ export function prepareGroupsVDisk(data: TStorageVDisk = {}): PreparedVDisk {
const Severity = calculateVDiskSeverity(mergedVDiskData);

const vDiskSizeFields = prepareVDiskSizeFields({
AvailableSize: mergedVDiskData.AvailableSize ?? PDisk?.AvailableSize,
AvailableSize: mergedVDiskData.AvailableSize,
AllocatedSize: mergedVDiskData.AllocatedSize,
SlotSize: PDisk?.SlotSize,
});

const preparedDonors = bscVDisk.Donors?.map((donor) => {
Expand Down
16 changes: 6 additions & 10 deletions src/store/reducers/vdisk/utils.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import type {StorageGroupsResponse} from '../../../types/api/storage';
import type {TEvSystemStateResponse} from '../../../types/api/systemState';
import {
prepareWhiteboardPDiskData,
prepareWhiteboardVDiskData,
} from '../../../utils/disks/prepareDisks';
import {prepareNodeSystemState} from '../../../utils/nodes';
import {prepareGroupsVDisk} from '../storage/prepareGroupsDisks';

import type {VDiskData} from './types';

Expand All @@ -18,21 +15,20 @@ export function prepareVDiskDataResponse(
const rawVDisk = storageGroupResponse?.StorageGroups?.[0].VDisks?.find(
({VDiskId}) => VDiskId === vDiskId,
);
const preparedVDisk = prepareWhiteboardVDiskData(rawVDisk?.Whiteboard);

const rawPDisk = rawVDisk?.PDisk?.Whiteboard;
const preparedPDisk = prepareWhiteboardPDiskData(rawPDisk);
const preparedVDisk = prepareGroupsVDisk(rawVDisk);
const preparedPDisk = preparedVDisk.PDisk;

const rawNode = nodeResponse?.SystemStateInfo?.[0];
const preparedNode = prepareNodeSystemState(rawNode);

const NodeId = preparedVDisk.NodeId ?? preparedPDisk.NodeId ?? preparedNode.NodeId;
const NodeId = preparedVDisk.NodeId ?? preparedPDisk?.NodeId ?? preparedNode.NodeId;
const NodeHost = preparedNode.Host;
const NodeType = preparedNode.Roles?.[0];
const NodeDC = preparedNode.DC;

const PDiskId = preparedVDisk.PDiskId ?? preparedPDisk.PDiskId;
const PDiskType = preparedPDisk.Type;
const PDiskId = preparedVDisk.PDiskId ?? preparedPDisk?.PDiskId;
const PDiskType = preparedPDisk?.Type;

return {
...preparedVDisk,
Expand Down
77 changes: 70 additions & 7 deletions src/utils/disks/__test__/prepareDisks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ describe('prepareWhiteboardVDiskData', () => {

AvailableSize: 188523479040,
AllocatedSize: 8996782080,
TotalSize: 197520261120,
SizeLimit: 197520261120,
AllocatedPercent: 4,
};

Expand Down Expand Up @@ -180,29 +180,92 @@ describe('prepareWhiteboardPDiskData', () => {
});

describe('prepareVDiskSizeFields', () => {
test('Should prepare VDisk size fields', () => {
test('Should prepare VDisk size fields with allocated + available as size limit', () => {
expect(
prepareVDiskSizeFields({
AvailableSize: '400',
AllocatedSize: '100',
SlotSize: '500',
}),
).toEqual({
AvailableSize: 400,
AllocatedSize: 100,
TotalSize: 500,
AllocatedPercent: 20,
SizeLimit: 500, // allocated (100) + available (400) = 500
AllocatedPercent: 20, // 100 / 500 * 100 = 20%
});
});
test('Returns NaN if on undefined data', () => {

test('Should use SlotSize as size limit when AvailableSize is 0', () => {
expect(
prepareVDiskSizeFields({
AvailableSize: '0',
AllocatedSize: '500',
SlotSize: '500',
}),
).toEqual({
AvailableSize: 0,
AllocatedSize: 500,
SizeLimit: 500, // SlotSize is used when available is 0
AllocatedPercent: 100, // 500 / 500 * 100 = 100%
});
});

test('Should use SlotSize as size limit when AvailableSize is undefined', () => {
expect(
prepareVDiskSizeFields({
AvailableSize: undefined,
AllocatedSize: '300',
SlotSize: '500',
}),
).toEqual({
AvailableSize: 0,
AllocatedSize: 300,
SizeLimit: 500, // SlotSize is used when available is undefined
AllocatedPercent: 60, // 300 / 500 * 100 = 60%
});
});

test('Should use allocated when SlotSize is undefined and available is 0', () => {
expect(
prepareVDiskSizeFields({
AvailableSize: '0',
AllocatedSize: '500',
SlotSize: undefined,
}),
).toEqual({
AvailableSize: 0,
AllocatedSize: 500,
SizeLimit: 500, // allocated (500)
AllocatedPercent: 100, // 500 / 500 * 100 = 100%
});
});

test('Should handle case when used size exceeds slot size', () => {
expect(
prepareVDiskSizeFields({
AvailableSize: '0',
AllocatedSize: '800',
SlotSize: '500',
}),
).toEqual({
AvailableSize: 0,
AllocatedSize: 800,
SizeLimit: 500, // SlotSize is used as limit
AllocatedPercent: 160, // 800 / 500 * 100 = 160%
});
});

test('Should return NaN for undefined data', () => {
expect(
prepareVDiskSizeFields({
AvailableSize: undefined,
AllocatedSize: undefined,
SlotSize: undefined,
}),
).toEqual({
AvailableSize: NaN,
AvailableSize: 0,
AllocatedSize: NaN,
TotalSize: NaN,
SizeLimit: NaN,
AllocatedPercent: NaN,
});
});
Expand Down
21 changes: 16 additions & 5 deletions src/utils/disks/prepareDisks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,9 @@ export function prepareWhiteboardVDiskData(
const actualPDiskId = PDiskId ?? preparedPDisk?.PDiskId;

const vDiskSizeFields = prepareVDiskSizeFields({
AvailableSize: AvailableSize ?? PDisk?.AvailableSize,
AvailableSize: AvailableSize,
AllocatedSize: AllocatedSize,
SlotSize: PDisk?.EnforcedDynamicSlotSize,
});

const Severity = calculateVDiskSeverity(vDiskState);
Expand Down Expand Up @@ -145,19 +146,29 @@ export function prepareWhiteboardPDiskData(pdiskState: TPDiskStateInfo = {}): Pr
export function prepareVDiskSizeFields({
AvailableSize,
AllocatedSize,
SlotSize,
}: {
AvailableSize: string | number | undefined;
AllocatedSize: string | number | undefined;
SlotSize: string | number | undefined;
}) {
const available = Number(AvailableSize);
const available = Number(AvailableSize ?? 0);
const allocated = Number(AllocatedSize);
const total = allocated + available;
const allocatedPercent = Math.floor((allocated * 100) / total);
const slotSize = Number(SlotSize);

let sizeLimit = allocated + available;

// If no available size or available size is 0, slot size should be used as limit
if (!available && slotSize) {
sizeLimit = slotSize;
}

const allocatedPercent = Math.floor((allocated * 100) / sizeLimit);

return {
AvailableSize: available,
AllocatedSize: allocated,
TotalSize: total,
SizeLimit: sizeLimit,
AllocatedPercent: allocatedPercent,
};
}
Expand Down
2 changes: 1 addition & 1 deletion src/utils/disks/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ export interface PreparedVDisk
StringifiedId?: string;

AvailableSize?: number;
TotalSize?: number;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this field doesnt exist anymore or we just intentionally choose to ignore it?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@greptile could you answer this question?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking at the code context, the comment is asking about the AllocatedSize field in the PreparedVDisk interface on line 31.

This field still exists - it's being kept intentionally. The PR renamed TotalSize to SizeLimit (line 33), but AllocatedSize remains because it represents the actual allocated/used size of the VDisk, which is still needed.

The change in this PR was specifically:

  • Removed: TotalSize
  • Added: SizeLimit (line 33)

AllocatedSize is a core field that comes from the API (TVDiskStateInfo) and is used throughout the codebase to show how much space is actually used. The PR just adds a new SizeLimit field to properly represent the maximum allowed size (using SlotSize when AvailableSize is 0).

So to answer: the field still exists and is intentionally kept - no changes were made to AllocatedSize in this interface.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@greptile I mean we removed TotalSize but it seems field from api? Is it correct to remove it totally?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perfect! Looking at the API type TVDiskStateInfo, there is no TotalSize field in the VDisk API response. The VDisk API only has:

  • AllocatedSize (how much space is used)
  • AvailableSize (how much space is available)

So yes, it's correct to remove TotalSize from PreparedVDisk. The PR actually fixes a conceptual issue:

  1. Before: PreparedVDisk had TotalSize which was computed as AllocatedSize + AvailableSize
  2. Problem: When AvailableSize is 0, this made it look like the VDisk was at 100% capacity, even though the actual slot size limit (from SlotSize field) might be larger
  3. After: Replaced TotalSize with SizeLimit which uses:
    • AllocatedSize + AvailableSize when AvailableSize > 0 (normal case)
    • SlotSize when AvailableSize = 0 (fallback to actual physical limit)

TotalSize was a derived/computed field that didn't exist in the API. The rename to SizeLimit better reflects its purpose and the new logic handles the edge case correctly.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@greptile thx darling, now I understand <3

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're welcome! 💙 Glad I could help clarify that TotalSize wasn't an API field and the PR correctly removes it. Feel free to ping me if you have any other questions about the changes!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just replaced TotalSize with SizeLimit, SizeLimit is a better naming for me, because used size can exceed this limit

AllocatedSize?: number;
AllocatedPercent?: number;
SizeLimit?: number;

Donors?: PreparedVDisk[];
}
Expand Down
Loading