Skip to content

Commit b5d5df4

Browse files
committed
DS proxy mirror-3-dc restoration strategy test
1 parent f711ded commit b5d5df4

File tree

2 files changed

+177
-4
lines changed

2 files changed

+177
-4
lines changed

ydb/core/blobstorage/dsproxy/dsproxy_blackboard.h

+4-4
Original file line numberDiff line numberDiff line change
@@ -108,9 +108,9 @@ struct TBlobState {
108108
};
109109

110110
struct TDiskGetRequest {
111-
const TLogoBlobID Id;
112-
const ui32 Shift;
113-
const ui32 Size;
111+
TLogoBlobID Id;
112+
ui32 Shift;
113+
ui32 Size;
114114
ssize_t PartMapIndex = -1;
115115

116116
TDiskGetRequest(const TLogoBlobID &id, const ui32 shift, const ui32 size)
@@ -127,7 +127,7 @@ struct TDiskPutRequest {
127127
ReasonInitial,
128128
ReasonAccelerate
129129
};
130-
const TLogoBlobID Id;
130+
TLogoBlobID Id;
131131
TRope Buffer;
132132
EPutReason Reason;
133133
bool IsHandoff;

ydb/core/blobstorage/dsproxy/ut_strategy/strategy_ut.cpp

+173
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
#include <ydb/core/blobstorage/dsproxy/dsproxy_blackboard.h>
22
#include <ydb/core/blobstorage/dsproxy/dsproxy_strategy_restore.h>
3+
#include <ydb/core/blobstorage/dsproxy/dsproxy_strategy_get_m3dc_restore.h>
34
#include <library/cpp/testing/unittest/registar.h>
45
#include <util/stream/null.h>
6+
#include <util/generic/overloaded.h>
57

68
using namespace NActors;
79
using namespace NKikimr;
@@ -193,10 +195,181 @@ void RunStrategyTest(TBlobStorageGroupType type) {
193195
}
194196
}
195197

