Skip to content

Commit 0af4225

Browse files
authored
Don't allow replication to overconsume space on PDisk (#17418)
1 parent 56856c4 commit 0af4225

File tree

11 files changed

+561
-15
lines changed

11 files changed

+561
-15
lines changed

ydb/core/blobstorage/pdisk/mock/pdisk_mock.cpp

Lines changed: 63 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#include <ydb/core/util/stlog.h>
44
#include <ydb/core/util/interval_set.h>
55

6+
#include <ydb/core/blobstorage/pdisk/blobstorage_pdisk_quota_record.h>
67
#include <ydb/core/blobstorage/pdisk/blobstorage_pdisk_util_space_color.h>
78

89
namespace NKikimr {
@@ -50,7 +51,12 @@ struct TPDiskMockState::TImpl {
5051
NPDisk::EDeviceType DeviceType;
5152
std::optional<TRcBuf> Metadata;
5253

53-
TImpl(ui32 nodeId, ui32 pdiskId, ui64 pdiskGuid, ui64 size, ui32 chunkSize, bool isDiskReadOnly, NPDisk::EDeviceType deviceType)
54+
ESpaceColorPolicy SpaceColorPolicy;
55+
std::shared_ptr<NPDisk::TQuotaRecord> ChunkSharedQuota;
56+
double Occupancy = 0;
57+
58+
TImpl(ui32 nodeId, ui32 pdiskId, ui64 pdiskGuid, ui64 size, ui32 chunkSize, bool isDiskReadOnly, NPDisk::EDeviceType deviceType,
59+
ESpaceColorPolicy spaceColorPolicy)
5460
: NodeId(nodeId)
5561
, PDiskId(pdiskId)
5662
, PDiskGuid(pdiskGuid)
@@ -62,7 +68,20 @@ struct TPDiskMockState::TImpl {
6268
, NextFreeChunk(1)
6369
, StatusFlags(NPDisk::TStatusFlags{})
6470
, DeviceType(deviceType)
65-
{}
71+
, SpaceColorPolicy(spaceColorPolicy)
72+
{
73+
switch (SpaceColorPolicy) {
74+
case ESpaceColorPolicy::SharedQuota: {
75+
ChunkSharedQuota = std::make_shared<NPDisk::TQuotaRecord>();
76+
// 13% for CYAN is default value in prod
77+
ChunkSharedQuota->ForceHardLimit(TotalChunks, NPDisk::TColorLimits::MakeChunkLimits(130));
78+
break;
79+
}
80+
case ESpaceColorPolicy::None:
81+
default:
82+
break;
83+
}
84+
}
6685

6786
TImpl(const TImpl&) = default;
6887

@@ -76,6 +95,28 @@ struct TPDiskMockState::TImpl {
7695
}
7796
}
7897

98+
void UpdateStatusFlags() {
99+
switch (SpaceColorPolicy) {
100+
case ESpaceColorPolicy::SharedQuota: {
101+
i64 before = ChunkSharedQuota->GetFree();
102+
i64 now = GetNumFreeChunks();
103+
if (before < now) {
104+
ChunkSharedQuota->Release(now - before);
105+
} else if (before > now) {
106+
ChunkSharedQuota->ForceAllocate(before - now);
107+
}
108+
109+
NKikimrBlobStorage::TPDiskSpaceColor::E newColor =
110+
ChunkSharedQuota->EstimateSpaceColor(0, &Occupancy);
111+
SetStatusFlags(SpaceColorToStatusFlag(newColor));
112+
break;
113+
}
114+
case ESpaceColorPolicy::None:
115+
default:
116+
break;
117+
}
118+
}
119+
79120
ui32 AllocateChunk(TOwner& to) {
80121
ui32 chunkIdx = TotalChunks;
81122

@@ -88,7 +129,8 @@ struct TPDiskMockState::TImpl {
88129
to.ReservedChunks.insert(FreeChunks.extract(it));
89130
}
90131

91-
Y_VERIFY(chunkIdx != TotalChunks);
132+
Y_ABORT_UNLESS(chunkIdx != TotalChunks);
133+
92134
return chunkIdx;
93135
}
94136

@@ -172,6 +214,7 @@ struct TPDiskMockState::TImpl {
172214
for (const TChunkIdx chunkIdx : owner.ReservedChunks) {
173215
owner.ChunkData.erase(chunkIdx);
174216
}
217+
175218
FreeChunks.merge(owner.ReservedChunks);
176219
AdjustFreeChunks();
177220
}
@@ -188,7 +231,8 @@ struct TPDiskMockState::TImpl {
188231
Y_VERIFY(num);
189232
owner.ChunkData.erase(chunkIdx);
190233
const bool inserted = FreeChunks.insert(chunkIdx).second;
191-
Y_VERIFY(inserted);
234+
Y_ABORT_UNLESS(inserted);
235+
192236
AdjustFreeChunks();
193237
}
194238

@@ -286,8 +330,9 @@ struct TPDiskMockState::TImpl {
286330
};
287331

288332
TPDiskMockState::TPDiskMockState(ui32 nodeId, ui32 pdiskId, ui64 pdiskGuid, ui64 size, ui32 chunkSize, bool isDiskReadOnly,
289-
NPDisk::EDeviceType deviceType)
290-
: TPDiskMockState(std::make_unique<TImpl>(nodeId, pdiskId, pdiskGuid, size, chunkSize, isDiskReadOnly, deviceType))
333+
NPDisk::EDeviceType deviceType, ESpaceColorPolicy spaceColorPolicy)
334+
: TPDiskMockState(std::make_unique<TImpl>(nodeId, pdiskId, pdiskGuid, size, chunkSize, isDiskReadOnly, deviceType,
335+
spaceColorPolicy))
291336
{}
292337

293338
TPDiskMockState::TPDiskMockState(std::unique_ptr<TImpl>&& impl)
@@ -670,12 +715,14 @@ class TPDiskMockActor : public TActorBootstrapped<TPDiskMockActor> {
670715
if (Impl.GetNumFreeChunks() < msg->SizeChunks) {
671716
PDISK_MOCK_LOG(NOTICE, PDM09, "received TEvChunkReserve", (Msg, msg->ToString()), (Error, "no free chunks"));
672717
res->Status = NKikimrProto::OUT_OF_SPACE;
718+
res->StatusFlags = GetStatusFlags() | ui32(NKikimrBlobStorage::StatusNotEnoughDiskSpaceForOperation);
673719
res->ErrorReason = "no free chunks";
674720
} else {
675721
PDISK_MOCK_LOG(DEBUG, PDM07, "received TEvChunkReserve", (Msg, msg->ToString()), (VDiskId, owner->VDiskId));
676722
for (ui32 i = 0; i < msg->SizeChunks; ++i) {
677723
res->ChunkIds.push_back(Impl.AllocateChunk(*owner));
678724
}
725+
res->StatusFlags = GetStatusFlags();
679726
PDISK_MOCK_LOG(DEBUG, PDM10, "sending TEvChunkReserveResult", (Msg, res->ToString()));
680727
}
681728
}
@@ -742,9 +789,11 @@ class TPDiskMockActor : public TActorBootstrapped<TPDiskMockActor> {
742789
if (!msg->ChunkIdx) { // allocate chunk
743790
if (!Impl.GetNumFreeChunks()) {
744791
res->Status = NKikimrProto::OUT_OF_SPACE;
792+
res->StatusFlags = GetStatusFlags() | ui32(NKikimrBlobStorage::StatusNotEnoughDiskSpaceForOperation);
745793
res->ErrorReason = "no free chunks";
746794
} else {
747795
msg->ChunkIdx = res->ChunkIdx = Impl.AllocateChunk(*owner);
796+
res->StatusFlags = GetStatusFlags();
748797
}
749798
}
750799
if (msg->ChunkIdx) {
@@ -848,7 +897,7 @@ class TPDiskMockActor : public TActorBootstrapped<TPDiskMockActor> {
848897
auto res = std::make_unique<NPDisk::TEvCheckSpaceResult>(NKikimrProto::OK, GetStatusFlags(),
849898
Impl.GetNumFreeChunks(), Impl.TotalChunks, Impl.TotalChunks - Impl.GetNumFreeChunks(),
850899
Impl.Owners.size(), TString());
851-
res->Occupancy = (double)res->UsedChunks / res->TotalChunks;
900+
res->Occupancy = GetOccupancy();
852901
Impl.FindOwner(msg, res); // to ensure correct owner/round
853902
Send(ev->Sender, res.release());
854903
}
@@ -883,9 +932,16 @@ class TPDiskMockActor : public TActorBootstrapped<TPDiskMockActor> {
883932
}
884933

885934
NPDisk::TStatusFlags GetStatusFlags() {
935+
Impl.UpdateStatusFlags();
886936
return Impl.StatusFlags;
887937
}
888938

939+
double GetOccupancy() {
940+
return (Impl.Occupancy == 0)
941+
? ((double)(Impl.TotalChunks - Impl.GetNumFreeChunks()) / Impl.TotalChunks)
942+
: Impl.Occupancy;
943+
}
944+
889945
void ErrorHandle(NPDisk::TEvYardInit::TPtr &ev) {
890946
Send(ev->Sender, new NPDisk::TEvYardInitResult(NKikimrProto::CORRUPTED, State->GetStateErrorReason()));
891947
}

ydb/core/blobstorage/pdisk/mock/pdisk_mock.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,19 @@ namespace NKikimr {
1717
std::unique_ptr<TImpl> Impl;
1818
friend class TPDiskMockActor;
1919

20+
public:
21+
enum class ESpaceColorPolicy {
22+
None = 0,
23+
SharedQuota,
24+
};
25+
2026
public:
2127
using TPtr = TIntrusivePtr<TPDiskMockState>;
2228

2329
public:
2430
TPDiskMockState(ui32 nodeId, ui32 pdiskId, ui64 pdiskGuid, ui64 size, ui32 chunkSize = 128 << 20,
25-
bool isDiskReadOnly = false, NPDisk::EDeviceType deviceType = NPDisk::EDeviceType::DEVICE_TYPE_NVME);
31+
bool isDiskReadOnly = false, NPDisk::EDeviceType deviceType = NPDisk::EDeviceType::DEVICE_TYPE_NVME,
32+
ESpaceColorPolicy spaceColorPolicy = ESpaceColorPolicy::None);
2633
TPDiskMockState(std::unique_ptr<TImpl>&& impl);
2734
~TPDiskMockState();
2835

ydb/core/blobstorage/ut_blobstorage/lib/env.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ struct TEnvironmentSetup {
5656
const ui32 MaxNumOfSlowDisks = 2;
5757
const ui32 ReplMaxQuantumBytes = 0;
5858
const ui32 ReplMaxDonorNotReadyCount = 0;
59+
const ui64 PDiskSize = 10_TB;
60+
const ui64 PDiskChunkSize = 0;
61+
const bool TrackSharedQuotaInPDiskMock = false;
5962
};
6063

6164
const TSettings Settings;
@@ -73,8 +76,12 @@ struct TEnvironmentSetup {
7376
const auto key = std::make_pair(nodeId, pdiskId);
7477
TIntrusivePtr<TPDiskMockState>& state = Env.PDiskMockStates[key];
7578
if (!state) {
76-
state.Reset(new TPDiskMockState(nodeId, pdiskId, cfg->PDiskGuid, ui64(10) << 40, cfg->ChunkSize,
77-
cfg->ReadOnly, Env.Settings.DiskType));
79+
ui64 chunkSize = Env.Settings.PDiskChunkSize ? Env.Settings.PDiskChunkSize : cfg->ChunkSize;
80+
TPDiskMockState::ESpaceColorPolicy spaceColorPolicy = Env.Settings.TrackSharedQuotaInPDiskMock
81+
? TPDiskMockState::ESpaceColorPolicy::SharedQuota
82+
: TPDiskMockState::ESpaceColorPolicy::None;
83+
state.Reset(new TPDiskMockState(nodeId, pdiskId, cfg->PDiskGuid, Env.Settings.PDiskSize, chunkSize,
84+
cfg->ReadOnly, Env.Settings.DiskType, spaceColorPolicy));
7885
}
7986
const TActorId& actorId = ctx.Register(CreatePDiskMockActor(state), TMailboxType::HTSwap, poolId);
8087
const TActorId& serviceId = MakeBlobStoragePDiskID(nodeId, pdiskId);

0 commit comments

Comments
 (0)