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-02-15 14:57:57 +0000
commitbbdf42ae03fdab670dbee8d5a154156aae5342a9 (patch)
tree882cd326231f16f6dea89e085ae926e131e7162b
parent1df6ba1b92ccbc77869314dd9182c708bcd80ad4 (diff)
downloadmongo-bbdf42ae03fdab670dbee8d5a154156aae5342a9.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 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<RecordStore> 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<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