198+
struct TGetQuery {
199+
ui32 OrderNumber;
200+
TLogoBlobID Id;
201+
ui32 Shift;
202+
ui32 Size;
203+
204+
auto AsTuple() const { return std::make_tuple(OrderNumber, Id, Shift, Size); }
205+
friend bool operator ==(const TGetQuery& x, const TGetQuery& y) { return x.AsTuple() == y.AsTuple(); }
206+
friend bool operator <(const TGetQuery& x, const TGetQuery& y) { return x.AsTuple() < y.AsTuple(); }
207+
};
208+
209+
struct TPutQuery {
210+
ui32 OrderNumber;
211+
TLogoBlobID Id;
212+
213+
auto AsTuple() const { return std::make_tuple(OrderNumber, Id); }
214+
friend bool operator ==(const TPutQuery& x, const TPutQuery& y) { return x.AsTuple() == y.AsTuple(); }
215+
friend bool operator <(const TPutQuery& x, const TPutQuery& y) { return x.AsTuple() < y.AsTuple(); }
216+
};
217+
218+
using TOperation = std::variant<TGetQuery, TPutQuery>;
219+
220+
void RunTestLevel(const TBlobStorageGroupInfo& info, TBlackboard& blackboard,
221+
const std::function<EStrategyOutcome(TBlackboard&)>& runStrategies, const TLogoBlobID& id,
222+
std::vector<TOperation>& stock, TSubgroupPartLayout presenceMask, bool nonWorkingDomain,
223+
std::set<TOperation>& context, ui32& terminals) {
224+
// see which operations we can add to the stock
225+
const size_t stockSizeOnEntry = stock.size();
226+
auto& requests = blackboard.GroupDiskRequests.DiskRequestsForOrderNumber;
227+
for (ui32 i = 0; i < info.GetTotalVDisksNum(); ++i) {
228+
for (auto& j = requests[i].FirstUnsentRequestIdx; j < requests[i].GetsToSend.size(); ++j) {
229+
auto& get = requests[i].GetsToSend[j];
230+
stock.push_back(TGetQuery{i, get.Id, get.Shift, get.Size});
231+
const bool inserted = context.insert(stock.back()).second;
232+
UNIT_ASSERT(inserted);
233+
}
234+
for (auto& j = requests[i].FirstUnsentPutIdx; j < requests[i].PutsToSend.size(); ++j) {
235+
auto& put = requests[i].PutsToSend[j];
236+
stock.push_back(TPutQuery{i, put.Id});
237+
const bool inserted = context.insert(stock.back()).second;
238+
UNIT_ASSERT(inserted);
239+
}
240+
}
241+
242+
bool canIssuePuts = true;
243+
for (size_t i = 0; i < stock.size(); ++i) {
244+
if (std::holds_alternative<TGetQuery>(stock[i])) {
245+
canIssuePuts = false;
246+
break;
247+
}
248+
}
249+
250+
// try every single operation in stock
251+
for (size_t i = 0; i < stock.size(); ++i) {
252+
if (!canIssuePuts && std::holds_alternative<TPutQuery>(stock[i])) {
253+
continue;
254+
}
255+
if (auto *get = std::get_if<TGetQuery>(&stock[i]); get && context.contains(TPutQuery{get->OrderNumber, get->Id})) {
256+
continue;
257+
}
258+
259+
std::swap(stock[i], stock.back());
260+
TOperation operation = std::move(stock.back());
261+
stock.pop_back();
262+
263+
TBlackboard branch(blackboard);
264+
TSubgroupPartLayout myPresenceMask(presenceMask);
265+
266+
std::visit(TOverloaded{
267+
[&](const TGetQuery& op) {
268+
const ui32 idxInSubgroup = info.GetTopology().GetIdxInSubgroup(info.GetVDiskId(op.OrderNumber), id.Hash());
269+
if (nonWorkingDomain && idxInSubgroup % 3 == 2) {
270+
branch.AddErrorResponse(op.Id, op.OrderNumber);
271+
} else if (myPresenceMask.GetDisksWithPart(op.Id.PartId() - 1) >> idxInSubgroup & 1) {
272+
const ui32 blobSize = op.Id.BlobSize();
273+
const ui32 shift = Min(op.Shift, blobSize);
274+
const ui32 size = Min(op.Size ? op.Size : Max<ui32>(), blobSize - shift);
275+
branch.AddResponseData(op.Id, op.OrderNumber, shift, TRope(TString(size, 'X')));
276+
} else {
277+
branch.AddNoDataResponse(op.Id, op.OrderNumber);
278+
}
279+
},
280+
[&](const TPutQuery& op) {
281+
const ui32 idxInSubgroup = info.GetTopology().GetIdxInSubgroup(info.GetVDiskId(op.OrderNumber), id.Hash());
282+
if (nonWorkingDomain && idxInSubgroup % 3 == 2) {
283+
branch.AddErrorResponse(op.Id, op.OrderNumber);
284+
} else {
285+
myPresenceMask.AddItem(idxInSubgroup, op.Id.PartId() - 1, info.Type);
286+
branch.AddPutOkResponse(op.Id, op.OrderNumber);
287+
}
288+
}
289+
}, operation);
290+
291+
auto outcome = runStrategies(branch);
292+
UNIT_ASSERT(outcome != EStrategyOutcome::ERROR);
293+
if (outcome == EStrategyOutcome::DONE) {
294+
TBlobStorageGroupInfo::TOrderNums nums;
295+
info.GetTopology().PickSubgroup(id.Hash(), nums);
296+
UNIT_ASSERT(info.GetQuorumChecker().GetBlobState(myPresenceMask, {&info.GetTopology()}) == TBlobStorageGroupInfo::EBS_FULL);
297+
++terminals;
298+
} else {
299+
RunTestLevel(info, branch, runStrategies, id, stock, myPresenceMask, nonWorkingDomain, context, terminals);
300+
}
301+
302+
stock.push_back(std::move(operation));
303+
std::swap(stock[i], stock.back());
304+
}
305+
306+
// revert stock
307+
for (size_t i = stockSizeOnEntry; i < stock.size(); ++i) {
308+
const size_t n = context.erase(stock[i]);
309+
UNIT_ASSERT(n);
310+
}
311+
stock.resize(stockSizeOnEntry);
312+
}
313+
196314
Y_UNIT_TEST_SUITE(DSProxyStrategyTest) {
197315

198316
Y_UNIT_TEST(Restore_block42) {
199317
RunStrategyTest<TRestoreStrategy>(TBlobStorageGroupType::Erasure4Plus2Block);
200318
}
201319

320+
Y_UNIT_TEST(Restore_mirror3dc) {
321+
THPTimer timer;
322+
const TBlobStorageGroupType type(TBlobStorageGroupType::ErasureMirror3dc);
323+
324+
TBlobStorageGroupInfo info(type, 1, 3, 3);
325+
info.Ref();
326+
TGroupQueues groupQueues(info.GetTopology());
327+
groupQueues.Ref();
328+
329+
std::vector<TOperation> stock;
330+
331+
TLogContext logCtx(NKikimrServices::BS_PROXY, false);
332+
logCtx.SuppressLog = true;
333+
334+
auto runStrategies = [&](TBlackboard& blackboard) {
335+
return blackboard.RunStrategy(logCtx, TMirror3dcGetWithRestoreStrategy());
336+
};
337+
338+
const ui32 base = RandomNumber(512u);
339+
for (ui32 i = 0; i < 512; ++i) {
340+
const ui32 diskMask = (base + i) % 512;
341+
for (bool nonWorkingDomain : {false, true}) {
342+
TBlackboard blackboard(&info, &groupQueues, NKikimrBlobStorage::UserData, NKikimrBlobStorage::FastRead);
343+
344+
const TLogoBlobID id(1'000'000'000, 1, 1, 0, 1000, 0);
345+
TSubgroupPartLayout presenceMask;
346+
blackboard.AddNeeded(id, 0, id.BlobSize());
347+
bool partsAvailable = false;
348+
for (ui32 idxInSubgroup = 0; idxInSubgroup < 9; ++idxInSubgroup) {
349+
if (diskMask >> idxInSubgroup & 1 && (!nonWorkingDomain || idxInSubgroup % 3 != 2)) {
350+
presenceMask.AddItem(idxInSubgroup, idxInSubgroup % 3, info.Type);
351+
partsAvailable = true;
352+
}
353+
}
354+
if (!partsAvailable) {
355+
continue;
356+
}
357+
358+
Cerr << "diskMask# " << diskMask << " nonWorkingDomain# " << nonWorkingDomain;
359+
360+
auto outcome = runStrategies(blackboard);
361+
UNIT_ASSERT(outcome == EStrategyOutcome::IN_PROGRESS);
362+
363+
std::set<TOperation> context;
364+
ui32 terminals = 0;
365+
RunTestLevel(info, blackboard, runStrategies, id, stock, presenceMask, nonWorkingDomain, context, terminals);
366+
Cerr << " " << terminals << Endl;
367+
368+
if (TDuration::Seconds(timer.Passed()) >= TDuration::Minutes(5)) {
369+
break;
370+
}
371+
}
372+
}
373+
}
374+
202375
}

0 commit comments

Comments
 (0)