summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Mulrow <jack.mulrow@mongodb.com>2021-05-27 21:21:52 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-06-01 20:50:16 +0000
commitf5920f892b6cc0572140be6b81babf8bf4419278 (patch)
tree7af6c99325c9820fee141bd6b37f3bbf1309a7e9
parent87e10cc39920a7aff6283820686de312d4527df8 (diff)
downloadmongo-f5920f892b6cc0572140be6b81babf8bf4419278.tar.gz
SERVER-56981 Verify the blockTimestamp is higher than the timestamp of any donor writes
-rw-r--r--src/mongo/db/op_observer_impl.cpp4
-rw-r--r--src/mongo/db/repl/oplog.cpp2
-rw-r--r--src/mongo/db/repl/tenant_migration_access_blocker.h2
-rw-r--r--src/mongo/db/repl/tenant_migration_access_blocker_util.cpp4
-rw-r--r--src/mongo/db/repl/tenant_migration_access_blocker_util.h2
-rw-r--r--src/mongo/db/repl/tenant_migration_donor_access_blocker.cpp12
-rw-r--r--src/mongo/db/repl/tenant_migration_donor_access_blocker.h3
-rw-r--r--src/mongo/db/repl/tenant_migration_recipient_access_blocker.cpp2
-rw-r--r--src/mongo/db/repl/tenant_migration_recipient_access_blocker.h2
-rw-r--r--src/mongo/db/repl/tenant_migration_recipient_access_blocker_test.cpp2
10 files changed, 23 insertions, 12 deletions
diff --git a/src/mongo/db/op_observer_impl.cpp b/src/mongo/db/op_observer_impl.cpp
index d1267aa5768..72594583d3d 100644
--- a/src/mongo/db/op_observer_impl.cpp
+++ b/src/mongo/db/op_observer_impl.cpp
@@ -1421,8 +1421,8 @@ void OpObserverImpl::onUnpreparedTransactionCommit(OperationContext* opCtx,
// Throw TenantMigrationConflict error if the database for the transaction statements is being
// migrated. We only need check the namespace of the first statement since a transaction's
// statements must all be for the same tenant.
- tenant_migration_access_blocker::checkIfCanWriteOrThrow(opCtx,
- statements->begin()->getNss().db());
+ tenant_migration_access_blocker::checkIfCanWriteOrThrow(
+ opCtx, statements->begin()->getNss().db(), oplogSlots.back().getTimestamp());
if (MONGO_unlikely(hangAndFailUnpreparedCommitAfterReservingOplogSlot.shouldFail())) {
hangAndFailUnpreparedCommitAfterReservingOplogSlot.pauseWhileSet(opCtx);
diff --git a/src/mongo/db/repl/oplog.cpp b/src/mongo/db/repl/oplog.cpp
index 4879330a958..dce50640e27 100644
--- a/src/mongo/db/repl/oplog.cpp
+++ b/src/mongo/db/repl/oplog.cpp
@@ -334,7 +334,7 @@ void _logOpsInner(OperationContext* opCtx,
// index build on the donor after the blockTimestamp, plus if an index build fails to commit due
// to TenantMigrationConflict, we need to be able to abort the index build and clean up.
if (repl::feature_flags::gTenantMigrations.isEnabledAndIgnoreFCV() && !isAbortIndexBuild) {
- tenant_migration_access_blocker::checkIfCanWriteOrThrow(opCtx, nss.db());
+ tenant_migration_access_blocker::checkIfCanWriteOrThrow(opCtx, nss.db(), timestamps.back());
}
Status result = oplogCollection->insertDocumentsForOplog(opCtx, records, timestamps);
diff --git a/src/mongo/db/repl/tenant_migration_access_blocker.h b/src/mongo/db/repl/tenant_migration_access_blocker.h
index 06a78bb3584..48c0179b8be 100644
--- a/src/mongo/db/repl/tenant_migration_access_blocker.h
+++ b/src/mongo/db/repl/tenant_migration_access_blocker.h
@@ -56,7 +56,7 @@ public:
// Called by all writes and reads against the database.
//
- virtual Status checkIfCanWrite() = 0;
+ virtual Status checkIfCanWrite(Timestamp writeTs) = 0;
virtual Status waitUntilCommittedOrAborted(OperationContext* opCtx) = 0;
diff --git a/src/mongo/db/repl/tenant_migration_access_blocker_util.cpp b/src/mongo/db/repl/tenant_migration_access_blocker_util.cpp
index 54985fb0afc..d766bb74341 100644
--- a/src/mongo/db/repl/tenant_migration_access_blocker_util.cpp
+++ b/src/mongo/db/repl/tenant_migration_access_blocker_util.cpp
@@ -243,14 +243,14 @@ void checkIfLinearizableReadWasAllowedOrThrow(OperationContext* opCtx, StringDat
}
}
-void checkIfCanWriteOrThrow(OperationContext* opCtx, StringData dbName) {
+void checkIfCanWriteOrThrow(OperationContext* opCtx, StringData dbName, Timestamp writeTs) {
// The migration protocol guarantees the recipient will not get writes until the migration
// is committed.
auto mtab = TenantMigrationAccessBlockerRegistry::get(opCtx->getServiceContext())
.getTenantMigrationAccessBlockerForDbName(dbName, MtabType::kDonor);
if (mtab) {
- auto status = mtab->checkIfCanWrite();
+ auto status = mtab->checkIfCanWrite(writeTs);
mtab->recordTenantMigrationError(status);
uassertStatusOK(status);
}
diff --git a/src/mongo/db/repl/tenant_migration_access_blocker_util.h b/src/mongo/db/repl/tenant_migration_access_blocker_util.h
index 530632d1b1b..6ff5d3405a3 100644
--- a/src/mongo/db/repl/tenant_migration_access_blocker_util.h
+++ b/src/mongo/db/repl/tenant_migration_access_blocker_util.h
@@ -72,7 +72,7 @@ void checkIfLinearizableReadWasAllowedOrThrow(OperationContext* opCtx, StringDat
* Throws TenantMigrationConflict if the database is being migrated and the migration is in the
* blocking state. Throws TenantMigrationCommitted if it is in committed.
*/
-void checkIfCanWriteOrThrow(OperationContext* opCtx, StringData dbName);
+void checkIfCanWriteOrThrow(OperationContext* opCtx, StringData dbName, Timestamp writeTs);
/**
* Returns TenantMigrationConflict if the database is being migrated (even if migration is not yet
diff --git a/src/mongo/db/repl/tenant_migration_donor_access_blocker.cpp b/src/mongo/db/repl/tenant_migration_donor_access_blocker.cpp
index 41f6be0c327..0b25fb1a9d3 100644
--- a/src/mongo/db/repl/tenant_migration_donor_access_blocker.cpp
+++ b/src/mongo/db/repl/tenant_migration_donor_access_blocker.cpp
@@ -80,11 +80,14 @@ TenantMigrationDonorAccessBlocker::TenantMigrationDonorAccessBlocker(
.getOrCreateBlockedOperationsExecutor();
}
-Status TenantMigrationDonorAccessBlocker::checkIfCanWrite() {
+Status TenantMigrationDonorAccessBlocker::checkIfCanWrite(Timestamp writeTs) {
stdx::lock_guard<Latch> lg(_mutex);
switch (_state.getState()) {
case BlockerState::State::kAllow:
+ // As a sanity check, we track the highest allowed write timestamp to ensure no
+ // writes are allowed with a timestamp higher than the block timestamp.
+ _highestAllowedWriteTimestamp = std::max(writeTs, _highestAllowedWriteTimestamp);
case BlockerState::State::kAborted:
return Status::OK();
case BlockerState::State::kBlockWrites:
@@ -234,6 +237,13 @@ void TenantMigrationDonorAccessBlocker::startBlockingReadsAfter(const Timestamp&
invariant(!_commitOpTime);
invariant(!_abortOpTime);
+ invariant(
+ blockTimestamp > _highestAllowedWriteTimestamp,
+ str::stream() << "The block timestamp must be higher than the timestamp of any allowed "
+ "write, blockTimestamp: "
+ << blockTimestamp.toString() << ", highestAllowedWriteTimestamp: "
+ << _highestAllowedWriteTimestamp.toString());
+
_state.transitionTo(BlockerState::State::kBlockWritesAndReads);
_blockTimestamp = blockTimestamp;
}
diff --git a/src/mongo/db/repl/tenant_migration_donor_access_blocker.h b/src/mongo/db/repl/tenant_migration_donor_access_blocker.h
index b11a1a56b9a..51bf14c0bd0 100644
--- a/src/mongo/db/repl/tenant_migration_donor_access_blocker.h
+++ b/src/mongo/db/repl/tenant_migration_donor_access_blocker.h
@@ -187,7 +187,7 @@ public:
// Called by all writes and reads against the database.
//
- Status checkIfCanWrite() final;
+ Status checkIfCanWrite(Timestamp writeTs) final;
Status waitUntilCommittedOrAborted(OperationContext* opCtx) final;
Status checkIfLinearizableReadWasAllowed(OperationContext* opCtx) final;
@@ -331,6 +331,7 @@ private:
BlockerState _state;
+ Timestamp _highestAllowedWriteTimestamp;
boost::optional<Timestamp> _blockTimestamp;
boost::optional<repl::OpTime> _commitOpTime;
boost::optional<repl::OpTime> _abortOpTime;
diff --git a/src/mongo/db/repl/tenant_migration_recipient_access_blocker.cpp b/src/mongo/db/repl/tenant_migration_recipient_access_blocker.cpp
index 3bd49d79934..6428afa32ca 100644
--- a/src/mongo/db/repl/tenant_migration_recipient_access_blocker.cpp
+++ b/src/mongo/db/repl/tenant_migration_recipient_access_blocker.cpp
@@ -65,7 +65,7 @@ TenantMigrationRecipientAccessBlocker::TenantMigrationRecipientAccessBlocker(
.getOrCreateBlockedOperationsExecutor();
}
-Status TenantMigrationRecipientAccessBlocker::checkIfCanWrite() {
+Status TenantMigrationRecipientAccessBlocker::checkIfCanWrite(Timestamp writeTs) {
// This is guaranteed by the migration protocol. The recipient will not get any writes until the
// migration is committed on the donor.
return Status::OK();
diff --git a/src/mongo/db/repl/tenant_migration_recipient_access_blocker.h b/src/mongo/db/repl/tenant_migration_recipient_access_blocker.h
index 4b17dfdac88..fb3050f8b27 100644
--- a/src/mongo/db/repl/tenant_migration_recipient_access_blocker.h
+++ b/src/mongo/db/repl/tenant_migration_recipient_access_blocker.h
@@ -81,7 +81,7 @@ public:
// Called by all writes and reads against the database.
//
- Status checkIfCanWrite() final;
+ Status checkIfCanWrite(Timestamp writeTs) final;
Status waitUntilCommittedOrAborted(OperationContext* opCtx) final;
Status checkIfLinearizableReadWasAllowed(OperationContext* opCtx) final;
diff --git a/src/mongo/db/repl/tenant_migration_recipient_access_blocker_test.cpp b/src/mongo/db/repl/tenant_migration_recipient_access_blocker_test.cpp
index 4b708cd25fe..ebdea752a36 100644
--- a/src/mongo/db/repl/tenant_migration_recipient_access_blocker_test.cpp
+++ b/src/mongo/db/repl/tenant_migration_recipient_access_blocker_test.cpp
@@ -125,7 +125,7 @@ TEST_F(TenantMigrationRecipientAccessBlockerTest, NoopFunctions) {
getServiceContext(), getMigrationId(), getTenantId(), getDonorConnectionString());
// These functions are noop functions and should not throw even in reject state.
- ASSERT_OK(mtab.checkIfCanWrite());
+ ASSERT_OK(mtab.checkIfCanWrite(Timestamp()));
ASSERT_OK(mtab.checkIfLinearizableReadWasAllowed(opCtx()));
ASSERT_OK(mtab.checkIfCanBuildIndex());
}