diff options
author | Daniel Gómez Ferro <daniel.gomezferro@mongodb.com> | 2022-02-15 14:08:54 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-10-31 17:06:54 +0000 |
commit | a58c20209f4b63c4032a4df6caa8df19bd58c22c (patch) | |
tree | 0ab8e85ae5e3c80fb09784e3652ce6056fdd0f3b | |
parent | 3962df8de03706b5bd55a4343bdc6d827756a1a1 (diff) | |
download | mongo-a58c20209f4b63c4032a4df6caa8df19bd58c22c.tar.gz |
SERVER-63585 Allow negative values in WTRecordStore::numRecords
-rw-r--r-- | src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp | 10 | ||||
-rw-r--r-- | src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp | 50 |
2 files changed, 54 insertions, 6 deletions
diff --git a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp index f48e579a426..56d10abba70 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp @@ -1000,7 +1000,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, @@ -1821,9 +1822,7 @@ public: virtual void rollback() { LOGV2_DEBUG( 22404, 3, "WiredTigerRecordStore: rolling back NumRecordsChange", "diff"_attr = -_diff); - if (_rs->_sizeInfo->numRecords.addAndFetch(-_diff) < 0) { - _rs->_sizeInfo->numRecords.store(0); - } + _rs->_sizeInfo->numRecords.addAndFetch(-_diff); } private: @@ -1841,8 +1840,7 @@ void WiredTigerRecordStore::_changeNumRecords(OperationContext* opCtx, int64_t d } opCtx->recoveryUnit()->registerChange(std::make_unique<NumRecordsChange>(this, diff)); - if (_sizeInfo->numRecords.addAndFetch(diff) < 0) - _sizeInfo->numRecords.store(0); + _sizeInfo->numRecords.addAndFetch(diff); } class WiredTigerRecordStore::DataSizeChange : public RecoveryUnit::Change { 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 c532c2ccb97..f5cdc0d8b75 100644 --- a/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp +++ b/src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp @@ -47,6 +47,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" @@ -1046,5 +1047,54 @@ TEST(WiredTigerRecordStoreTest, CursorInActiveTxnAfterSeek) { } } +// 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<RecordStore> rs(harnessHelper->newNonCappedRecordStore()); + + 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<unittest::Barrier>(2); + auto deleted = std::make_shared<unittest::Barrier>(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 |