YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/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
366k
bool IsRaftConfigMember(const std::string& uuid, const RaftConfigPB& config) {
56
747k
  for (const RaftPeerPB& peer : config.peers()) {
57
747k
    if (peer.permanent_uuid() == uuid) {
58
365k
      return true;
59
365k
    }
60
747k
  }
61
1.27k
  return false;
62
366k
}
63
64
159M
bool IsRaftConfigVoter(const std::string& uuid, const RaftConfigPB& config) {
65
328M
  for (const RaftPeerPB& peer : config.peers()) {
66
328M
    if (peer.permanent_uuid() == uuid) {
67
157M
      return peer.member_type() == PeerMemberType::VOTER;
68
157M
    }
69
328M
  }
70
2.05M
  return false;
71
159M
}
72
73
Status GetRaftConfigMember(const RaftConfigPB& config,
74
                           const std::string& uuid,
75
15.2M
                           RaftPeerPB* peer_pb) {
76
30.5M
  for (const RaftPeerPB& peer : config.peers()) {
77
30.5M
    if (peer.permanent_uuid() == uuid) {
78
15.2M
      *peer_pb = peer;
79
15.2M
      return Status::OK();
80
15.2M
    }
81
30.5M
  }
82
18.4E
  return STATUS(NotFound, Substitute("Peer with uuid $0 not found in consensus config { $1 }",
83
15.2M
                                     uuid, config.ShortDebugString()));
84
15.2M
}
85
86
Status GetMutableRaftConfigMember(RaftConfigPB* config,
87
                                  const std::string& uuid,
88
989
                                  RaftPeerPB** peer_pb) {
89
4.15k
  for (int i = 0; i < config->peers_size(); ++i) {
90
4.15k
    auto peer = config->mutable_peers(i);
91
4.15k
    if (peer->permanent_uuid() == uuid) {
92
989
      *peer_pb = peer;
93
989
      return Status::OK();
94
989
    }
95
4.15k
  }
96
0
  return STATUS(NotFound, Substitute("Peer with uuid $0 not found in consensus config", uuid));
97
989
}
98
99
6
Status GetRaftConfigLeader(const ConsensusStatePB& cstate, RaftPeerPB* peer_pb) {
100
6
  if (!cstate.has_leader_uuid() || cstate.leader_uuid().empty()) {
101
1
    return STATUS(NotFound, "Consensus config has no leader");
102
1
  }
103
5
  return GetRaftConfigMember(cstate.config(), cstate.leader_uuid(), peer_pb);
104
5
}
105
106
Status GetHostPortFromConfig(const RaftConfigPB& config, const std::string& uuid,
107
4
                             const CloudInfoPB& from, HostPort* hp) {
108
4
  if (!hp) {
109
0
    return STATUS(InvalidArgument, "Need a non-null hostport.");
110
0
  }
111
10
  for (const RaftPeerPB& peer : config.peers()) {
112
10
    if (peer.permanent_uuid() == uuid) {
113
4
      *hp = HostPortFromPB(DesiredHostPort(peer, from));
114
4
      return Status::OK();
115
4
    }
116
10
  }
117
0
  return STATUS(NotFound, Substitute("Consensus config did not find $0.", uuid));
118
4
}
119
120
901
bool RemoveFromRaftConfig(RaftConfigPB* config, const ChangeConfigRequestPB& req) {
121
901
  RepeatedPtrField<RaftPeerPB> modified_peers;
122
901
  bool removed = false;
123
901
  bool use_host = req.has_use_host() && req.use_host();
124
901
  const HostPortPB* hp = nullptr;
125
901
  if (use_host) {
126
4
    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
4
    hp = &req.server().last_known_private_addr()[0];
132
4
    LOG(INFO) << "Using host/port " << hp->ShortDebugString() << " instead of UUID";
133
4
  }
134
901
  const std::string& uuid = req.server().permanent_uuid();
135
3.62k
  for (const RaftPeerPB& peer : config->peers()) {
136
3.62k
    bool matches;
137
3.62k
    if (use_host) {
138
12
      matches = HasHostPortPB(peer.last_known_private_addr(), *hp) ||
139
8
                HasHostPortPB(peer.last_known_broadcast_addr(), *hp);
140
3.61k
    } else {
141
3.61k
      matches = peer.permanent_uuid() == uuid;
142
3.61k
    }
143
3.62k
    if (matches) {
144
901
      removed = true;
145
901
      continue;
146
901
    }
147
2.72k
    *modified_peers.Add() = peer;
148
2.72k
  }
149
901
  if (!removed) return false;
150
901
  config->mutable_peers()->Swap(&modified_peers);
151
901
  return true;
152
901
}
153
154
114k
size_t CountVoters(const RaftConfigPB& config) {
155
114k
  return CountMemberType(config, PeerMemberType::VOTER);
156
114k
}
157
158
2.70k
size_t CountVotersInTransition(const RaftConfigPB& config) {
159
2.70k
  return CountMemberType(config, PeerMemberType::PRE_VOTER);
160
2.70k
}
161
162
13.5k
size_t CountServersInTransition(const RaftConfigPB& config, const string& ignore_uuid) {
163
13.5k
  return CountMemberType(config, PeerMemberType::PRE_VOTER, ignore_uuid) +
164
13.5k
         CountMemberType(config, PeerMemberType::PRE_OBSERVER, ignore_uuid);
165
13.5k
}
166
167
size_t CountMemberType(const RaftConfigPB& config, const PeerMemberType member_type,
168
144k
                       const string& ignore_uuid) {
169
144k
  size_t count = 0;
170
435k
  for (const RaftPeerPB& peer : config.peers()) {
171
435k
    if (peer.member_type() == member_type && peer.permanent_uuid() != ignore_uuid) {
172
335k
      count++;
173
335k
    }
174
435k
  }
175
144k
  return count;
176
144k
}
177
178
114k
size_t MajoritySize(size_t num_voters) {
179
114k
  DCHECK_GE(num_voters, 1);
180
114k
  return (num_voters / 2) + 1;
181
114k
}
182
183
PeerMemberType GetConsensusMemberType(const std::string& permanent_uuid,
184
376k
                                      const ConsensusStatePB& cstate) {
185
771k
  for (const RaftPeerPB& peer : cstate.config().peers()) {
186
771k
    if (peer.permanent_uuid() == permanent_uuid) {
187
376k
      return peer.member_type();
188
376k
    }
189
771k
  }
190
37
  return PeerMemberType::UNKNOWN_MEMBER_TYPE;
191
376k
}
192
193
1.48M
PeerRole GetConsensusRole(const std::string& permanent_uuid, const ConsensusStatePB& cstate) {
194
1.48M
  if (cstate.leader_uuid() == permanent_uuid) {
195
245k
    if (IsRaftConfigVoter(permanent_uuid, cstate.config())) {
196
245k
      return PeerRole::LEADER;
197
245k
    }
198
12
    return PeerRole::NON_PARTICIPANT;
199
12
  }
200
201
2.51M
  for (const RaftPeerPB& peer : cstate.config().peers()) {
202
2.51M
    if (peer.permanent_uuid() == permanent_uuid) {
203
1.24M
      switch (peer.member_type()) {
204
1.22M
        case PeerMemberType::VOTER:
205
1.22M
          return PeerRole::FOLLOWER;
206
207
        // PRE_VOTER, PRE_OBSERVER peers are considered LEARNERs.
208
7.16k
        case PeerMemberType::PRE_VOTER:
209
7.55k
        case PeerMemberType::PRE_OBSERVER:
210
7.55k
          return PeerRole::LEARNER;
211
212
8.82k
        case PeerMemberType::OBSERVER:
213
8.82k
          return PeerRole::READ_REPLICA;
214
215
0
        case PeerMemberType::UNKNOWN_MEMBER_TYPE:
216
0
          return PeerRole::UNKNOWN_ROLE;
217
1.24M
      }
218
1.24M
    }
219
2.51M
  }
220
577
  return PeerRole::NON_PARTICIPANT;
221
1.24M
}
222
223
326k
Status VerifyRaftConfig(const RaftConfigPB& config, RaftConfigState type) {
224
326k
  std::set<string> uuids;
225
326k
  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
326k
  if (type == COMMITTED_QUORUM) {
232
    // Committed configurations must have 'opid_index' populated.
233
316k
    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
10.1k
  } else if (type == UNCOMMITTED_QUORUM) {
239
    // Uncommitted configurations must *not* have 'opid_index' populated.
240
10.1k
    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
326k
  }
246
247
326k
  int num_peers = config.peers_size();
248
1.01M
  for (const RaftPeerPB& peer : config.peers()) {
249
1.01M
    if (!peer.has_permanent_uuid() || 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
1.01M
    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
1.01M
    uuids.insert(peer.permanent_uuid());
259
260
1.01M
    if (num_peers > 1 && peer.last_known_private_addr().empty()) {
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
1.01M
    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
1.01M
  }
271
272
326k
  return Status::OK();
273
326k
}
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()) {
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
3
  }
291
292
3
  return Status::OK();
293
3
}
294
295
} // namespace consensus
296
}  // namespace yb