diff options
Diffstat (limited to 'src/mongo/db/repl/replication_coordinator_impl.cpp')
-rw-r--r-- | src/mongo/db/repl/replication_coordinator_impl.cpp | 71 |
1 files changed, 67 insertions, 4 deletions
diff --git a/src/mongo/db/repl/replication_coordinator_impl.cpp b/src/mongo/db/repl/replication_coordinator_impl.cpp index e78e9c645af..a13520f2b7c 100644 --- a/src/mongo/db/repl/replication_coordinator_impl.cpp +++ b/src/mongo/db/repl/replication_coordinator_impl.cpp @@ -3187,9 +3187,8 @@ Status ReplicationCoordinatorImpl::processReplSetReconfig(OperationContext* opCt if (newMem.isArbiter()) { continue; } - - const int newMemId = newMem.getId().getData(); - const auto oldMem = oldConfig.findMemberByID(newMemId); + const auto newMemId = newMem.getId(); + const auto oldMem = oldConfig.findMemberByID(newMemId.getData()); const bool isNewVotingMember = (oldMem == nullptr && newMem.isVoter()); const bool isCurrentlyNewlyAdded = @@ -3199,7 +3198,7 @@ Status ReplicationCoordinatorImpl::processReplSetReconfig(OperationContext* opCt // 1) Is a new, voting node // 2) Already has a 'newlyAdded' field in the old config if (isNewVotingMember || isCurrentlyNewlyAdded) { - newConfig.setNewlyAddedFieldForMemberAtIndex(i, true); + newConfig.addNewlyAddedFieldForMember(newMemId); addedNewlyAddedField = true; } } @@ -3507,6 +3506,70 @@ Status ReplicationCoordinatorImpl::awaitConfigCommitment(OperationContext* opCtx } +void ReplicationCoordinatorImpl::_reconfigToRemoveNewlyAddedField( + const executor::TaskExecutor::CallbackArgs& cbData, + MemberId memberId, + ConfigVersionAndTerm versionAndTerm) { + if (cbData.status == ErrorCodes::CallbackCanceled) { + LOGV2_DEBUG(4634502, + 2, + "Failed to remove 'newlyAdded' config field", + "memberId"_attr = memberId.getData(), + "error"_attr = cbData.status); + // We will retry on the next heartbeat. + return; + } + + LOGV2(4634505, + "Beginning automatic reconfig to remove 'newlyAdded' config field", + "memberId"_attr = memberId.getData()); + + auto getNewConfig = [&](const repl::ReplSetConfig& oldConfig, + long long term) -> StatusWith<ReplSetConfig> { + // Even though memberIds should properly identify nodes across config changes, to be safe we + // only want to do an automatic reconfig where the base config is the one that specified + // this memberId. + if (oldConfig.getConfigVersionAndTerm() != versionAndTerm) { + return Status(ErrorCodes::StaleConfig, + str::stream() + << "Current config is no longer consistent with heartbeat " + "data. Current config version: " + << oldConfig.getConfigVersionAndTerm().toString() + << ", heartbeat data config version: " << versionAndTerm.toString()); + } + + auto newConfig = oldConfig; + newConfig.setConfigVersion(newConfig.getConfigVersion() + 1); + + const auto hasNewlyAddedField = + oldConfig.findMemberByID(memberId.getData())->isNewlyAdded(); + if (!hasNewlyAddedField) { + return Status(ErrorCodes::NoSuchKey, "Old config no longer has 'newlyAdded' field"); + } + + newConfig.removeNewlyAddedFieldForMember(memberId); + return newConfig; + }; + + auto opCtx = cc().makeOperationContext(); + auto status = doReplSetReconfig(opCtx.get(), getNewConfig, false /* force */); + + if (!status.isOK()) { + LOGV2_DEBUG(4634503, + 2, + "Failed to remove 'newlyAdded' config field", + "memberId"_attr = memberId.getData(), + "error"_attr = status); + // It is safe to do nothing here as we will retry this on the next heartbeat, or we may + // instead find out the reconfig already took place and is no longer necessary. + return; + } + + // We intentionally do not wait for config commitment. If the config does not get committed, we + // will try again on the next heartbeat. + LOGV2(4634504, "Removed 'newlyAdded' config field", "memberId"_attr = memberId.getData()); +} + Status ReplicationCoordinatorImpl::processReplSetInitiate(OperationContext* opCtx, const BSONObj& configObj, BSONObjBuilder* resultObj) { |