|
1 | 1 | #include <ydb/core/blobstorage/dsproxy/dsproxy_blackboard.h>
|
2 | 2 | #include <ydb/core/blobstorage/dsproxy/dsproxy_strategy_restore.h>
|
| 3 | +#include <ydb/core/blobstorage/dsproxy/dsproxy_strategy_get_m3dc_restore.h> |
3 | 4 | #include <library/cpp/testing/unittest/registar.h>
|
4 | 5 | #include <util/stream/null.h>
|
| 6 | +#include <util/generic/overloaded.h> |
5 | 7 |
|
6 | 8 | using namespace NActors;
|
7 | 9 | using namespace NKikimr;
|
@@ -193,10 +195,182 @@ void RunStrategyTest(TBlobStorageGroupType type) {
|
193 | 195 | }
|
194 | 196 | }
|
195 | 197 |
|
| 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 | + UNIT_ASSERT(!stock.empty()); |
| 242 | + |
| 243 | + bool canIssuePuts = true; |
| 244 | + for (size_t i = 0; i < stock.size(); ++i) { |
| 245 | + if (std::holds_alternative<TGetQuery>(stock[i])) { |
| 246 | + canIssuePuts = false; |
| 247 | + break; |
| 248 | + } |
| 249 | + } |
| 250 | + |
| 251 | + // try every single operation in stock |
| 252 | + for (size_t i = 0; i < stock.size(); ++i) { |
| 253 | + if (!canIssuePuts && std::holds_alternative<TPutQuery>(stock[i])) { |
| 254 | + continue; |
| 255 | + } |
| 256 | + if (auto *get = std::get_if<TGetQuery>(&stock[i]); get && context.contains(TPutQuery{get->OrderNumber, get->Id})) { |
| 257 | + continue; |
| 258 | + } |
| 259 | + |
| 260 | + std::swap(stock[i], stock.back()); |
| 261 | + TOperation operation = std::move(stock.back()); |
| 262 | + stock.pop_back(); |
| 263 | + |
| 264 | + TBlackboard branch(blackboard); |
| 265 | + TSubgroupPartLayout myPresenceMask(presenceMask); |
| 266 | + |
| 267 | + std::visit(TOverloaded{ |
| 268 | + [&](const TGetQuery& op) { |
| 269 | + const ui32 idxInSubgroup = info.GetTopology().GetIdxInSubgroup(info.GetVDiskId(op.OrderNumber), id.Hash()); |
| 270 | + if (nonWorkingDomain && idxInSubgroup % 3 == 2) { |
| 271 | + branch.AddErrorResponse(op.Id, op.OrderNumber); |
| 272 | + } else if (myPresenceMask.GetDisksWithPart(op.Id.PartId() - 1) >> idxInSubgroup & 1) { |
| 273 | + const ui32 blobSize = op.Id.BlobSize(); |
| 274 | + const ui32 shift = Min(op.Shift, blobSize); |
| 275 | + const ui32 size = Min(op.Size ? op.Size : Max<ui32>(), blobSize - shift); |
| 276 | + branch.AddResponseData(op.Id, op.OrderNumber, shift, TRope(TString(size, 'X'))); |
| 277 | + } else { |
| 278 | + branch.AddNoDataResponse(op.Id, op.OrderNumber); |
| 279 | + } |
| 280 | + }, |
| 281 | + [&](const TPutQuery& op) { |
| 282 | + const ui32 idxInSubgroup = info.GetTopology().GetIdxInSubgroup(info.GetVDiskId(op.OrderNumber), id.Hash()); |
| 283 | + if (nonWorkingDomain && idxInSubgroup % 3 == 2) { |
| 284 | + branch.AddErrorResponse(op.Id, op.OrderNumber); |
| 285 | + } else { |
| 286 | + myPresenceMask.AddItem(idxInSubgroup, op.Id.PartId() - 1, info.Type); |
| 287 | + branch.AddPutOkResponse(op.Id, op.OrderNumber); |
| 288 | + } |
| 289 | + } |
| 290 | + }, operation); |
| 291 | + |
| 292 | + auto outcome = runStrategies(branch); |
| 293 | + UNIT_ASSERT(outcome != EStrategyOutcome::ERROR); |
| 294 | + if (outcome == EStrategyOutcome::DONE) { |
| 295 | + TBlobStorageGroupInfo::TOrderNums nums; |
| 296 | + info.GetTopology().PickSubgroup(id.Hash(), nums); |
| 297 | + UNIT_ASSERT(info.GetQuorumChecker().GetBlobState(myPresenceMask, {&info.GetTopology()}) == TBlobStorageGroupInfo::EBS_FULL); |
| 298 | + ++terminals; |
| 299 | + } else { |
| 300 | + RunTestLevel(info, branch, runStrategies, id, stock, myPresenceMask, nonWorkingDomain, context, terminals); |
| 301 | + } |
| 302 | + |
| 303 | + stock.push_back(std::move(operation)); |
| 304 | + std::swap(stock[i], stock.back()); |
| 305 | + } |
| 306 | + |
| 307 | + // revert stock |
| 308 | + for (size_t i = stockSizeOnEntry; i < stock.size(); ++i) { |
| 309 | + const size_t n = context.erase(stock[i]); |
| 310 | + UNIT_ASSERT(n); |
| 311 | + } |
| 312 | + stock.resize(stockSizeOnEntry); |
| 313 | +} |
| 314 | + |
196 | 315 | Y_UNIT_TEST_SUITE(DSProxyStrategyTest) {
|
197 | 316 |
|
198 | 317 | Y_UNIT_TEST(Restore_block42) {
|
199 | 318 | RunStrategyTest<TRestoreStrategy>(TBlobStorageGroupType::Erasure4Plus2Block);
|
200 | 319 | }
|
201 | 320 |
|
| 321 | + Y_UNIT_TEST(Restore_mirror3dc) { |
| 322 | + THPTimer timer; |
| 323 | + const TBlobStorageGroupType type(TBlobStorageGroupType::ErasureMirror3dc); |
| 324 | + |
| 325 | + TBlobStorageGroupInfo info(type, 1, 3, 3); |
| 326 | + info.Ref(); |
| 327 | + TGroupQueues groupQueues(info.GetTopology()); |
| 328 | + groupQueues.Ref(); |
| 329 | + |
| 330 | + std::vector<TOperation> stock; |
| 331 | + |
| 332 | + TLogContext logCtx(NKikimrServices::BS_PROXY, false); |
| 333 | + logCtx.SuppressLog = true; |
| 334 | + |
| 335 | + auto runStrategies = [&](TBlackboard& blackboard) { |
| 336 | + return blackboard.RunStrategy(logCtx, TMirror3dcGetWithRestoreStrategy()); |
| 337 | + }; |
| 338 | + |
| 339 | + const ui32 base = RandomNumber(512u); |
| 340 | + for (ui32 i = 0; i < 512; ++i) { |
| 341 | + const ui32 diskMask = (base + i) % 512; |
| 342 | + for (bool nonWorkingDomain : {false, true}) { |
| 343 | + TBlackboard blackboard(&info, &groupQueues, NKikimrBlobStorage::UserData, NKikimrBlobStorage::FastRead); |
| 344 | + |
| 345 | + const TLogoBlobID id(1'000'000'000, 1, 1, 0, 1000, 0); |
| 346 | + TSubgroupPartLayout presenceMask; |
| 347 | + blackboard.AddNeeded(id, 0, id.BlobSize()); |
| 348 | + bool partsAvailable = false; |
| 349 | + for (ui32 idxInSubgroup = 0; idxInSubgroup < 9; ++idxInSubgroup) { |
| 350 | + if (diskMask >> idxInSubgroup & 1 && (!nonWorkingDomain || idxInSubgroup % 3 != 2)) { |
| 351 | + presenceMask.AddItem(idxInSubgroup, idxInSubgroup % 3, info.Type); |
| 352 | + partsAvailable = true; |
| 353 | + } |
| 354 | + } |
| 355 | + if (!partsAvailable) { |
| 356 | + continue; |
| 357 | + } |
| 358 | + |
| 359 | + Cerr << "diskMask# " << diskMask << " nonWorkingDomain# " << nonWorkingDomain; |
| 360 | + |
| 361 | + auto outcome = runStrategies(blackboard); |
| 362 | + UNIT_ASSERT(outcome == EStrategyOutcome::IN_PROGRESS); |
| 363 | + |
| 364 | + std::set<TOperation> context; |
| 365 | + ui32 terminals = 0; |
| 366 | + RunTestLevel(info, blackboard, runStrategies, id, stock, presenceMask, nonWorkingDomain, context, terminals); |
| 367 | + Cerr << " " << terminals << Endl; |
| 368 | + |
| 369 | + if (TDuration::Seconds(timer.Passed()) >= TDuration::Minutes(5)) { |
| 370 | + return; |
| 371 | + } |
| 372 | + } |
| 373 | + } |
| 374 | + } |
| 375 | + |
202 | 376 | }
|
0 commit comments