diff options
Diffstat (limited to 'src/mongo/db/timeseries/bucket_catalog_test.cpp')
| -rw-r--r-- | src/mongo/db/timeseries/bucket_catalog_test.cpp | 185 |
1 files changed, 35 insertions, 150 deletions
diff --git a/src/mongo/db/timeseries/bucket_catalog_test.cpp b/src/mongo/db/timeseries/bucket_catalog_test.cpp index 99ce87d5b1b..b39b1065b89 100644 --- a/src/mongo/db/timeseries/bucket_catalog_test.cpp +++ b/src/mongo/db/timeseries/bucket_catalog_test.cpp @@ -43,13 +43,17 @@ namespace mongo { namespace { class BucketCatalogTest : public CatalogTestFixture { protected: - class RunBackgroundTaskAndWaitForFailpoint { + class Task { + AtomicWord<bool> _running{false}; + stdx::packaged_task<void()> _task; + stdx::future<void> _future; stdx::thread _taskThread; public: - RunBackgroundTaskAndWaitForFailpoint(const std::string& failpointName, - std::function<void()>&& fn); - ~RunBackgroundTaskAndWaitForFailpoint(); + Task(std::function<void()>&& fn); + ~Task(); + + const stdx::future<void>& future(); }; void setUp() override; @@ -92,6 +96,25 @@ protected: BSONObj _makeTimeseriesOptionsForCreate() const override; }; +BucketCatalogTest::Task::Task(std::function<void()>&& fn) + : _task{[this, fn = std::move(fn)]() { + _running.store(true); + fn(); + }}, + _future{_task.get_future()}, + _taskThread{std::move(_task)} { + while (!_running.load()) { + stdx::this_thread::yield(); + } +} +BucketCatalogTest::Task::~Task() { + _taskThread.join(); +} + +const stdx::future<void>& BucketCatalogTest::Task::future() { + return _future; +} + void BucketCatalogTest::setUp() { CatalogTestFixture::setUp(); @@ -106,23 +129,6 @@ void BucketCatalogTest::setUp() { } } -BucketCatalogTest::RunBackgroundTaskAndWaitForFailpoint::RunBackgroundTaskAndWaitForFailpoint( - const std::string& failpointName, std::function<void()>&& fn) { - auto fp = globalFailPointRegistry().find(failpointName); - auto timesEntered = fp->setMode(FailPoint::alwaysOn, 0); - - // Start background job. - _taskThread = stdx::thread(std::move(fn)); - - // Once we hit the failpoint once, turn it off. - fp->waitForTimesEntered(timesEntered + 1); - fp->setMode(FailPoint::off, 0); -} - -BucketCatalogTest::RunBackgroundTaskAndWaitForFailpoint::~RunBackgroundTaskAndWaitForFailpoint() { - _taskThread.join(); -} - std::pair<ServiceContext::UniqueClient, ServiceContext::UniqueOperationContext> BucketCatalogTest::_makeOperationContext() { auto client = getServiceContext()->makeClient("BucketCatalogTest"); @@ -889,137 +895,16 @@ TEST_F(BucketCatalogTest, CannotConcurrentlyCommitBatchesForSameBucket) { // Batch 2 will not be able to commit until batch 1 has finished. ASSERT_OK(_bucketCatalog->prepareCommit(batch1)); + auto task = Task{[&]() { ASSERT_OK(_bucketCatalog->prepareCommit(batch2)); }}; + // Add a little extra wait to make sure prepareCommit actually gets to the blocking point. + stdx::this_thread::sleep_for(stdx::chrono::milliseconds(10)); + ASSERT(task.future().valid()); + ASSERT(stdx::future_status::timeout == task.future().wait_for(stdx::chrono::microseconds(1))) + << "prepareCommit finished before expected"; - { - auto task = RunBackgroundTaskAndWaitForFailpoint{ - "hangWaitingForConflictingPreparedBatch", - [&]() { ASSERT_OK(_bucketCatalog->prepareCommit(batch2)); }}; - - // Finish the first batch. - _bucketCatalog->finish(batch1, {}); - ASSERT(batch1->finished()); - } - - _bucketCatalog->finish(batch2, {}); - ASSERT(batch2->finished()); -} - -TEST_F(BucketCatalogTest, AbortingBatchEnsuresBucketIsEventuallyClosed) { - auto batch1 = _bucketCatalog - ->insert(_opCtx, - _ns1, - _getCollator(_ns1), - _getTimeseriesOptions(_ns1), - BSON(_timeField << Date_t::now()), - BucketCatalog::CombineWithInsertsFromOtherClients::kDisallow) - .getValue() - .batch; - - auto batch2 = _bucketCatalog - ->insert(_makeOperationContext().second.get(), - _ns1, - _getCollator(_ns1), - _getTimeseriesOptions(_ns1), - BSON(_timeField << Date_t::now()), - BucketCatalog::CombineWithInsertsFromOtherClients::kDisallow) - .getValue() - .batch; - auto batch3 = _bucketCatalog - ->insert(_makeOperationContext().second.get(), - _ns1, - _getCollator(_ns1), - _getTimeseriesOptions(_ns1), - BSON(_timeField << Date_t::now()), - BucketCatalog::CombineWithInsertsFromOtherClients::kDisallow) - .getValue() - .batch; - ASSERT_EQ(batch1->bucket().id, batch2->bucket().id); - ASSERT_EQ(batch1->bucket().id, batch3->bucket().id); - - ASSERT(batch1->claimCommitRights()); - ASSERT(batch2->claimCommitRights()); - ASSERT(batch3->claimCommitRights()); - - // Batch 2 will not be able to commit until batch 1 has finished. - ASSERT_OK(_bucketCatalog->prepareCommit(batch1)); - - { - auto task = RunBackgroundTaskAndWaitForFailpoint{ - "hangWaitingForConflictingPreparedBatch", - [&]() { ASSERT_NOT_OK(_bucketCatalog->prepareCommit(batch2)); }}; - - // If we abort the third batch, it should abort the second one too, as it isn't prepared. - // However, since the first batch is prepared, we can't abort it or clean up the bucket. We - // can then finish the first batch, which will allow the second batch to proceed. It should - // recognize it has been aborted and clean up the bucket. - _bucketCatalog->abort(batch3, Status{ErrorCodes::TimeseriesBucketCleared, "cleared"}); - _bucketCatalog->finish(batch1, {}); - ASSERT(batch1->finished()); - } - // Wait for the batch 2 task to finish preparing commit. Since batch 1 finished, batch 2 should - // be unblocked. Note that after aborting batch 3, batch 2 was not in a prepared state, so we - // expect the prepareCommit() call to fail. - ASSERT(batch2->finished()); - - // Make sure a new batch ends up in a new bucket. - auto batch4 = _bucketCatalog - ->insert(_opCtx, - _ns1, - _getCollator(_ns1), - _getTimeseriesOptions(_ns1), - BSON(_timeField << Date_t::now()), - BucketCatalog::CombineWithInsertsFromOtherClients::kDisallow) - .getValue() - .batch; - ASSERT_NE(batch2->bucket().id, batch4->bucket().id); -} - -TEST_F(BucketCatalogTest, AbortingBatchEnsuresNewInsertsGoToNewBucket) { - auto batch1 = _bucketCatalog - ->insert(_opCtx, - _ns1, - _getCollator(_ns1), - _getTimeseriesOptions(_ns1), - BSON(_timeField << Date_t::now()), - BucketCatalog::CombineWithInsertsFromOtherClients::kDisallow) - .getValue() - .batch; - - auto batch2 = _bucketCatalog - ->insert(_makeOperationContext().second.get(), - _ns1, - _getCollator(_ns1), - _getTimeseriesOptions(_ns1), - BSON(_timeField << Date_t::now()), - BucketCatalog::CombineWithInsertsFromOtherClients::kDisallow) - .getValue() - .batch; - - // Batch 1 and 2 use the same bucket. - ASSERT_EQ(batch1->bucket().id, batch2->bucket().id); - ASSERT(batch1->claimCommitRights()); - ASSERT(batch2->claimCommitRights()); - ASSERT_OK(_bucketCatalog->prepareCommit(batch1)); - - // Batch 1 will be in a prepared state now. Abort the second batch so that bucket 1 will be - // closed after batch 1 finishes. - _bucketCatalog->abort(batch2, Status{ErrorCodes::TimeseriesBucketCleared, "cleared"}); _bucketCatalog->finish(batch1, {}); - ASSERT(batch1->finished()); - ASSERT(batch2->finished()); - - // Ensure a batch started after batch 2 aborts, does not insert future measurements into the - // aborted batch/bucket. - auto batch3 = _bucketCatalog - ->insert(_opCtx, - _ns1, - _getCollator(_ns1), - _getTimeseriesOptions(_ns1), - BSON(_timeField << Date_t::now()), - BucketCatalog::CombineWithInsertsFromOtherClients::kDisallow) - .getValue() - .batch; - ASSERT_NE(batch1->bucket().id, batch3->bucket().id); + task.future().wait(); + _bucketCatalog->finish(batch2, {}); } TEST_F(BucketCatalogTest, DuplicateNewFieldNamesAcrossConcurrentBatches) { |
