summaryrefslogtreecommitdiff
path: root/src/mongo/db/concurrency/d_concurrency_test.cpp
diff options
context:
space:
mode:
authorJustin Seyster <justin.seyster@mongodb.com>2018-03-30 15:25:47 -0400
committerJustin Seyster <justin.seyster@mongodb.com>2018-04-12 16:05:53 -0400
commitbc19d43fdc4aab85264def96f638128c0ddb8483 (patch)
tree09b5a9a15982edd4998d4665d6c67f8fbd7c83e7 /src/mongo/db/concurrency/d_concurrency_test.cpp
parentc6620182aebd1b62d31879ce4d9456ff197aea22 (diff)
downloadmongo-bc19d43fdc4aab85264def96f638128c0ddb8483.tar.gz
SERVER-27534 All writing operations must fail if the term changes.
The description of this SERVER ticket describes a nasty race that can occur if elections happen inbetween two batches during a large update. (The included test confirms that the race is possible.) To fix this, we want to check the operation context for interrupts with each batch, and we need to make sure the check happens _after_ the collection lock gets taken and before the batch inserts/updates/deletes execute. A recent change to locking gives us almost exactly this for free: if a collection lock has to wait, it throws an exception when the operation context is interrupted, ending the operation. If the lock doesn't wait, though, there is no check. This patch adds that check in. Acquiring a lock now always throws if the operation context is interrupted, which closes the race window in this bug.
Diffstat (limited to 'src/mongo/db/concurrency/d_concurrency_test.cpp')
-rw-r--r--src/mongo/db/concurrency/d_concurrency_test.cpp74
1 files changed, 74 insertions, 0 deletions
diff --git a/src/mongo/db/concurrency/d_concurrency_test.cpp b/src/mongo/db/concurrency/d_concurrency_test.cpp
index 902fe3c2fa9..4058836c478 100644
--- a/src/mongo/db/concurrency/d_concurrency_test.cpp
+++ b/src/mongo/db/concurrency/d_concurrency_test.cpp
@@ -1176,6 +1176,80 @@ TEST_F(DConcurrencyTestFixture, TicketReacquireCanBeInterrupted) {
ASSERT_THROWS_CODE(result.get(), AssertionException, ErrorCodes::Interrupted);
}
+TEST_F(DConcurrencyTestFixture, GlobalLockInInterruptedContextThrowsEvenWhenUncontested) {
+ auto clients = makeKClientsWithLockers<DefaultLockerImpl>(1);
+ auto opCtx = clients[0].second.get();
+
+ opCtx->markKilled();
+
+ boost::optional<Lock::GlobalRead> globalReadLock;
+ ASSERT_THROWS_CODE(
+ globalReadLock.emplace(opCtx, Date_t::now()), AssertionException, ErrorCodes::Interrupted);
+}
+
+TEST_F(DConcurrencyTestFixture, GlobalLockInInterruptedContextThrowsEvenAcquiringRecursively) {
+ auto clients = makeKClientsWithLockers<DefaultLockerImpl>(1);
+ auto opCtx = clients[0].second.get();
+
+ Lock::GlobalWrite globalWriteLock(opCtx, Date_t::now());
+
+ opCtx->markKilled();
+
+ {
+ boost::optional<Lock::GlobalWrite> recursiveGlobalWriteLock;
+ ASSERT_THROWS_CODE(recursiveGlobalWriteLock.emplace(opCtx, Date_t::now()),
+ AssertionException,
+ ErrorCodes::Interrupted);
+ }
+}
+
+TEST_F(DConcurrencyTestFixture, GlobalLockInInterruptedContextRespectsUninterruptibleGuard) {
+ auto clients = makeKClientsWithLockers<DefaultLockerImpl>(1);
+ auto opCtx = clients[0].second.get();
+
+ opCtx->markKilled();
+
+ UninterruptibleLockGuard noInterrupt(opCtx->lockState());
+ Lock::GlobalRead globalReadLock(opCtx, Date_t::now()); // Does not throw.
+}
+
+TEST_F(DConcurrencyTestFixture, DBLockInInterruptedContextThrowsEvenWhenUncontested) {
+ auto clients = makeKClientsWithLockers<DefaultLockerImpl>(1);
+ auto opCtx = clients[0].second.get();
+
+ opCtx->markKilled();
+
+ boost::optional<Lock::DBLock> dbWriteLock;
+ ASSERT_THROWS_CODE(
+ dbWriteLock.emplace(opCtx, "db", MODE_IX), AssertionException, ErrorCodes::Interrupted);
+}
+
+TEST_F(DConcurrencyTestFixture, DBLockInInterruptedContextThrowsEvenWhenAcquiringRecursively) {
+ auto clients = makeKClientsWithLockers<DefaultLockerImpl>(1);
+ auto opCtx = clients[0].second.get();
+
+ Lock::DBLock dbWriteLock(opCtx, "db", MODE_X);
+
+ opCtx->markKilled();
+
+ {
+ boost::optional<Lock::DBLock> recursiveDBWriteLock;
+ ASSERT_THROWS_CODE(recursiveDBWriteLock.emplace(opCtx, "db", MODE_X),
+ AssertionException,
+ ErrorCodes::Interrupted);
+ }
+}
+
+TEST_F(DConcurrencyTestFixture, DBLockInInterruptedContextRespectsUninterruptibleGuard) {
+ auto clients = makeKClientsWithLockers<DefaultLockerImpl>(1);
+ auto opCtx = clients[0].second.get();
+
+ opCtx->markKilled();
+
+ UninterruptibleLockGuard noInterrupt(opCtx->lockState());
+ Lock::DBLock dbWriteLock(opCtx, "db", MODE_X); // Does not throw.
+}
+
TEST_F(DConcurrencyTestFixture, DBLockTimeout) {
auto clientOpctxPairs = makeKClientsWithLockers<DefaultLockerImpl>(2);
auto opctx1 = clientOpctxPairs[0].second.get();