/Users/deen/code/yugabyte-db/src/yb/consensus/quorum_util.cc
Line | Count | Source (jump to first uncovered line) |
1 | | // Licensed to the Apache Software Foundation (ASF) under one |
2 | | // or more contributor license agreements. See the NOTICE file |
3 | | // distributed with this work for additional information |
4 | | // regarding copyright ownership. The ASF licenses this file |
5 | | // to you under the Apache License, Version 2.0 (the |
6 | | // "License"); you may not use this file except in compliance |
7 | | // with the License. You may obtain a copy of the License at |
8 | | // |
9 | | // http://www.apache.org/licenses/LICENSE-2.0 |
10 | | // |
11 | | // Unless required by applicable law or agreed to in writing, |
12 | | // software distributed under the License is distributed on an |
13 | | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
14 | | // KIND, either express or implied. See the License for the |
15 | | // specific language governing permissions and limitations |
16 | | // under the License. |
17 | | // |
18 | | // The following only applies to changes made to this file as part of YugaByte development. |
19 | | // |
20 | | // Portions Copyright (c) YugaByte, Inc. |
21 | | // |
22 | | // Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except |
23 | | // in compliance with the License. You may obtain a copy of the License at |
24 | | // |
25 | | // http://www.apache.org/licenses/LICENSE-2.0 |
26 | | // |
27 | | // Unless required by applicable law or agreed to in writing, software distributed under the License |
28 | | // is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express |
29 | | // or implied. See the License for the specific language governing permissions and limitations |
30 | | // under the License. |
31 | | // |
32 | | |
33 | | #include "yb/consensus/quorum_util.h" |
34 | | |
35 | | #include <set> |
36 | | #include <string> |
37 | | |
38 | | #include "yb/common/wire_protocol.h" |
39 | | |
40 | | #include "yb/consensus/consensus_meta.h" |
41 | | #include "yb/consensus/consensus.pb.h" |
42 | | |
43 | | #include "yb/gutil/map-util.h" |
44 | | |
45 | | #include "yb/util/net/net_util.h" |
46 | | #include "yb/util/status.h" |
47 | | |
48 | | namespace yb { |
49 | | namespace consensus { |
50 | | |
51 | | using google::protobuf::RepeatedPtrField; |
52 | | using std::string; |
53 | | using strings::Substitute; |
54 | | |
55 | 1.78M | bool IsRaftConfigMember(const std::string& uuid, const RaftConfigPB& config) { |
56 | 3.29M | for (const RaftPeerPB& peer : config.peers()) { |
57 | 3.29M | if (peer.permanent_uuid() == uuid) { |
58 | 1.77M | return true; |
59 | 1.77M | } |
60 | 3.29M | } |
61 | 2.49k | return false; |
62 | 1.78M | } |
63 | | |
64 | 391M | bool IsRaftConfigVoter(const std::string& uuid, const RaftConfigPB& config) { |
65 | 802M | for (const RaftPeerPB& peer : config.peers()) { |
66 | 802M | if (peer.permanent_uuid() == uuid) { |
67 | 381M | return peer.member_type() == PeerMemberType::VOTER; |
68 | 381M | } |
69 | 802M | } |
70 | 10.3M | return false; |
71 | 391M | } |
72 | | |
73 | | Status GetRaftConfigMember(const RaftConfigPB& config, |
74 | | const std::string& uuid, |
75 | 34.8M | RaftPeerPB* peer_pb) { |
76 | 69.4M | for (const RaftPeerPB& peer : config.peers()) { |
77 | 69.4M | if (peer.permanent_uuid() == uuid) { |
78 | 34.8M | *peer_pb = peer; |
79 | 34.8M | return Status::OK(); |
80 | 34.8M | } |
81 | 69.4M | } |
82 | 18.4E | return STATUS(NotFound, Substitute("Peer with uuid $0 not found in consensus config { $1 }", |
83 | 34.8M | uuid, config.ShortDebugString())); |
84 | 34.8M | } |
85 | | |
86 | | Status GetMutableRaftConfigMember(RaftConfigPB* config, |
87 | | const std::string& uuid, |
88 | 2.01k | RaftPeerPB** peer_pb) { |
89 | 8.04k | for (int i = 0; i < config->peers_size(); ++i6.02k ) { |
90 | 8.04k | auto peer = config->mutable_peers(i); |
91 | 8.04k | if (peer->permanent_uuid() == uuid) { |
92 | 2.01k | *peer_pb = peer; |
93 | 2.01k | return Status::OK(); |
94 | 2.01k | } |
95 | 8.04k | } |
96 | 0 | return STATUS(NotFound, Substitute("Peer with uuid $0 not found in consensus config", uuid)); |
97 | 2.01k | } |
98 | | |
99 | 6 | Status GetRaftConfigLeader(const ConsensusStatePB& cstate, RaftPeerPB* peer_pb) { |
100 | 6 | if (!cstate.has_leader_uuid() || cstate.leader_uuid().empty()5 ) { |
101 | 1 | return STATUS(NotFound, "Consensus config has no leader"); |
102 | 1 | } |
103 | 5 | return GetRaftConfigMember(cstate.config(), cstate.leader_uuid(), peer_pb); |
104 | 6 | } |
105 | | |
106 | | Status GetHostPortFromConfig(const RaftConfigPB& config, const std::string& uuid, |
107 | 5 | const CloudInfoPB& from, HostPort* hp) { |
108 | 5 | if (!hp) { |
109 | 0 | return STATUS(InvalidArgument, "Need a non-null hostport."); |
110 | 0 | } |
111 | 11 | for (const RaftPeerPB& peer : config.peers())5 { |
112 | 11 | if (peer.permanent_uuid() == uuid) { |
113 | 5 | *hp = HostPortFromPB(DesiredHostPort(peer, from)); |
114 | 5 | return Status::OK(); |
115 | 5 | } |
116 | 11 | } |
117 | 0 | return STATUS(NotFound, Substitute("Consensus config did not find $0.", uuid)); |
118 | 5 | } |
119 | | |
120 | 1.81k | bool RemoveFromRaftConfig(RaftConfigPB* config, const ChangeConfigRequestPB& req) { |
121 | 1.81k | RepeatedPtrField<RaftPeerPB> modified_peers; |
122 | 1.81k | bool removed = false; |
123 | 1.81k | bool use_host = req.has_use_host() && req.use_host()12 ; |
124 | 1.81k | const HostPortPB* hp = nullptr; |
125 | 1.81k | if (use_host) { |
126 | 5 | if (req.server().last_known_private_addr().size() != 1) { |
127 | 0 | LOG(WARNING) << "Remove from raft config with invalid host specified: " |
128 | 0 | << req.server().ShortDebugString(); |
129 | 0 | return false; |
130 | 0 | } |
131 | 5 | hp = &req.server().last_known_private_addr()[0]; |
132 | 5 | LOG(INFO) << "Using host/port " << hp->ShortDebugString() << " instead of UUID"; |
133 | 5 | } |
134 | 1.81k | const std::string& uuid = req.server().permanent_uuid(); |
135 | 7.23k | for (const RaftPeerPB& peer : config->peers()) { |
136 | 7.23k | bool matches; |
137 | 7.23k | if (use_host) { |
138 | 15 | matches = HasHostPortPB(peer.last_known_private_addr(), *hp) || |
139 | 15 | HasHostPortPB(peer.last_known_broadcast_addr(), *hp)10 ; |
140 | 7.21k | } else { |
141 | 7.21k | matches = peer.permanent_uuid() == uuid; |
142 | 7.21k | } |
143 | 7.23k | if (matches) { |
144 | 1.81k | removed = true; |
145 | 1.81k | continue; |
146 | 1.81k | } |
147 | 5.42k | *modified_peers.Add() = peer; |
148 | 5.42k | } |
149 | 1.81k | if (!removed) return false0 ; |
150 | 1.81k | config->mutable_peers()->Swap(&modified_peers); |
151 | 1.81k | return true; |
152 | 1.81k | } |
153 | | |
154 | 1.02M | size_t CountVoters(const RaftConfigPB& config) { |
155 | 1.02M | return CountMemberType(config, PeerMemberType::VOTER); |
156 | 1.02M | } |
157 | | |
158 | 5.80k | size_t CountVotersInTransition(const RaftConfigPB& config) { |
159 | 5.80k | return CountMemberType(config, PeerMemberType::PRE_VOTER); |
160 | 5.80k | } |
161 | | |
162 | 27.7k | size_t CountServersInTransition(const RaftConfigPB& config, const string& ignore_uuid) { |
163 | 27.7k | return CountMemberType(config, PeerMemberType::PRE_VOTER, ignore_uuid) + |
164 | 27.7k | CountMemberType(config, PeerMemberType::PRE_OBSERVER, ignore_uuid); |
165 | 27.7k | } |
166 | | |
167 | | size_t CountMemberType(const RaftConfigPB& config, const PeerMemberType member_type, |
168 | 1.08M | const string& ignore_uuid) { |
169 | 1.08M | size_t count = 0; |
170 | 3.03M | for (const RaftPeerPB& peer : config.peers()) { |
171 | 3.03M | if (peer.member_type() == member_type && peer.permanent_uuid() != ignore_uuid2.83M ) { |
172 | 2.83M | count++; |
173 | 2.83M | } |
174 | 3.03M | } |
175 | 1.08M | return count; |
176 | 1.08M | } |
177 | | |
178 | 805k | size_t MajoritySize(size_t num_voters) { |
179 | 805k | DCHECK_GE(num_voters, 1); |
180 | 805k | return (num_voters / 2) + 1; |
181 | 805k | } |
182 | | |
183 | | PeerMemberType GetConsensusMemberType(const std::string& permanent_uuid, |
184 | 660k | const ConsensusStatePB& cstate) { |
185 | 1.35M | for (const RaftPeerPB& peer : cstate.config().peers()) { |
186 | 1.35M | if (peer.permanent_uuid() == permanent_uuid) { |
187 | 660k | return peer.member_type(); |
188 | 660k | } |
189 | 1.35M | } |
190 | 64 | return PeerMemberType::UNKNOWN_MEMBER_TYPE; |
191 | 660k | } |
192 | | |
193 | 2.41M | PeerRole GetConsensusRole(const std::string& permanent_uuid, const ConsensusStatePB& cstate) { |
194 | 2.41M | if (cstate.leader_uuid() == permanent_uuid) { |
195 | 371k | if (IsRaftConfigVoter(permanent_uuid, cstate.config())) { |
196 | 371k | return PeerRole::LEADER; |
197 | 371k | } |
198 | 5 | return PeerRole::NON_PARTICIPANT; |
199 | 371k | } |
200 | | |
201 | 4.15M | for (const RaftPeerPB& peer : cstate.config().peers())2.04M { |
202 | 4.15M | if (peer.permanent_uuid() == permanent_uuid) { |
203 | 2.04M | switch (peer.member_type()) { |
204 | 2.01M | case PeerMemberType::VOTER: |
205 | 2.01M | return PeerRole::FOLLOWER; |
206 | | |
207 | | // PRE_VOTER, PRE_OBSERVER peers are considered LEARNERs. |
208 | 14.8k | case PeerMemberType::PRE_VOTER: |
209 | 15.3k | case PeerMemberType::PRE_OBSERVER: |
210 | 15.3k | return PeerRole::LEARNER; |
211 | | |
212 | 14.2k | case PeerMemberType::OBSERVER: |
213 | 14.2k | return PeerRole::READ_REPLICA; |
214 | | |
215 | 0 | case PeerMemberType::UNKNOWN_MEMBER_TYPE: |
216 | 0 | return PeerRole::UNKNOWN_ROLE; |
217 | 2.04M | } |
218 | 2.04M | } |
219 | 4.15M | } |
220 | 853 | return PeerRole::NON_PARTICIPANT; |
221 | 2.04M | } |
222 | | |
223 | 1.14M | Status VerifyRaftConfig(const RaftConfigPB& config, RaftConfigState type) { |
224 | 1.14M | std::set<string> uuids; |
225 | 1.14M | if (config.peers_size() == 0) { |
226 | 0 | return STATUS(IllegalState, |
227 | 0 | Substitute("RaftConfig must have at least one peer. RaftConfig: $0", |
228 | 0 | config.ShortDebugString())); |
229 | 0 | } |
230 | | |
231 | 1.14M | if (type == COMMITTED_QUORUM) { |
232 | | // Committed configurations must have 'opid_index' populated. |
233 | 1.12M | if (!config.has_opid_index()) { |
234 | 1 | return STATUS(IllegalState, |
235 | 1 | Substitute("Committed configs must have opid_index set. RaftConfig: $0", |
236 | 1 | config.ShortDebugString())); |
237 | 1 | } |
238 | 1.12M | } else if (19.7k type == UNCOMMITTED_QUORUM19.7k ) { |
239 | | // Uncommitted configurations must *not* have 'opid_index' populated. |
240 | 19.5k | if (config.has_opid_index()) { |
241 | 0 | return STATUS(IllegalState, |
242 | 0 | Substitute("Uncommitted configs must not have opid_index set. RaftConfig: $0", |
243 | 0 | config.ShortDebugString())); |
244 | 0 | } |
245 | 19.5k | } |
246 | | |
247 | 1.14M | int num_peers = config.peers_size(); |
248 | 3.49M | for (const RaftPeerPB& peer : config.peers()) { |
249 | 3.49M | if (!peer.has_permanent_uuid()3.49M || peer.permanent_uuid() == "") { |
250 | 0 | return STATUS(IllegalState, Substitute("One peer didn't have an uuid or had the empty" |
251 | 0 | " string. RaftConfig: $0", config.ShortDebugString())); |
252 | 0 | } |
253 | 3.49M | if (ContainsKey(uuids, peer.permanent_uuid())) { |
254 | 0 | return STATUS(IllegalState, |
255 | 0 | Substitute("Found multiple peers with uuid: $0. RaftConfig: $1", |
256 | 0 | peer.permanent_uuid(), config.ShortDebugString())); |
257 | 0 | } |
258 | 3.49M | uuids.insert(peer.permanent_uuid()); |
259 | | |
260 | 3.49M | if (num_peers > 1 && peer.last_known_private_addr().empty()3.48M ) { |
261 | 0 | return STATUS(IllegalState, |
262 | 0 | Substitute("Peer: $0 has no address. RaftConfig: $1", |
263 | 0 | peer.permanent_uuid(), config.ShortDebugString())); |
264 | 0 | } |
265 | 3.49M | if (!peer.has_member_type()) { |
266 | 0 | return STATUS(IllegalState, |
267 | 0 | Substitute("Peer: $0 has no member type set. RaftConfig: $1", peer.permanent_uuid(), |
268 | 0 | config.ShortDebugString())); |
269 | 0 | } |
270 | 3.49M | } |
271 | | |
272 | 1.14M | return Status::OK(); |
273 | 1.14M | } |
274 | | |
275 | 3 | Status VerifyConsensusState(const ConsensusStatePB& cstate, RaftConfigState type) { |
276 | 3 | if (!cstate.has_current_term()) { |
277 | 0 | return STATUS(IllegalState, "ConsensusStatePB missing current_term", cstate.ShortDebugString()); |
278 | 0 | } |
279 | 3 | if (!cstate.has_config()) { |
280 | 0 | return STATUS(IllegalState, "ConsensusStatePB missing config", cstate.ShortDebugString()); |
281 | 0 | } |
282 | 3 | RETURN_NOT_OK(VerifyRaftConfig(cstate.config(), type)); |
283 | | |
284 | 3 | if (cstate.has_leader_uuid() && !cstate.leader_uuid().empty()2 ) { |
285 | 2 | if (!IsRaftConfigVoter(cstate.leader_uuid(), cstate.config())) { |
286 | 0 | return STATUS(IllegalState, |
287 | 0 | Substitute("Leader with UUID $0 is not a VOTER in the config! Consensus state: $1", |
288 | 0 | cstate.leader_uuid(), cstate.ShortDebugString())); |
289 | 0 | } |
290 | 2 | } |
291 | | |
292 | 3 | return Status::OK(); |
293 | 3 | } |
294 | | |
295 | | } // namespace consensus |
296 | | } // namespace yb |