in src/kudu/consensus/quorum_util.cc [356:459]
string DiffConsensusStates(const ConsensusStatePB& old_state,
const ConsensusStatePB& new_state,
std::vector<std::string>* evicted_peers) {
bool leader_changed = old_state.leader_uuid() != new_state.leader_uuid();
bool term_changed = old_state.current_term() != new_state.current_term();
bool config_changed =
old_state.committed_config().opid_index() != new_state.committed_config().opid_index();
bool pending_config_gained = !old_state.has_pending_config() && new_state.has_pending_config();
bool pending_config_lost = old_state.has_pending_config() && !new_state.has_pending_config();
// Construct a map from Peer UUID to '<old peer, new peer>' pairs.
// Due to the default construction nature of std::map and std::pair, if a peer
// is present in one configuration but not the other, we'll end up with an empty
// protobuf in that element of the pair.
PeerInfoMap committed_peer_infos;
for (const auto& p : old_state.committed_config().peers()) {
committed_peer_infos[p.permanent_uuid()].first = p;
}
for (const auto& p : new_state.committed_config().peers()) {
committed_peer_infos[p.permanent_uuid()].second = p;
}
// Now collect strings representing the changes.
vector<string> change_strs;
if (config_changed) {
change_strs.push_back(
Substitute("config changed from index $0 to $1",
old_state.committed_config().opid_index(),
new_state.committed_config().opid_index()));
}
if (term_changed) {
change_strs.push_back(
Substitute("term changed from $0 to $1",
old_state.current_term(),
new_state.current_term()));
}
if (leader_changed) {
string old_leader = "<none>";
string new_leader = "<none>";
if (!old_state.leader_uuid().empty()) {
old_leader = Substitute("$0 ($1)",
old_state.leader_uuid(),
committed_peer_infos[old_state.leader_uuid()].first
.last_known_addr().host());
}
if (!new_state.leader_uuid().empty()) {
new_leader = Substitute("$0 ($1)",
new_state.leader_uuid(),
committed_peer_infos[new_state.leader_uuid()].second
.last_known_addr().host());
}
change_strs.push_back(Substitute("leader changed from $0 to $1",
old_leader, new_leader));
}
DiffPeers(committed_peer_infos, &change_strs, evicted_peers);
if (pending_config_gained) {
change_strs.push_back(Substitute("now has a pending config: $0",
PeersString(new_state.pending_config())));
}
if (pending_config_lost) {
change_strs.push_back(Substitute("no longer has a pending config: $0",
PeersString(old_state.pending_config())));
}
// A pending config doesn't have a committed opid_index yet, so we determine if there's a change
// by computing the peer differences.
if (old_state.has_pending_config() && new_state.has_pending_config()) {
PeerInfoMap pending_peer_infos;
for (const auto &p : old_state.pending_config().peers()) {
pending_peer_infos[p.permanent_uuid()].first = p;
}
for (const auto &p : new_state.pending_config().peers()) {
pending_peer_infos[p.permanent_uuid()].second = p;
}
vector<string> pending_change_strs;
if (DiffPeers(pending_peer_infos, &pending_change_strs, evicted_peers)) {
change_strs.emplace_back("pending config changed");
change_strs.insert(change_strs.end(), pending_change_strs.cbegin(),
pending_change_strs.cend());
}
}
// We expect to have detected some differences above, but in case
// someone forgets to update this function when adding a new field,
// it's still useful to report some change unless the protobufs are identical.
// So, we fall back to just dumping the before/after debug strings.
if (change_strs.empty()) {
if (SecureShortDebugString(old_state) == SecureShortDebugString(new_state)) {
return "no change";
}
return Substitute("change from {$0} to {$1}",
SecureShortDebugString(old_state),
SecureShortDebugString(new_state));
}
return JoinStrings(change_strs, ", ");
}