diff options
author | Matthew Russotto <matthew.russotto@10gen.com> | 2019-08-05 14:25:53 -0400 |
---|---|---|
committer | Matthew Russotto <matthew.russotto@10gen.com> | 2019-08-05 17:24:11 -0400 |
commit | 21fee500b5eb2aa0e2125527d616e59b4392ac7b (patch) | |
tree | 0976276b7a9276c5d819538652228d9d9729be90 /src | |
parent | a8f80c31ced63a9ab42a146f35b64d5a0b0607eb (diff) | |
download | mongo-21fee500b5eb2aa0e2125527d616e59b4392ac7b.tar.gz |
SERVER-42055 Only acquire a collection IX lock to write the lastVote document
Backported error code CannotCreateCollection on top of cherry pick.
(cherry picked from commit 7320a19db85c552a1c88204c4145f2cf18dfcb90)
Diffstat (limited to 'src')
9 files changed, 79 insertions, 25 deletions
diff --git a/src/mongo/base/error_codes.err b/src/mongo/base/error_codes.err index dc86e388698..ec32feb946f 100644 --- a/src/mongo/base/error_codes.err +++ b/src/mongo/base/error_codes.err @@ -233,6 +233,7 @@ error_code("MaxSubPipelineDepthExceeded", 232) error_code("TooManyDocumentSequences", 233) error_code("RetryChangeStream", 234) error_code("DNSRecordTypeMismatch", 240) +error_code("CannotCreateCollection", 242) error_code("BrokenPromise", 245) error_code("ProducerConsumerQueueBatchTooLarge", 247) error_code("ProducerConsumerQueueEndClosed", 248) diff --git a/src/mongo/db/repl/replication_coordinator_external_state.h b/src/mongo/db/repl/replication_coordinator_external_state.h index 5ff52e82683..aaebae3d3b9 100644 --- a/src/mongo/db/repl/replication_coordinator_external_state.h +++ b/src/mongo/db/repl/replication_coordinator_external_state.h @@ -190,6 +190,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 048f873b812..6921d0e9839 100644 --- a/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_external_state_impl.cpp @@ -564,6 +564,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 { @@ -589,26 +620,24 @@ Status ReplicationCoordinatorExternalStateImpl::storeLocalLastVoteDocument( try { Status status = writeConflictRetry(opCtx, "save replica set lastVote", lastVoteCollectionName, [&] { - 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 87bc657844f..3e6109e5861 100644 --- a/src/mongo/db/repl/replication_coordinator_external_state_impl.h +++ b/src/mongo/db/repl/replication_coordinator_external_state_impl.h @@ -88,6 +88,7 @@ public: virtual void forwardSlaveProgress(); virtual OID ensureMe(OperationContext* opCtx); 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 b40eee1b04a..8e869b11d83 100644 --- a/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp +++ b/src/mongo/db/repl/replication_coordinator_external_state_mock.cpp @@ -142,6 +142,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 af4029e8188..3ecb180e0c5 100644 --- a/src/mongo/db/repl/replication_coordinator_external_state_mock.h +++ b/src/mongo/db/repl/replication_coordinator_external_state_mock.h @@ -122,6 +122,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 009d07ddbbb..260a7055abb 100644 --- a/src/mongo/db/repl/replication_coordinator_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl.cpp @@ -458,16 +458,18 @@ void ReplicationCoordinatorImpl::appendConnectionStats(executor::ConnectionPoolS 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/SConscript b/src/mongo/dbtests/SConscript index 156d91111c0..f40bf0a4bba 100644 --- a/src/mongo/dbtests/SConscript +++ b/src/mongo/dbtests/SConscript @@ -132,6 +132,7 @@ dbtest = env.Program( "$BUILD_DIR/mongo/db/repl/repl_coordinator_global", "$BUILD_DIR/mongo/db/repl/replication_consistency_markers_impl", "$BUILD_DIR/mongo/db/repl/replmocks", + "$BUILD_DIR/mongo/db/repl/storage_interface_impl", "$BUILD_DIR/mongo/db/serveronly", "$BUILD_DIR/mongo/db/sessions_collection_standalone", "$BUILD_DIR/mongo/db/storage/mmap_v1/paths", 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() { |