YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/consensus/leader_election-test.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 <functional>
34
#include <memory>
35
#include <string>
36
#include <vector>
37
38
#include "yb/consensus/consensus-test-util.h"
39
#include "yb/consensus/consensus_peers.h"
40
#include "yb/consensus/leader_election.h"
41
#include "yb/consensus/metadata.pb.h"
42
43
#include "yb/gutil/casts.h"
44
#include "yb/gutil/map-util.h"
45
#include "yb/gutil/strings/substitute.h"
46
47
#include "yb/util/status_log.h"
48
#include "yb/util/test_macros.h"
49
#include "yb/util/test_util.h"
50
51
namespace yb {
52
53
namespace rpc {
54
class Messenger;
55
} // namespace rpc
56
57
namespace consensus {
58
59
using std::shared_ptr;
60
using std::string;
61
using std::unordered_map;
62
using std::vector;
63
using strings::Substitute;
64
65
namespace {
66
67
const MonoDelta kLeaderElectionTimeout = MonoDelta::FromSeconds(10);
68
69
// Generate list of voter uuids.
70
199
static vector<string> GenVoterUUIDs(int num_voters) {
71
199
  vector<string> voter_uuids;
72
2.09k
  for (int i = 0; i < num_voters; i++) {
73
1.89k
    voter_uuids.push_back(Substitute("peer-$0", i));
74
1.89k
  }
75
199
  return voter_uuids;
76
199
}
77
78
} // namespace
79
80
////////////////////////////////////////
81
// LeaderElectionTest
82
////////////////////////////////////////
83
84
typedef unordered_map<string, PeerProxy*> ProxyMap;
85
86
// A proxy factory that serves proxies from a map.
87
class FromMapPeerProxyFactory : public PeerProxyFactory {
88
 public:
89
  explicit FromMapPeerProxyFactory(const ProxyMap* proxy_map)
90
5
      : proxy_map_(proxy_map) {
91
5
  }
92
93
5
  ~FromMapPeerProxyFactory() {
94
5
    DeleteUnusedPeerProxies();
95
5
  }
96
97
396
  PeerProxyPtr NewProxy(const RaftPeerPB& peer_pb) override {
98
396
    PeerProxy* proxy_ptr = FindPtrOrNull(*proxy_map_, peer_pb.permanent_uuid());
99
396
    if (proxy_ptr == nullptr) {
100
0
      return nullptr;
101
0
    }
102
396
    used_peer_proxy_.insert(peer_pb.permanent_uuid());
103
396
    return PeerProxyPtr(proxy_ptr);
104
396
  }
105
106
197
  void DeleteUnusedPeerProxies() {
107
1.69k
    for (auto item : *proxy_map_) {
108
1.69k
      if (used_peer_proxy_.count(item.first) == 0) {
109
1.29k
        delete item.second;
110
1.29k
      }
111
1.69k
    }
112
197
    used_peer_proxy_.clear();
113
197
  }
114
115
0
  Messenger* messenger() const override {
116
0
    return nullptr;
117
0
  }
118
119
 private:
120
  // FYI, the tests may add and remove nodes from this map while we hold a
121
  // reference to it.
122
  const ProxyMap* const proxy_map_;
123
  std::set<string> used_peer_proxy_;
124
};
125
126
class LeaderElectionTest : public YBTest {
127
 public:
128
  LeaderElectionTest()
129
    : tablet_id_("test-tablet"),
130
      proxy_factory_(new FromMapPeerProxyFactory(&proxies_)),
131
5
      latch_(1) {
132
5
    CHECK_OK(ThreadPoolBuilder("test-peer-pool").set_max_threads(5).Build(&pool_));
133
5
  }
134
135
  void ElectionCallback(const ElectionResult& result);
136
137
 protected:
138
  void InitUUIDs(int num_voters, int non_voters);
139
  void InitNoOpPeerProxies(int num_voters,
140
                           int num_pre_voters,
141
                           int num_pre_observers,
142
                           int num_observers);
143
  void InitDelayableMockedProxies(int num_voters,
144
                                  int num_pre_voters,
145
                                  int num_pre_observers,
146
                                  int num_observers,
147
                                  bool enable_delay);
148
  std::unique_ptr<VoteCounter> InitVoteCounter(int num_voters, int majority_size);
149
150
  // Voter 0 is the high-term voter.
151
  LeaderElectionPtr SetUpElectionWithHighTermVoter(ConsensusTerm election_term);
152
153
  // Predetermine the election results using the specified number of
154
  // grant / deny / error responses.
155
  // num_grant must be at least 1, for the candidate to vote for itself.
156
  // num_grant + num_deny + num_error must add up to an odd number.
157
  LeaderElectionPtr SetUpElectionWithGrantDenyErrorVotes(ConsensusTerm election_term,
158
                                                                     int num_grant,
159
                                                                     int num_deny,
160
                                                                     int num_error);
161
162
  const string tablet_id_;
163
  string candidate_uuid_;
164
  vector<string> voter_uuids_;
165
166
  RaftConfigPB config_;
167
  ProxyMap proxies_;
168
  std::unique_ptr<PeerProxyFactory> proxy_factory_;
169
  std::unique_ptr<ThreadPool> pool_;
170
171
  CountDownLatch latch_;
172
  std::unique_ptr<ElectionResult> result_;
173
};
174
175
196
void LeaderElectionTest::ElectionCallback(const ElectionResult& result) {
176
196
  result_.reset(new ElectionResult(result));
177
196
  latch_.CountDown();
178
196
}
179
180
196
void LeaderElectionTest::InitUUIDs(int num_voters, int non_voters) {
181
196
  ASSERT_GT(num_voters, 0);
182
196
  voter_uuids_ = GenVoterUUIDs(num_voters + non_voters);
183
196
  candidate_uuid_ = voter_uuids_[0];
184
196
  voter_uuids_.erase(voter_uuids_.begin());
185
196
}
186
187
void LeaderElectionTest::InitNoOpPeerProxies(int num_voters,
188
                                             int num_pre_voters,
189
                                             int num_pre_observers,
190
192
                                             int num_observers) {
191
192
  ASSERT_GT(num_voters, 0);
192
192
  ASSERT_GE(num_pre_voters, 0);
193
192
  ASSERT_GE(num_pre_observers, 0);
194
192
  ASSERT_GE(num_observers, 0);
195
192
  config_.Clear();
196
197
  // Remove candidate_uuid_.
198
192
  num_voters--;
199
192
  ASSERT_EQ(voter_uuids_.size(), num_voters + num_pre_voters + num_pre_observers + num_observers);
200
1.68k
  for (const string& uuid : voter_uuids_) {
201
1.68k
    RaftPeerPB* peer_pb = config_.add_peers();
202
1.68k
    PeerMemberType member_type;
203
1.68k
    if (num_voters > 0) {
204
384
      member_type = PeerMemberType::VOTER;
205
384
      num_voters--;
206
1.29k
    } else if (num_pre_voters > 0) {
207
432
      member_type = PeerMemberType::PRE_VOTER;
208
432
      num_pre_voters--;
209
864
    } else if (num_pre_observers > 0) {
210
432
      member_type = PeerMemberType::PRE_OBSERVER;
211
432
      num_pre_observers--;
212
432
    } else if (num_observers > 0) {
213
432
      member_type = PeerMemberType::OBSERVER;
214
432
      num_observers--;
215
0
    } else {
216
0
      member_type = PeerMemberType::UNKNOWN_MEMBER_TYPE;
217
0
      LOG(FATAL) << "Invalid member type";
218
0
    }
219
1.68k
    peer_pb->set_member_type(member_type);
220
1.68k
    peer_pb->set_permanent_uuid(uuid);
221
1.68k
    PeerProxy* proxy = new NoOpTestPeerProxy(pool_.get(), *peer_pb);
222
1.68k
    InsertOrDie(&proxies_, uuid, proxy);
223
1.68k
  }
224
192
}
225
226
void LeaderElectionTest::InitDelayableMockedProxies(int num_voters,
227
                                                    int num_pre_voters,
228
                                                    int num_pre_observers,
229
                                                    int num_observers,
230
4
                                                    bool enable_delay) {
231
4
  ASSERT_GT(num_voters, 0);
232
4
  ASSERT_GE(num_pre_voters, 0);
233
4
  ASSERT_GE(num_pre_observers, 0);
234
4
  ASSERT_GE(num_observers, 0);
235
4
  config_.Clear();
236
237
  // Remove candidate_uuid_.
238
4
  num_voters--;
239
4
  ASSERT_EQ(voter_uuids_.size(), num_voters + num_pre_voters + num_pre_observers + num_observers);
240
12
  for (const string& uuid : voter_uuids_) {
241
12
    RaftPeerPB* peer_pb = config_.add_peers();
242
12
    PeerMemberType member_type;
243
12
    if (num_voters > 0) {
244
12
      member_type = PeerMemberType::VOTER;
245
12
      num_voters--;
246
0
    } else if (num_pre_voters > 0) {
247
0
      member_type = PeerMemberType::PRE_VOTER;
248
0
      num_pre_voters--;
249
0
    } else if (num_pre_observers >0) {
250
0
      member_type = PeerMemberType::PRE_OBSERVER;
251
0
      num_pre_observers--;
252
0
    } else if (num_observers > 0) {
253
0
      member_type = PeerMemberType::OBSERVER;
254
0
      num_observers--;
255
0
    } else {
256
0
      member_type = PeerMemberType::UNKNOWN_MEMBER_TYPE;
257
0
      LOG(FATAL) << "Invalid member type";
258
0
    }
259
12
    peer_pb->set_member_type(member_type);
260
12
    peer_pb->set_permanent_uuid(uuid);
261
12
    auto proxy = new DelayablePeerProxy<MockedPeerProxy>(pool_.get(),
262
12
                                                         new MockedPeerProxy(pool_.get()));
263
12
    if (enable_delay) {
264
4
      proxy->DelayResponse();
265
4
    }
266
12
    InsertOrDie(&proxies_, uuid, proxy);
267
12
  }
268
4
}
269
270
std::unique_ptr<VoteCounter> LeaderElectionTest::InitVoteCounter(
271
196
    int num_voters, int majority_size) {
272
196
  auto counter = std::make_unique<VoteCounter>(num_voters, majority_size);
273
196
  bool duplicate;
274
196
  CHECK_OK(counter->RegisterVote(candidate_uuid_, ElectionVote::kGranted, &duplicate));
275
196
  CHECK(!duplicate);
276
196
  return counter;
277
196
}
278
279
2
LeaderElectionPtr LeaderElectionTest::SetUpElectionWithHighTermVoter(ConsensusTerm election_term) {
280
2
  const int kNumVoters = 3;
281
2
  const int kMajoritySize = 2;
282
283
2
  InitUUIDs(kNumVoters, 0);
284
2
  InitDelayableMockedProxies(kNumVoters, 0, 0, 0, true);
285
2
  auto counter = InitVoteCounter(kNumVoters, kMajoritySize);
286
287
2
  VoteResponsePB response;
288
2
  response.set_responder_uuid(voter_uuids_[0]);
289
2
  response.set_responder_term(election_term + 1);
290
2
  response.set_vote_granted(false);
291
2
  response.mutable_consensus_error()->set_code(ConsensusErrorPB::INVALID_TERM);
292
2
  StatusToPB(STATUS(InvalidArgument, "Bad term"),
293
2
      response.mutable_consensus_error()->mutable_status());
294
2
  down_cast<DelayablePeerProxy<MockedPeerProxy>*>(proxies_[voter_uuids_[0]])
295
2
      ->proxy()->set_vote_response(response);
296
297
2
  response.Clear();
298
2
  response.set_responder_uuid(voter_uuids_[1]);
299
2
  response.set_responder_term(election_term);
300
2
  response.set_vote_granted(true);
301
2
  down_cast<DelayablePeerProxy<MockedPeerProxy>*>(proxies_[voter_uuids_[1]])
302
2
      ->proxy()->set_vote_response(response);
303
304
2
  VoteRequestPB request;
305
2
  request.set_candidate_uuid(candidate_uuid_);
306
2
  request.set_candidate_term(election_term);
307
2
  request.set_tablet_id(tablet_id_);
308
309
2
  return make_scoped_refptr<LeaderElection>(
310
2
      config_, proxy_factory_.get(), request, std::move(counter), kLeaderElectionTimeout,
311
2
      PreElection::kFalse, TEST_SuppressVoteRequest::kFalse,
312
2
      std::bind(&LeaderElectionTest::ElectionCallback, this, std::placeholders::_1));
313
2
}
314
315
LeaderElectionPtr LeaderElectionTest::SetUpElectionWithGrantDenyErrorVotes(
316
2
    ConsensusTerm election_term, int num_grant, int num_deny, int num_error) {
317
2
  const int kNumVoters = num_grant + num_deny + num_error;
318
2
  CHECK_GE(num_grant, 1);       // Gotta vote for yourself.
319
2
  CHECK_EQ(1, kNumVoters % 2);  // RaftConfig size must be odd.
320
2
  const int kMajoritySize = (kNumVoters / 2) + 1;
321
322
2
  InitUUIDs(kNumVoters, 0);
323
2
  InitDelayableMockedProxies(kNumVoters, 0, 0, 0, false); // Don't delay the vote responses.
324
2
  auto counter = InitVoteCounter(kNumVoters, kMajoritySize);
325
2
  int num_grant_followers = num_grant - 1;
326
327
  // Set up mocked responses based on the params specified in the method arguments.
328
2
  size_t voter_index = 0;
329
10
  while (voter_index < voter_uuids_.size()) {
330
8
    VoteResponsePB response;
331
8
    if (num_grant_followers > 0) {
332
1
      response.set_responder_uuid(voter_uuids_[voter_index]);
333
1
      response.set_responder_term(election_term);
334
1
      response.set_vote_granted(true);
335
1
      --num_grant_followers;
336
7
    } else if (num_deny > 0) {
337
3
      response.set_responder_uuid(voter_uuids_[voter_index]);
338
3
      response.set_responder_term(election_term);
339
3
      response.set_vote_granted(false);
340
3
      response.mutable_consensus_error()->set_code(ConsensusErrorPB::LAST_OPID_TOO_OLD);
341
3
      StatusToPB(STATUS(InvalidArgument, "Last OpId"),
342
3
          response.mutable_consensus_error()->mutable_status());
343
3
      --num_deny;
344
4
    } else if (num_error > 0) {
345
4
      response.mutable_error()->set_code(tserver::TabletServerErrorPB::TABLET_NOT_FOUND);
346
4
      StatusToPB(STATUS(NotFound, "Unknown Tablet"),
347
4
          response.mutable_error()->mutable_status());
348
4
      --num_error;
349
0
    } else {
350
0
      LOG(FATAL) << "Unexpected fallthrough";
351
0
    }
352
353
8
    down_cast<DelayablePeerProxy<MockedPeerProxy>*>(proxies_[voter_uuids_[voter_index]])
354
8
        ->proxy()->set_vote_response(response);
355
8
    ++voter_index;
356
8
  }
357
358
2
  VoteRequestPB request;
359
2
  request.set_candidate_uuid(candidate_uuid_);
360
2
  request.set_candidate_term(election_term);
361
2
  request.set_tablet_id(tablet_id_);
362
363
2
  return make_scoped_refptr<LeaderElection>(
364
2
      config_, proxy_factory_.get(), request, std::move(counter), kLeaderElectionTimeout,
365
2
      PreElection::kFalse, TEST_SuppressVoteRequest::kFalse,
366
2
      std::bind(&LeaderElectionTest::ElectionCallback, this, std::placeholders::_1));
367
2
}
368
369
// All peers respond "yes", no failures.
370
1
TEST_F(LeaderElectionTest, TestPerfectElection) {
371
  // Try configuration sizes of 1, 3, 5.
372
1
  const vector<int> pre_voters_config_sizes = { 0, 1, 3, 5 };
373
1
  const vector<int> pre_observers_config_sizes = { 0, 1, 3, 5 };
374
1
  const vector<int> observers_config_sizes = { 0, 1, 3, 5 };
375
1
  const vector<int> voters_config_sizes = { 1, 3, 5 };
376
377
4
  for (int num_pre_voters : pre_voters_config_sizes) {
378
16
    for (int num_pre_observers : pre_observers_config_sizes) {
379
64
      for (int num_observers : observers_config_sizes) {
380
192
        for (int num_voters : voters_config_sizes) {
381
192
          LOG(INFO) << "Testing election with config { voters: " << num_voters
382
192
                    << ", pre_voters: " << num_pre_voters
383
192
                    << ", pre_observers: " << num_pre_observers
384
192
                    << ", observers: " << num_observers << " }";
385
192
          int majority_size = (num_voters / 2) + 1;
386
192
          ConsensusTerm election_term = 10 + num_voters; // Just to be able to differentiate.
387
388
192
          InitUUIDs(num_voters, num_pre_voters + num_pre_observers + num_observers);
389
192
          InitNoOpPeerProxies(num_voters, num_pre_voters, num_pre_observers, num_observers);
390
192
          auto counter = InitVoteCounter(num_voters, majority_size);
391
392
192
          VoteRequestPB request;
393
192
          LOG(INFO) << "candidate_uuid_: " << candidate_uuid_;
394
192
          request.set_candidate_uuid(candidate_uuid_);
395
192
          request.set_candidate_term(election_term);
396
192
          request.set_tablet_id(tablet_id_);
397
398
192
          auto election = make_scoped_refptr<LeaderElection>(
399
192
              config_, proxy_factory_.get(), request, std::move(counter), kLeaderElectionTimeout,
400
192
              PreElection::kFalse, TEST_SuppressVoteRequest::kFalse,
401
192
              std::bind(&LeaderElectionTest::ElectionCallback, this, std::placeholders::_1));
402
192
          election->Run();
403
192
          latch_.Wait();
404
405
192
          ASSERT_EQ(election_term, result_->election_term);
406
192
          ASSERT_EQ(ElectionVote::kGranted, result_->decision);
407
408
192
          pool_->Wait();
409
192
          FromMapPeerProxyFactory *from_map_proxy_factory =
410
192
              down_cast<FromMapPeerProxyFactory*>(proxy_factory_.get());
411
192
          from_map_proxy_factory->DeleteUnusedPeerProxies();
412
192
          proxies_.clear(); // We don't delete them; The election VoterState object
413
          // ends up owning them.
414
192
          latch_.Reset(1);
415
192
        }
416
64
      }
417
16
    }
418
4
  }
419
1
}
420
421
// Test leader election when we encounter a peer with a higher term before we
422
// have arrived at a majority decision.
423
1
TEST_F(LeaderElectionTest, TestHigherTermBeforeDecision) {
424
1
  const ConsensusTerm kElectionTerm = 2;
425
1
  auto election = SetUpElectionWithHighTermVoter(kElectionTerm);
426
1
  election->Run();
427
428
  // This guy has a higher term.
429
1
  down_cast<DelayablePeerProxy<MockedPeerProxy>*>(proxies_[voter_uuids_[0]])
430
1
      ->Respond(TestPeerProxy::kRequestVote);
431
1
  latch_.Wait();
432
433
1
  ASSERT_EQ(kElectionTerm, result_->election_term);
434
1
  ASSERT_EQ(ElectionVote::kDenied, result_->decision);
435
1
  ASSERT_TRUE(result_->higher_term);
436
1
  ASSERT_EQ(kElectionTerm + 1, *result_->higher_term);
437
1
  LOG(INFO) << "Election lost. Reason: " << result_->message;
438
439
  // This guy will vote "yes".
440
1
  down_cast<DelayablePeerProxy<MockedPeerProxy>*>(proxies_[voter_uuids_[1]])
441
1
      ->Respond(TestPeerProxy::kRequestVote);
442
443
1
  pool_->Wait(); // Wait for the election callbacks to finish before we destroy proxies.
444
1
}
445
446
// Test leader election when we encounter a peer with a higher term after we
447
// have arrived at a majority decision of "yes".
448
1
TEST_F(LeaderElectionTest, TestHigherTermAfterDecision) {
449
1
  const ConsensusTerm kElectionTerm = 2;
450
1
  auto election = SetUpElectionWithHighTermVoter(kElectionTerm);
451
1
  election->Run();
452
453
  // This guy will vote "yes".
454
1
  down_cast<DelayablePeerProxy<MockedPeerProxy>*>(proxies_[voter_uuids_[1]])
455
1
      ->Respond(TestPeerProxy::kRequestVote);
456
1
  latch_.Wait();
457
458
1
  ASSERT_EQ(kElectionTerm, result_->election_term);
459
1
  ASSERT_EQ(ElectionVote::kGranted, result_->decision);
460
1
  ASSERT_FALSE(result_->higher_term);
461
1
  ASSERT_TRUE(result_->message.empty());
462
1
  LOG(INFO) << "Election won.";
463
464
  // This guy has a higher term.
465
1
  down_cast<DelayablePeerProxy<MockedPeerProxy>*>(proxies_[voter_uuids_[0]])
466
1
      ->Respond(TestPeerProxy::kRequestVote);
467
468
1
  pool_->Wait(); // Wait for the election callbacks to finish before we destroy proxies.
469
1
}
470
471
// Out-of-date OpId "vote denied" case.
472
1
TEST_F(LeaderElectionTest, TestWithDenyVotes) {
473
1
  const ConsensusTerm kElectionTerm = 2;
474
1
  const int kNumGrant = 2;
475
1
  const int kNumDeny = 3;
476
1
  const int kNumError = 0;
477
1
  auto election = SetUpElectionWithGrantDenyErrorVotes(
478
1
      kElectionTerm, kNumGrant, kNumDeny, kNumError);
479
1
  LOG(INFO) << "Running";
480
1
  election->Run();
481
482
1
  latch_.Wait();
483
1
  ASSERT_EQ(kElectionTerm, result_->election_term);
484
1
  ASSERT_EQ(ElectionVote::kDenied, result_->decision);
485
1
  ASSERT_FALSE(result_->higher_term);
486
1
  ASSERT_TRUE(result_->message.empty());
487
1
  LOG(INFO) << "Election denied.";
488
489
1
  pool_->Wait(); // Wait for the election callbacks to finish before we destroy proxies.
490
1
}
491
492
// Count errors as denied votes.
493
1
TEST_F(LeaderElectionTest, TestWithErrorVotes) {
494
1
  const ConsensusTerm kElectionTerm = 2;
495
1
  const int kNumGrant = 1;
496
1
  const int kNumDeny = 0;
497
1
  const int kNumError = 4;
498
1
  auto election = SetUpElectionWithGrantDenyErrorVotes(
499
1
      kElectionTerm, kNumGrant, kNumDeny, kNumError);
500
1
  election->Run();
501
502
1
  latch_.Wait();
503
1
  ASSERT_EQ(kElectionTerm, result_->election_term);
504
1
  ASSERT_EQ(ElectionVote::kDenied, result_->decision);
505
1
  ASSERT_FALSE(result_->higher_term);
506
1
  ASSERT_TRUE(result_->message.empty());
507
1
  LOG(INFO) << "Election denied.";
508
509
1
  pool_->Wait(); // Wait for the election callbacks to finish before we destroy proxies.
510
1
}
511
512
////////////////////////////////////////
513
// VoteCounterTest
514
////////////////////////////////////////
515
516
class VoteCounterTest : public YBTest {
517
 protected:
518
  static void AssertUndecided(const VoteCounter& counter);
519
  static void AssertVoteCount(const VoteCounter& counter, int yes_votes, int no_votes);
520
};
521
522
14
void VoteCounterTest::AssertUndecided(const VoteCounter& counter) {
523
14
  ElectionVote decision = counter.GetDecision();
524
14
  ASSERT_EQ(decision, ElectionVote::kUnknown);
525
14
}
526
527
20
void VoteCounterTest::AssertVoteCount(const VoteCounter& counter, int yes_votes, int no_votes) {
528
20
  ASSERT_EQ(yes_votes, counter.yes_votes_);
529
20
  ASSERT_EQ(no_votes, counter.no_votes_);
530
20
  ASSERT_EQ(yes_votes + no_votes, counter.GetTotalVotesCounted());
531
20
}
532
533
// Test basic vote counting functionality with an early majority.
534
1
TEST_F(VoteCounterTest, TestVoteCounter_EarlyDecision) {
535
1
  const int kNumVoters = 3;
536
1
  const int kMajoritySize = 2;
537
1
  vector<string> voter_uuids = GenVoterUUIDs(kNumVoters);
538
539
  // "Yes" decision.
540
1
  {
541
    // Start off undecided.
542
1
    VoteCounter counter(kNumVoters, kMajoritySize);
543
1
    ASSERT_NO_FATALS(AssertUndecided(counter));
544
1
    ASSERT_NO_FATALS(AssertVoteCount(counter, 0, 0));
545
1
    ASSERT_FALSE(counter.AreAllVotesIn());
546
547
    // First yes vote.
548
1
    bool duplicate;
549
1
    ASSERT_OK(counter.RegisterVote(voter_uuids[0], ElectionVote::kGranted, &duplicate));
550
1
    ASSERT_FALSE(duplicate);
551
1
    ASSERT_NO_FATALS(AssertUndecided(counter));
552
1
    ASSERT_NO_FATALS(AssertVoteCount(counter, 1, 0));
553
1
    ASSERT_FALSE(counter.AreAllVotesIn());
554
555
    // Second yes vote wins it in a configuration of 3.
556
1
    ASSERT_OK(counter.RegisterVote(voter_uuids[1], ElectionVote::kGranted, &duplicate));
557
1
    ASSERT_FALSE(duplicate);
558
1
    ASSERT_EQ(counter.GetDecision(), ElectionVote::kGranted);
559
1
    ASSERT_NO_FATALS(AssertVoteCount(counter, 2, 0));
560
1
    ASSERT_FALSE(counter.AreAllVotesIn());
561
1
  }
562
563
  // "No" decision.
564
1
  {
565
    // Start off undecided.
566
1
    VoteCounter counter(kNumVoters, kMajoritySize);
567
1
    ASSERT_NO_FATALS(AssertUndecided(counter));
568
1
    ASSERT_NO_FATALS(AssertVoteCount(counter, 0, 0));
569
1
    ASSERT_FALSE(counter.AreAllVotesIn());
570
571
    // First no vote.
572
1
    bool duplicate;
573
1
    ASSERT_OK(counter.RegisterVote(voter_uuids[0], ElectionVote::kDenied, &duplicate));
574
1
    ASSERT_FALSE(duplicate);
575
1
    ASSERT_NO_FATALS(AssertUndecided(counter));
576
1
    ASSERT_NO_FATALS(AssertVoteCount(counter, 0, 1));
577
1
    ASSERT_FALSE(counter.AreAllVotesIn());
578
579
    // Second no vote loses it in a configuration of 3.
580
1
    ASSERT_OK(counter.RegisterVote(voter_uuids[1], ElectionVote::kDenied, &duplicate));
581
1
    ASSERT_FALSE(duplicate);
582
1
    ASSERT_EQ(counter.GetDecision(), ElectionVote::kDenied);
583
1
    ASSERT_NO_FATALS(AssertVoteCount(counter, 0, 2));
584
1
    ASSERT_FALSE(counter.AreAllVotesIn());
585
1
  }
586
1
}
587
588
// Test basic vote counting functionality with the last vote being the deciding vote.
589
1
TEST_F(VoteCounterTest, TestVoteCounter_LateDecision) {
590
1
  const int kNumVoters = 5;
591
1
  const int kMajoritySize = 3;
592
1
  vector<string> voter_uuids = GenVoterUUIDs(kNumVoters);
593
594
  // Start off undecided.
595
1
  VoteCounter counter(kNumVoters, kMajoritySize);
596
1
  ASSERT_NO_FATALS(AssertUndecided(counter));
597
1
  ASSERT_NO_FATALS(AssertVoteCount(counter, 0, 0));
598
1
  ASSERT_FALSE(counter.AreAllVotesIn());
599
600
  // Add single yes vote, still undecided.
601
1
  bool duplicate;
602
1
  ASSERT_OK(counter.RegisterVote(voter_uuids[0], ElectionVote::kGranted, &duplicate));
603
1
  ASSERT_FALSE(duplicate);
604
1
  ASSERT_NO_FATALS(AssertUndecided(counter));
605
1
  ASSERT_NO_FATALS(AssertVoteCount(counter, 1, 0));
606
1
  ASSERT_FALSE(counter.AreAllVotesIn());
607
608
  // Attempt duplicate vote.
609
1
  ASSERT_OK(counter.RegisterVote(voter_uuids[0], ElectionVote::kGranted, &duplicate));
610
1
  ASSERT_TRUE(duplicate);
611
1
  ASSERT_NO_FATALS(AssertUndecided(counter));
612
1
  ASSERT_NO_FATALS(AssertVoteCount(counter, 1, 0));
613
1
  ASSERT_FALSE(counter.AreAllVotesIn());
614
615
  // Attempt to change vote.
616
1
  Status s = counter.RegisterVote(voter_uuids[0], ElectionVote::kDenied, &duplicate);
617
1
  ASSERT_TRUE(s.IsInvalidArgument());
618
1
  ASSERT_STR_CONTAINS(s.ToString(), "voted a different way twice");
619
1
  LOG(INFO) << "Expected vote-changed error: " << s.ToString();
620
1
  ASSERT_NO_FATALS(AssertUndecided(counter));
621
1
  ASSERT_NO_FATALS(AssertVoteCount(counter, 1, 0));
622
1
  ASSERT_FALSE(counter.AreAllVotesIn());
623
624
  // Add more votes...
625
1
  ASSERT_OK(counter.RegisterVote(voter_uuids[1], ElectionVote::kDenied, &duplicate));
626
1
  ASSERT_FALSE(duplicate);
627
1
  ASSERT_NO_FATALS(AssertUndecided(counter));
628
1
  ASSERT_NO_FATALS(AssertVoteCount(counter, 1, 1));
629
1
  ASSERT_FALSE(counter.AreAllVotesIn());
630
631
1
  ASSERT_OK(counter.RegisterVote(voter_uuids[2], ElectionVote::kGranted, &duplicate));
632
1
  ASSERT_FALSE(duplicate);
633
1
  ASSERT_NO_FATALS(AssertUndecided(counter));
634
1
  ASSERT_NO_FATALS(AssertVoteCount(counter, 2, 1));
635
1
  ASSERT_FALSE(counter.AreAllVotesIn());
636
637
1
  ASSERT_OK(counter.RegisterVote(voter_uuids[3], ElectionVote::kDenied, &duplicate));
638
1
  ASSERT_FALSE(duplicate);
639
1
  ASSERT_NO_FATALS(AssertUndecided(counter));
640
1
  ASSERT_NO_FATALS(AssertVoteCount(counter, 2, 2));
641
1
  ASSERT_FALSE(counter.AreAllVotesIn());
642
643
  // Win the election.
644
1
  ASSERT_OK(counter.RegisterVote(voter_uuids[4], ElectionVote::kGranted, &duplicate));
645
1
  ASSERT_FALSE(duplicate);
646
1
  ASSERT_EQ(counter.GetDecision(), ElectionVote::kGranted);
647
1
  ASSERT_NO_FATALS(AssertVoteCount(counter, 3, 2));
648
1
  ASSERT_TRUE(counter.AreAllVotesIn());
649
650
  // Attempt to vote with > the whole configuration.
651
1
  s = counter.RegisterVote("some-random-node", ElectionVote::kGranted, &duplicate);
652
1
  ASSERT_TRUE(s.IsInvalidArgument());
653
1
  ASSERT_STR_CONTAINS(s.ToString(), "cause the number of votes to exceed the expected number");
654
1
  LOG(INFO) << "Expected voters-exceeded error: " << s.ToString();
655
1
  ASSERT_NE(counter.GetDecision(), ElectionVote::kUnknown);
656
1
  ASSERT_NO_FATALS(AssertVoteCount(counter, 3, 2));
657
1
  ASSERT_TRUE(counter.AreAllVotesIn());
658
1
}
659
660
// Test vote counting with an even number of voters.
661
1
TEST_F(VoteCounterTest, TestVoteCounter_EvenVoters) {
662
1
  const int kNumVoters = 2;
663
1
  const int kMajoritySize = 2;
664
1
  vector<string> voter_uuids = GenVoterUUIDs(kNumVoters);
665
666
  // "Yes" decision.
667
1
  {
668
1
    VoteCounter counter(kNumVoters, kMajoritySize);
669
1
    ASSERT_NO_FATALS(AssertUndecided(counter));
670
1
    ASSERT_NO_FATALS(AssertVoteCount(counter, 0, 0));
671
1
    ASSERT_FALSE(counter.AreAllVotesIn());
672
673
    // Initial yes vote.
674
1
    bool duplicate;
675
1
    ASSERT_OK(counter.RegisterVote(voter_uuids[0], ElectionVote::kGranted, &duplicate));
676
1
    ASSERT_FALSE(duplicate);
677
1
    ASSERT_NO_FATALS(AssertUndecided(counter));
678
1
    ASSERT_NO_FATALS(AssertVoteCount(counter, 1, 0));
679
1
    ASSERT_FALSE(counter.AreAllVotesIn());
680
681
    // Second yes vote wins it.
682
1
    ASSERT_OK(counter.RegisterVote(voter_uuids[1], ElectionVote::kGranted, &duplicate));
683
1
    ASSERT_FALSE(duplicate);
684
1
    ASSERT_EQ(counter.GetDecision(), ElectionVote::kGranted);
685
1
    ASSERT_NO_FATALS(AssertVoteCount(counter, 2, 0));
686
1
    ASSERT_TRUE(counter.AreAllVotesIn());
687
1
  }
688
689
  // "No" decision.
690
1
  {
691
1
    VoteCounter counter(kNumVoters, kMajoritySize);
692
1
    ASSERT_NO_FATALS(AssertUndecided(counter));
693
1
    ASSERT_NO_FATALS(AssertVoteCount(counter, 0, 0));
694
1
    ASSERT_FALSE(counter.AreAllVotesIn());
695
696
    // The first "no" vote guarantees a failed election when num voters == 2.
697
1
    bool duplicate;
698
1
    ASSERT_OK(counter.RegisterVote(voter_uuids[0], ElectionVote::kDenied, &duplicate));
699
1
    ASSERT_FALSE(duplicate);
700
1
    ASSERT_EQ(counter.GetDecision(), ElectionVote::kDenied);
701
1
    ASSERT_NO_FATALS(AssertVoteCount(counter, 0, 1));
702
1
    ASSERT_FALSE(counter.AreAllVotesIn());
703
1
  }
704
1
}
705
706
}  // namespace consensus
707
}  // namespace yb