summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Gómez Ferro <daniel.gomezferro@mongodb.com>2022-02-15 14:08:54 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-10-31 17:06:54 +0000
commita58c20209f4b63c4032a4df6caa8df19bd58c22c (patch)
tree0ab8e85ae5e3c80fb09784e3652ce6056fdd0f3b
parent3962df8de03706b5bd55a4343bdc6d827756a1a1 (diff)
downloadmongo-a58c20209f4b63c4032a4df6caa8df19bd58c22c.tar.gz
SERVER-63585 Allow negative values in WTRecordStore::numRecords
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp10
-rw-r--r--src/mongo/db/storage/wiredtiger/wiredtiger_record_store_test.cpp50
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