diff options
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() { |