YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

/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