From bbdf42ae03fdab670dbee8d5a154156aae5342a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20G=C3=B3mez=20Ferro?= Date: Tue, 15 Feb 2022 14:08:54 +0000 Subject: SERVER-63585 Allow negative values in WTRecordStore::numRecords --- .../storage/wiredtiger/wiredtiger_record_store.cpp | 10 ++--- .../wiredtiger/wiredtiger_record_store_test.cpp | 50 ++++++++++++++++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) (limited to 'src') diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp index 2f17cb04310..bb92b0c88c0 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp @@ -1050,7 +1050,8 @@ long long WiredTigerRecordStore::dataSize(OperationContext* opCtx) const { } long long WiredTigerRecordStore::numRecords(OperationContext* opCtx) const { - return _sizeInfo->numRecords.load(); + auto numRecords = _sizeInfo->numRecords.load(); + return numRecords > 0 ? numRecords : 0; } int64_t WiredTigerRecordStore::storageSize(OperationContext* opCtx, @@ -1966,12 +1967,9 @@ void WiredTigerRecordStore::_changeNumRecords(OperationContext* opCtx, int64_t d opCtx->recoveryUnit()->onRollback([this, diff]() { LOGV2_DEBUG( 22404, 3, "WiredTigerRecordStore: rolling back NumRecordsChange", "diff"_attr = -diff); - if (_sizeInfo->numRecords.addAndFetch(-diff) < 0) { - _sizeInfo->numRecords.store(0); - } + _sizeInfo->numRecords.addAndFetch(-diff); }); - if (_sizeInfo->numRecords.addAndFetch(diff) < 0) - _sizeInfo->numRecords.store(0); + _sizeInfo->numRecords.addAndFetch(diff); } void WiredTigerRecordStore::_increaseDataSize(OperationContext* opCtx, int64_t amount) { diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp index 9d40f13b878..c02a8ca55c2 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp @@ -48,6 +48,7 @@ #include "mongo/db/storage/wiredtiger/wiredtiger_recovery_unit.h" #include "mongo/db/storage/wiredtiger/wiredtiger_size_storer.h" #include "mongo/db/storage/wiredtiger/wiredtiger_util.h" +#include "mongo/unittest/barrier.h" #include "mongo/unittest/temp_dir.h" #include "mongo/unittest/unittest.h" #include "mongo/util/fail_point.h" @@ -1141,5 +1142,54 @@ TEST(WiredTigerRecordStoreTest, ClusteredRecordStore) { ASSERT_EQ(0, memcmp(dataUpdated, rd.data(), strlen(dataUpdated))); } +// Make sure numRecords is accurate after a delete rolls back and some other transaction deletes the +// same rows before we have a chance of patching up the metadata. +TEST(WiredTigerRecordStoreTest, NumRecordsAccurateAfterRollbackWithDelete) { + const auto harnessHelper(newRecordStoreHarnessHelper()); + unique_ptr rs(harnessHelper->newRecordStore()); + + RecordId rid; // This record will be deleted by two transactions. + + ServiceContext::UniqueOperationContext ctx(harnessHelper->newOperationContext()); + { + WriteUnitOfWork uow(ctx.get()); + rid = rs->insertRecord(ctx.get(), "a", 2, Timestamp()).getValue(); + uow.commit(); + } + + ASSERT_EQ(1, rs->numRecords(ctx.get())); + + WriteUnitOfWork uow(ctx.get()); + + auto aborted = std::make_shared(2); + auto deleted = std::make_shared(2); + + // This thread will delete the record and then rollback. We'll block the roll back process after + // rolling back the WT transaction and before running the rest of the registered changes, + // allowing the main thread to delete the same rows again. + stdx::thread abortedThread([&harnessHelper, &rs, &rid, aborted, deleted]() { + auto client = harnessHelper->serviceContext()->makeClient("c1"); + auto ctx = harnessHelper->newOperationContext(client.get()); + WriteUnitOfWork txn(ctx.get()); + // Registered changes are executed in reverse order. + rs->deleteRecord(ctx.get(), rid); + ctx.get()->recoveryUnit()->onRollback([&]() { deleted->countDownAndWait(); }); + ctx.get()->recoveryUnit()->onRollback([&]() { aborted->countDownAndWait(); }); + }); + + // Wait for the other thread to abort. + aborted->countDownAndWait(); + + rs->deleteRecord(ctx.get(), rid); + + // Notify the other thread we have deleted, let it complete the rollback. + deleted->countDownAndWait(); + + uow.commit(); + + abortedThread.join(); + ASSERT_EQ(0, rs->numRecords(ctx.get())); +} + } // namespace } // namespace mongo -- cgit v1.2.1