summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Russotto <matthew.russotto@10gen.com>2019-08-05 14:25:53 -0400
committerMatthew Russotto <matthew.russotto@10gen.com>2019-08-05 17:24:11 -0400
commit21fee500b5eb2aa0e2125527d616e59b4392ac7b (patch)
tree0976276b7a9276c5d819538652228d9d9729be90
parenta8f80c31ced63a9ab42a146f35b64d5a0b0607eb (diff)
downloadmongo-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)
-rw-r--r--src/mongo/base/error_codes.err1
-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/SConscript1
-rw-r--r--src/mongo/dbtests/replica_set_tests.cpp5
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() {