|
22 | 22 | #include "collections/vbucket_manifest_handles.h"
|
23 | 23 | #include "dcp/response.h"
|
24 | 24 | #include "test_helpers.h"
|
| 25 | +#include "thread_gate.h" |
25 | 26 | #include "vbucket.h"
|
26 | 27 |
|
27 | 28 | #include <folly/portability/GMock.h>
|
@@ -454,6 +455,70 @@ TEST_P(CheckpointRemoverTest, MemRecoveryByCheckpointCreation) {
|
454 | 455 | EXPECT_EQ(0, store->getRequiredCMMemoryReduction());
|
455 | 456 | }
|
456 | 457 |
|
| 458 | +// Without the fix, there is a data race in |
| 459 | +// CheckpointManager::takeAndResetCursors which did not take a queueLock, |
| 460 | +// and could mutate the CheckpointManager while it is being accessed, |
| 461 | +// e.g. in CheckpointManager::getListOfCursorsToDrop. |
| 462 | +TEST_P(CheckpointRemoverTest, MB59601) { |
| 463 | + if (!isPersistent()) { |
| 464 | + GTEST_SKIP(); |
| 465 | + } |
| 466 | + |
| 467 | + setVBucketStateAndRunPersistTask(vbid, vbucket_state_active); |
| 468 | + auto& config = engine->getConfiguration(); |
| 469 | + config.setChkExpelEnabled(false); |
| 470 | + config.setMaxSize(100UL * 1024 * 1024); |
| 471 | + // Disable the mem-based checkpoint creation in this test, we would end up |
| 472 | + // doing straight CheckpointRemoval rather than ItemExpel/CursorDrop |
| 473 | + config.setCheckpointMaxSize(std::numeric_limits<size_t>::max()); |
| 474 | + const auto chkptMemRecoveryLimit = |
| 475 | + config.getMaxSize() * store->getCheckpointMemoryRatio() * |
| 476 | + store->getCheckpointMemoryRecoveryUpperMark(); |
| 477 | + auto& stats = engine->getEpStats(); |
| 478 | + stats.mem_low_wat.store(1); |
| 479 | + |
| 480 | + int numItems = 0; |
| 481 | + const std::string value(1024 * 1024, 'x'); |
| 482 | + while (stats.getCheckpointManagerEstimatedMemUsage() < |
| 483 | + chkptMemRecoveryLimit) { |
| 484 | + auto docKey = "key_" + std::to_string(++numItems); |
| 485 | + store_item(vbid, makeStoredDocKey(docKey), value); |
| 486 | + } |
| 487 | + flushVBucketToDiskIfPersistent(vbid, numItems); |
| 488 | + |
| 489 | + // VB needs to be replica to rollback |
| 490 | + store->setVBucketState(vbid, vbucket_state_replica); |
| 491 | + |
| 492 | + EXPECT_GT(stats.getNumCheckpoints(), 0); |
| 493 | + EXPECT_GT(store->getRequiredCMMemoryReduction(), 0); |
| 494 | + |
| 495 | + /// Synchronises just before accessing and mutating CM::cursors |
| 496 | + ThreadGate tg(2); |
| 497 | + std::thread bgThread; |
| 498 | + |
| 499 | + auto& oldManager = *store->getVBucket(vbid)->checkpointManager; |
| 500 | + oldManager.takeAndResetCursorsHook = [this, &tg, &bgThread]() { |
| 501 | + // Note: takeAndResetCursorsHook is executed *after* the new VBucket |
| 502 | + // has already been created |
| 503 | + |
| 504 | + auto& newManager = *store->getVBucket(vbid)->checkpointManager; |
| 505 | + newManager.getListOfCursorsToDropHook = [&tg]() { tg.threadUp(); }; |
| 506 | + bgThread = std::thread([this]() { |
| 507 | + auto remover = std::make_shared<CheckpointMemRecoveryTask>( |
| 508 | + *engine, |
| 509 | + engine->getEpStats(), |
| 510 | + engine->getConfiguration().getChkRemoverStime(), |
| 511 | + 0); |
| 512 | + remover->run(); |
| 513 | + }); |
| 514 | + |
| 515 | + tg.threadUp(); |
| 516 | + }; |
| 517 | + |
| 518 | + store->rollback(vbid, 0); |
| 519 | + bgThread.join(); |
| 520 | +} |
| 521 | + |
457 | 522 | // Test written for MB-36366. With the fix removed this test failed because
|
458 | 523 | // post expel, we continued onto cursor dropping.
|
459 | 524 | // MB-36447 - unreliable test, disabling for now
|
|
0 commit comments