blob: ef9393462f09f7e386f5887f463f60464828715c [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
#include "kudu/consensus/quorum_util.h"
#include <set>
#include <string>
#include "kudu/gutil/map-util.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/util/status.h"
namespace kudu {
namespace consensus {
using google::protobuf::RepeatedPtrField;
using std::string;
using strings::Substitute;
bool IsRaftConfigMember(const std::string& uuid, const RaftConfigPB& config) {
for (const RaftPeerPB& peer : config.peers()) {
if (peer.permanent_uuid() == uuid) {
return true;
}
}
return false;
}
bool IsRaftConfigVoter(const std::string& uuid, const RaftConfigPB& config) {
for (const RaftPeerPB& peer : config.peers()) {
if (peer.permanent_uuid() == uuid) {
return peer.member_type() == RaftPeerPB::VOTER;
}
}
return false;
}
Status GetRaftConfigMember(const RaftConfigPB& config,
const std::string& uuid,
RaftPeerPB* peer_pb) {
for (const RaftPeerPB& peer : config.peers()) {
if (peer.permanent_uuid() == uuid) {
*peer_pb = peer;
return Status::OK();
}
}
return Status::NotFound(Substitute("Peer with uuid $0 not found in consensus config", uuid));
}
Status GetRaftConfigLeader(const ConsensusStatePB& cstate, RaftPeerPB* peer_pb) {
if (!cstate.has_leader_uuid() || cstate.leader_uuid().empty()) {
return Status::NotFound("Consensus config has no leader");
}
return GetRaftConfigMember(cstate.config(), cstate.leader_uuid(), peer_pb);
}
bool RemoveFromRaftConfig(RaftConfigPB* config, const string& uuid) {
RepeatedPtrField<RaftPeerPB> modified_peers;
bool removed = false;
for (const RaftPeerPB& peer : config->peers()) {
if (peer.permanent_uuid() == uuid) {
removed = true;
continue;
}
*modified_peers.Add() = peer;
}
if (!removed) return false;
config->mutable_peers()->Swap(&modified_peers);
return true;
}
int CountVoters(const RaftConfigPB& config) {
int voters = 0;
for (const RaftPeerPB& peer : config.peers()) {
if (peer.member_type() == RaftPeerPB::VOTER) {
voters++;
}
}
return voters;
}
int MajoritySize(int num_voters) {
DCHECK_GE(num_voters, 1);
return (num_voters / 2) + 1;
}
RaftPeerPB::Role GetConsensusRole(const std::string& permanent_uuid,
const ConsensusStatePB& cstate) {
if (cstate.leader_uuid() == permanent_uuid) {
if (IsRaftConfigVoter(permanent_uuid, cstate.config())) {
return RaftPeerPB::LEADER;
}
return RaftPeerPB::NON_PARTICIPANT;
}
for (const RaftPeerPB& peer : cstate.config().peers()) {
if (peer.permanent_uuid() == permanent_uuid) {
switch (peer.member_type()) {
case RaftPeerPB::VOTER:
return RaftPeerPB::FOLLOWER;
default:
return RaftPeerPB::LEARNER;
}
}
}
return RaftPeerPB::NON_PARTICIPANT;
}
Status VerifyRaftConfig(const RaftConfigPB& config, RaftConfigState type) {
std::set<string> uuids;
if (config.peers_size() == 0) {
return Status::IllegalState(
Substitute("RaftConfig must have at least one peer. RaftConfig: $0",
config.ShortDebugString()));
}
if (!config.has_local()) {
return Status::IllegalState(
Substitute("RaftConfig must specify whether it is local. RaftConfig: ",
config.ShortDebugString()));
}
if (type == COMMITTED_QUORUM) {
// Committed configurations must have 'opid_index' populated.
if (!config.has_opid_index()) {
return Status::IllegalState(
Substitute("Committed configs must have opid_index set. RaftConfig: $0",
config.ShortDebugString()));
}
} else if (type == UNCOMMITTED_QUORUM) {
// Uncommitted configurations must *not* have 'opid_index' populated.
if (config.has_opid_index()) {
return Status::IllegalState(
Substitute("Uncommitted configs must not have opid_index set. RaftConfig: $0",
config.ShortDebugString()));
}
}
// Local configurations must have only one peer and it may or may not
// have an address.
if (config.local()) {
if (config.peers_size() != 1) {
return Status::IllegalState(
Substitute("Local configs must have 1 and only one peer. RaftConfig: ",
config.ShortDebugString()));
}
if (!config.peers(0).has_permanent_uuid() ||
config.peers(0).permanent_uuid() == "") {
return Status::IllegalState(
Substitute("Local peer must have an UUID. RaftConfig: ",
config.ShortDebugString()));
}
return Status::OK();
}
for (const RaftPeerPB& peer : config.peers()) {
if (!peer.has_permanent_uuid() || peer.permanent_uuid() == "") {
return Status::IllegalState(Substitute("One peer didn't have an uuid or had the empty"
" string. RaftConfig: $0", config.ShortDebugString()));
}
if (ContainsKey(uuids, peer.permanent_uuid())) {
return Status::IllegalState(
Substitute("Found multiple peers with uuid: $0. RaftConfig: $1",
peer.permanent_uuid(), config.ShortDebugString()));
}
uuids.insert(peer.permanent_uuid());
if (!peer.has_last_known_addr()) {
return Status::IllegalState(
Substitute("Peer: $0 has no address. RaftConfig: $1",
peer.permanent_uuid(), config.ShortDebugString()));
}
if (!peer.has_member_type()) {
return Status::IllegalState(
Substitute("Peer: $0 has no member type set. RaftConfig: $1", peer.permanent_uuid(),
config.ShortDebugString()));
}
if (peer.member_type() == RaftPeerPB::NON_VOTER) {
return Status::IllegalState(
Substitute(
"Peer: $0 is a NON_VOTER, but this isn't supported yet. RaftConfig: $1",
peer.permanent_uuid(), config.ShortDebugString()));
}
}
return Status::OK();
}
Status VerifyConsensusState(const ConsensusStatePB& cstate, RaftConfigState type) {
if (!cstate.has_current_term()) {
return Status::IllegalState("ConsensusStatePB missing current_term", cstate.ShortDebugString());
}
if (!cstate.has_config()) {
return Status::IllegalState("ConsensusStatePB missing config", cstate.ShortDebugString());
}
RETURN_NOT_OK(VerifyRaftConfig(cstate.config(), type));
if (cstate.has_leader_uuid() && !cstate.leader_uuid().empty()) {
if (!IsRaftConfigVoter(cstate.leader_uuid(), cstate.config())) {
return Status::IllegalState(
Substitute("Leader with UUID $0 is not a VOTER in the config! Consensus state: $1",
cstate.leader_uuid(), cstate.ShortDebugString()));
}
}
return Status::OK();
}
} // namespace consensus
} // namespace kudu