void ReplicationCoordinatorImpl::_writeLastVoteForMyElection( LastVote lastVote, const ReplicationExecutor::CallbackArgs& cbData) { invariant(_voteRequester); invariant(!_electionWinnerDeclarer); LoseElectionDryRunGuardV1 lossGuard(this); if (cbData.status == ErrorCodes::CallbackCanceled) { return; } invariant(cbData.txn); Status status = _externalState->storeLocalLastVoteDocument(cbData.txn, lastVote); if (!status.isOK()) { error() << "failed to store LastVote document when voting for myself: " << status; return; } auto cbStatus = _replExecutor.scheduleWork( [this, lastVote](const ReplicationExecutor::CallbackArgs& cbData) { _startVoteRequester(lastVote.getTerm()); }); if (cbStatus.getStatus() == ErrorCodes::ShutdownInProgress) { return; } fassert(28768, cbStatus.getStatus()); _replExecutor.signalEvent(_electionDryRunFinishedEvent); lossGuard.dismiss(); }
void ReplicationCoordinatorImpl::_onDryRunComplete(long long originalTerm) { invariant(_voteRequester); LoseElectionDryRunGuardV1 lossGuard(this); LockGuard lk(_topoMutex); if (_topCoord->getTerm() != originalTerm) { log() << "not running for primary, we have been superceded already"; return; } const VoteRequester::Result endResult = _voteRequester->getResult(); if (endResult == VoteRequester::Result::kInsufficientVotes) { log() << "not running for primary, we received insufficient votes"; return; } else if (endResult == VoteRequester::Result::kStaleTerm) { log() << "not running for primary, we have been superceded already"; return; } else if (endResult != VoteRequester::Result::kSuccessfullyElected) { log() << "not running for primary, we received an unexpected problem"; return; } log() << "dry election run succeeded, running for election"; // Stepdown is impossible from this term update. TopologyCoordinator::UpdateTermResult updateTermResult; _updateTerm_incallback(originalTerm + 1, &updateTermResult); invariant(updateTermResult == TopologyCoordinator::UpdateTermResult::kUpdatedTerm); // Secure our vote for ourself first _topCoord->voteForMyselfV1(); // Store the vote in persistent storage. LastVote lastVote; lastVote.setTerm(originalTerm + 1); lastVote.setCandidateIndex(_selfIndex); auto cbStatus = _replExecutor.scheduleDBWork( [this, lastVote](const ReplicationExecutor::CallbackArgs& cbData) { _writeLastVoteForMyElection(lastVote, cbData); }); if (cbStatus.getStatus() == ErrorCodes::ShutdownInProgress) { return; } fassert(34421, cbStatus.getStatus()); lossGuard.dismiss(); }
StatusWith<LastVote> ReplicationCoordinatorExternalStateImpl::loadLocalLastVoteDocument( OperationContext* txn) { try { MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { BSONObj lastVoteObj; if (!Helpers::getSingleton(txn, lastVoteCollectionName, lastVoteObj)) { return StatusWith<LastVote>(ErrorCodes::NoMatchingDocument, str::stream() << "Did not find replica set lastVote document in " << lastVoteCollectionName); } LastVote lastVote; lastVote.initialize(lastVoteObj); return StatusWith<LastVote>(lastVote); } MONGO_WRITE_CONFLICT_RETRY_LOOP_END( txn, "load replica set lastVote", lastVoteCollectionName); } catch (const DBException& ex) { return StatusWith<LastVote>(ex.toStatus()); } }
Status ReplicationCoordinatorExternalStateImpl::storeLocalLastVoteDocument( OperationContext* opCtx, const LastVote& lastVote) { BSONObj lastVoteObj = lastVote.toBSON(); try { Status status = writeConflictRetry(opCtx, "save replica set lastVote", lastVoteCollectionName, [&] { Lock::DBLock dbWriteLock(opCtx, lastVoteDatabaseName, MODE_X); // 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. BSONObj result; bool exists = Helpers::getSingleton(opCtx, lastVoteCollectionName, result); if (!exists) { 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); } } return Status::OK(); }); if (!status.isOK()) { return status; } opCtx->recoveryUnit()->waitUntilDurable(); return Status::OK(); } catch (const DBException& ex) { return ex.toStatus(); } }
void ReplicationCoordinatorImpl::_writeLastVoteForMyElection( LastVote lastVote, const executor::TaskExecutor::CallbackArgs& cbData) { // storeLocalLastVoteDocument can call back in to the replication coordinator, // so _mutex must be unlocked here. However, we cannot return until we // lock it because we want to lose the election on cancel or error and // doing so requires _mutex. auto status = [&] { if (!cbData.status.isOK()) { return cbData.status; } auto opCtx = cc().makeOperationContext(); return _externalState->storeLocalLastVoteDocument(opCtx.get(), lastVote); }(); stdx::lock_guard<stdx::mutex> lk(_mutex); invariant(_voteRequester); LoseElectionDryRunGuardV1 lossGuard(this); if (status == ErrorCodes::CallbackCanceled) { return; } if (!status.isOK()) { log() << "failed to store LastVote document when voting for myself: " << status; return; } if (_topCoord->getTerm() != lastVote.getTerm()) { log() << "not running for primary, we have been superseded already while writing our last " "vote. election term: " << lastVote.getTerm() << ", current term: " << _topCoord->getTerm(); return; } _startVoteRequester_inlock(lastVote.getTerm()); _replExecutor->signalEvent(_electionDryRunFinishedEvent); lossGuard.dismiss(); }
Status ReplicationCoordinatorExternalStateImpl::storeLocalLastVoteDocument( OperationContext* txn, const LastVote& lastVote) { BSONObj lastVoteObj = lastVote.toBSON(); try { MONGO_WRITE_CONFLICT_RETRY_LOOP_BEGIN { ScopedTransaction transaction(txn, MODE_IX); Lock::DBLock dbWriteLock(txn->lockState(), lastVoteDatabaseName, MODE_X); Helpers::putSingleton(txn, lastVoteCollectionName, lastVoteObj); return Status::OK(); } MONGO_WRITE_CONFLICT_RETRY_LOOP_END( txn, "save replica set lastVote", lastVoteCollectionName); MONGO_UNREACHABLE; } catch (const DBException& ex) { return ex.toStatus(); } }
void ReplicationCoordinatorImpl::_writeLastVoteForMyElection( LastVote lastVote, const ReplicationExecutor::CallbackArgs& cbData) { invariant(_voteRequester); LoseElectionDryRunGuardV1 lossGuard(this); if (cbData.status == ErrorCodes::CallbackCanceled) { return; } invariant(cbData.txn); Status status = _externalState->storeLocalLastVoteDocument(cbData.txn, lastVote); if (!status.isOK()) { error() << "failed to store LastVote document when voting for myself: " << status; return; } _startVoteRequester(lastVote.getTerm()); _replExecutor.signalEvent(_electionDryRunFinishedEvent); lossGuard.dismiss(); }