summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Russotto <matthew.russotto@10gen.com>2019-07-08 16:07:59 -0400
committerMatthew Russotto <matthew.russotto@10gen.com>2019-07-11 10:57:42 -0400
commitf9c12ae627f07dbd622dd898bb97711b3c7f76db (patch)
tree2c4ae00be815d83096c3c66a680c6735611444ef
parente235f49d1ffaf1dab7b85daf8a3217a5a891a061 (diff)
downloadmongo-f9c12ae627f07dbd622dd898bb97711b3c7f76db.tar.gz
SERVER-42055 Only acquire a collection IX lock to write the lastVote document
(cherry picked from commit 7320a19db85c552a1c88204c4145f2cf18dfcb90)
-rw-r--r--src/mongo/db/repl/replication_coordinator_external_state.h6
-rw-r--r--src/mongo/db/repl/replication_coordinator_external_state_impl.cpp59
-rw-r--r--src/mongo/db/repl/replication_coordinator_external_state_impl.h1
-rw-r--r--src/mongo/db/repl/replication_coordinator_external_state_mock.cpp8
-rw-r--r--src/mongo/db/repl/replication_coordinator_external_state_mock.h5
-rw-r--r--src/mongo/db/repl/replication_coordinator_impl.cpp18
-rw-r--r--src/mongo/dbtests/replica_set_tests.cpp5
7 files changed, 77 insertions, 25 deletions
diff --git a/src/mongo/db/repl/replication_coordinator_external_state.h b/src/mongo/db/repl/replication_coordinator_external_state.h
index 2152cfc1f57..b6e70228cf1 100644
--- a/src/mongo/db/repl/replication_coordinator_external_state.h
+++ b/src/mongo/db/repl/replication_coordinator_external_state.h
@@ -166,6 +166,12 @@ public:
*/
virtual Status storeLocalConfigDocument(OperationContext* opCtx, const BSONObj& config) = 0;
+
+ /**
+ * Creates the collection for "lastVote" documents and initializes it, or returns an error.
+ */
+ virtual Status createLocalLastVoteCollection(OperationContext* opCtx) = 0;
+
/**
* Gets the replica set lastVote document from local storage, or returns an error.
*/
diff --git a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp
index 9a455930d43..fe92c9ebc8b 100644
--- a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp
+++ b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp
@@ -557,6 +557,37 @@ Status ReplicationCoordinatorExternalStateImpl::storeLocalConfigDocument(Operati
}
}
+Status ReplicationCoordinatorExternalStateImpl::createLocalLastVoteCollection(
+ OperationContext* opCtx) {
+ auto status = _storageInterface->createCollection(
+ opCtx, NamespaceString(lastVoteCollectionName), CollectionOptions());
+ if (!status.isOK() && status.code() != ErrorCodes::NamespaceExists) {
+ return {ErrorCodes::CannotCreateCollection,
+ str::stream() << "Failed to create local last vote collection. Ns: "
+ << lastVoteCollectionName
+ << " Error: "
+ << status.toString()};
+ }
+
+ // Make sure there's always a last vote document.
+ try {
+ writeConflictRetry(
+ opCtx, "create initial replica set lastVote", lastVoteCollectionName, [opCtx] {
+ AutoGetCollection coll(opCtx, NamespaceString(lastVoteCollectionName), MODE_X);
+ BSONObj result;
+ bool exists = Helpers::getSingleton(opCtx, lastVoteCollectionName, result);
+ if (!exists) {
+ LastVote lastVote{OpTime::kInitialTerm, -1};
+ Helpers::putSingleton(opCtx, lastVoteCollectionName, lastVote.toBSON());
+ }
+ });
+ } catch (const DBException& ex) {
+ return ex.toStatus();
+ }
+
+ return Status::OK();
+}
+
StatusWith<LastVote> ReplicationCoordinatorExternalStateImpl::loadLocalLastVoteDocument(
OperationContext* opCtx) {
try {
@@ -586,26 +617,24 @@ Status ReplicationCoordinatorExternalStateImpl::storeLocalLastVoteDocument(
// don't want to have this process interrupted due to us stepping down, since we
// want to be able to cast our vote for a new primary right away.
UninterruptibleLockGuard noInterrupt(opCtx->lockState());
- Lock::DBLock dbWriteLock(opCtx, lastVoteDatabaseName, MODE_X);
+ AutoGetCollection coll(opCtx, NamespaceString(lastVoteCollectionName), MODE_IX);
+ WriteUnitOfWork wunit(opCtx);
- // If there is no last vote document, we want to store one. Otherwise, we only want
- // to replace it if the new last vote document would have a higher term. We both
- // check the term of the current last vote document and insert the new document
- // under the DBLock to synchronize the two operations.
+ // We only want to replace the last vote document if the new last vote document
+ // would have a higher term. We check the term of the current last vote document and
+ // insert the new document in a WriteUnitOfWork to synchronize the two operations.
+ // We have already ensured at startup time that there is an old document.
BSONObj result;
bool exists = Helpers::getSingleton(opCtx, lastVoteCollectionName, result);
- if (!exists) {
+ fassert(51241, exists);
+ StatusWith<LastVote> oldLastVoteDoc = LastVote::readFromLastVote(result);
+ if (!oldLastVoteDoc.isOK()) {
+ return oldLastVoteDoc.getStatus();
+ }
+ if (lastVote.getTerm() > oldLastVoteDoc.getValue().getTerm()) {
Helpers::putSingleton(opCtx, lastVoteCollectionName, lastVoteObj);
- } else {
- StatusWith<LastVote> oldLastVoteDoc = LastVote::readFromLastVote(result);
- if (!oldLastVoteDoc.isOK()) {
- return oldLastVoteDoc.getStatus();
- }
- if (lastVote.getTerm() > oldLastVoteDoc.getValue().getTerm()) {
- Helpers::putSingleton(opCtx, lastVoteCollectionName, lastVoteObj);
- }
}
-
+ wunit.commit();
return Status::OK();
});
diff --git a/src/mongo/db/repl/replication_coordinator_external_state_impl.h b/src/mongo/db/repl/replication_coordinator_external_state_impl.h
index fdf3d3d2e38..99bc68fdf85 100644
--- a/src/mongo/db/repl/replication_coordinator_external_state_impl.h
+++ b/src/mongo/db/repl/replication_coordinator_external_state_impl.h
@@ -84,6 +84,7 @@ public:
OpTime onTransitionToPrimary(OperationContext* opCtx, bool isV1ElectionProtocol) override;
virtual void forwardSlaveProgress();
virtual bool isSelf(const HostAndPort& host, ServiceContext* service);
+ Status createLocalLastVoteCollection(OperationContext* opCtx) final;
virtual StatusWith<BSONObj> loadLocalConfigDocument(OperationContext* opCtx);
virtual Status storeLocalConfigDocument(OperationContext* opCtx, const BSONObj& config);
virtual StatusWith<LastVote> loadLocalLastVoteDocument(OperationContext* opCtx);
diff --git a/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp b/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp
index 6a3c7d97b3a..7fb3b80c553 100644
--- a/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp
+++ b/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp
@@ -133,6 +133,14 @@ void ReplicationCoordinatorExternalStateMock::setLocalConfigDocument(
_localRsConfigDocument = localConfigDocument;
}
+Status ReplicationCoordinatorExternalStateMock::createLocalLastVoteCollection(
+ OperationContext* opCtx) {
+ if (!_localRsLastVoteDocument.isOK()) {
+ setLocalLastVoteDocument(LastVote{OpTime::kInitialTerm, -1});
+ }
+ return Status::OK();
+}
+
StatusWith<LastVote> ReplicationCoordinatorExternalStateMock::loadLocalLastVoteDocument(
OperationContext* opCtx) {
return _localRsLastVoteDocument;
diff --git a/src/mongo/db/repl/replication_coordinator_external_state_mock.h b/src/mongo/db/repl/replication_coordinator_external_state_mock.h
index 009344027b5..93a82cff456 100644
--- a/src/mongo/db/repl/replication_coordinator_external_state_mock.h
+++ b/src/mongo/db/repl/replication_coordinator_external_state_mock.h
@@ -112,6 +112,11 @@ public:
void setLocalConfigDocument(const StatusWith<BSONObj>& localConfigDocument);
/**
+ * Initializes the return value for subsequent calls to loadLocalLastVoteDocument().
+ */
+ Status createLocalLastVoteCollection(OperationContext* opCtx) final;
+
+ /**
* Sets the return value for subsequent calls to loadLocalLastVoteDocument().
*/
void setLocalLastVoteDocument(const StatusWith<LastVote>& localLastVoteDocument);
diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp
index 3997add1882..15f73e214ba 100644
--- a/src/mongo/db/repl/replication_coordinator_impl.cpp
+++ b/src/mongo/db/repl/replication_coordinator_impl.cpp
@@ -462,16 +462,18 @@ bool ReplicationCoordinatorImpl::_startLoadLocalConfig(OperationContext* opCtx)
_replicationProcess->getConsistencyMarkers()->initializeMinValidDocument(opCtx);
+ fassert(51240, _externalState->createLocalLastVoteCollection(opCtx));
+
StatusWith<LastVote> lastVote = _externalState->loadLocalLastVoteDocument(opCtx);
if (!lastVote.isOK()) {
- if (lastVote.getStatus() == ErrorCodes::NoMatchingDocument) {
- log() << "Did not find local voted for document at startup.";
- } else {
- severe() << "Error loading local voted for document at startup; "
- << lastVote.getStatus();
- fassertFailedNoTrace(40367);
- }
- } else {
+ severe() << "Error loading local voted for document at startup; " << lastVote.getStatus();
+ fassertFailedNoTrace(40367);
+ }
+ if (lastVote.getValue().getTerm() == OpTime::kInitialTerm) {
+ // This log line is checked in unit tests.
+ log() << "Did not find local initialized voted for document at startup.";
+ }
+ {
stdx::lock_guard<stdx::mutex> lk(_mutex);
_topCoord->loadLastVote(lastVote.getValue());
}
diff --git a/src/mongo/dbtests/replica_set_tests.cpp b/src/mongo/dbtests/replica_set_tests.cpp
index 807314b52fe..038c3fcf24e 100644
--- a/src/mongo/dbtests/replica_set_tests.cpp
+++ b/src/mongo/dbtests/replica_set_tests.cpp
@@ -38,7 +38,7 @@
#include "mongo/db/repl/replication_coordinator_external_state_impl.h"
#include "mongo/db/repl/replication_process.h"
#include "mongo/db/repl/replication_recovery.h"
-#include "mongo/db/repl/storage_interface_mock.h"
+#include "mongo/db/repl/storage_interface_impl.h"
#include "mongo/db/service_context.h"
#include "mongo/unittest/unittest.h"
@@ -53,7 +53,7 @@ class ReplicaSetTest : public mongo::unittest::Test {
protected:
void setUp() {
auto opCtx = makeOpCtx();
- _storageInterface = stdx::make_unique<repl::StorageInterfaceMock>();
+ _storageInterface = std::make_unique<repl::StorageInterfaceImpl>();
_dropPendingCollectionReaper =
stdx::make_unique<repl::DropPendingCollectionReaper>(_storageInterface.get());
auto consistencyMarkers =
@@ -67,6 +67,7 @@ protected:
_dropPendingCollectionReaper.get(),
_storageInterface.get(),
_replicationProcess.get());
+ ASSERT_OK(_replCoordExternalState->createLocalLastVoteCollection(opCtx.get()));
}
void tearDown() {