Skip to content

Commit d360853

Browse files
committed
mon/ElectionLogic: tie-breaker ignore proposal from marked down mon
Problem: Monitor election gets stuck, resulting in the cluster being unaccessible. Set up stretch cluster: DC1: mon.a,mon.b DC2: mon.c, mon.d Arbiter: mon.e Apply netsplit between DC1 and DC2, wait around 10 seconds, client tried accessing the cluster ... failed to access. We expect the cluster to be functional when there is netsplit. This was due to how we suppose to kick one DC out of quorum and keep them in the dark until the connection comes back (netsplit gone). However, there is a small window where the out-of-quorum DC is able to have an influence on the tiebreaker MON by forcing the tiebreaker MON to bump its epoch which then it will reject the winner MON's victory message from in-quorum DC. Solution: In CONNECTIVITY election strategy, tie-breaker MON disregard any proposal that is coming from MONs that belongs to `stretch_marked_down_mons` set. As a result, the out-of-quorum MONs won't get the chance to force the tie-breaker MON to increase its epoch, hence will now accept the victory message from the in-quorum Monitors. Also, edited src/test/mon/test_election.cc to include `is_stretch_marked_down_mon` and `is_tiebreaker`, preventing the make check from breaking. Fixes: https://tracker.ceph.com/issues/66471 Signed-off-by: Kamoltat <[email protected]>
1 parent 6fc2885 commit d360853

File tree

6 files changed

+63
-1
lines changed

6 files changed

+63
-1
lines changed

src/mon/ElectionLogic.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,12 @@ void ElectionLogic::propose_connectivity_handler(int from, epoch_t mepoch,
335335
ldout(cct, 10) << __func__ << " from " << from << " mepoch: "
336336
<< mepoch << " epoch: " << epoch << dendl;
337337
ldout(cct, 30) << "last_election_winner: " << last_election_winner << dendl;
338+
// ignore proposal from marked down mons if we are the tiebreaker
339+
if (elector->is_tiebreaker(elector->get_my_rank()) &&
340+
elector->is_stretch_marked_down_mons(from)) {
341+
ldout(cct, 10) << "Ignoring proposal from marked down mon " << from << dendl;
342+
return;
343+
}
338344
if ((epoch % 2 == 0) &&
339345
last_election_winner != elector->get_my_rank() &&
340346
!elector->is_current_member(from)) {

src/mon/ElectionLogic.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,18 @@ class ElectionOwner {
8282
* @returns true if we have participated, false otherwise
8383
*/
8484
virtual bool ever_participated() const = 0;
85+
/**
86+
* Check if the monitor is the tiebreaker in a stretch cluster.
87+
*
88+
* @returns true if the Monitor is the tiebreaker, false otherwise.
89+
*/
90+
virtual bool is_tiebreaker(int rank) const = 0;
91+
/**
92+
* Check if the Monitor is marked down in a stretch cluster.
93+
*
94+
* @returns true if the Monitor in a stretch cluster is marked down, false otherwise.
95+
*/
96+
virtual bool is_stretch_marked_down_mons(int rank) const = 0;
8597
/**
8698
* Ask the ElectionOwner for the size of the Paxos set. This includes
8799
* those monitors which may not be in the current quorum!

src/mon/Elector.cc

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,22 @@ bool Elector::peer_tracker_is_clean()
722722
return peer_tracker.is_clean(mon->rank, paxos_size());
723723
}
724724

725+
bool Elector::is_tiebreaker(int rank) const
726+
{
727+
return mon->monmap->tiebreaker_mon == mon->monmap->get_name(rank);
728+
}
729+
730+
bool Elector::is_stretch_marked_down_mons(int rank) const
731+
{
732+
std::string mon_name = mon->monmap->get_name(rank);
733+
for (auto& i : mon->monmap->stretch_marked_down_mons) {
734+
if (i == mon_name) {
735+
return true;
736+
}
737+
}
738+
return false;
739+
}
740+
725741
void Elector::notify_clear_peer_state()
726742
{
727743
dout(10) << __func__ << dendl;

src/mon/Elector.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,19 @@ class Elector : public ElectionOwner, RankProvider {
244244
/* Right now we don't disallow anybody */
245245
std::set<int> disallowed_leaders;
246246
const std::set<int>& get_disallowed_leaders() const { return disallowed_leaders; }
247+
/**
248+
* Check if the monitor is the tiebreaker in a stretch cluster.
249+
*
250+
* @returns true if the Monitor is the tiebreaker, false otherwise.
251+
*/
252+
bool is_tiebreaker(int rank) const;
253+
/**
254+
* Check if the mon is marked dwon in stretch mode.
255+
*
256+
* @returns true if the monitor is marked down in stretch mode,
257+
* otherwise return false.
258+
*/
259+
bool is_stretch_marked_down_mons(int from) const;
247260
/**
248261
* Reset the expire_event timer so we can limit the amount of time we
249262
* will be electing. Clean up our peer_info.

src/mon/MonMap.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,8 @@ class MonMap {
165165
std::set<std::string> disallowed_leaders; // can't be leader under CONNECTIVITY/DISALLOW
166166
bool stretch_mode_enabled = false;
167167
std::string tiebreaker_mon;
168-
std::set<std::string> stretch_marked_down_mons; // can't be leader until fully recovered
168+
std::set<std::string> stretch_marked_down_mons; // can't be leader or taken proposal in CONNECTIVITY
169+
// seriously until fully recovered
169170

170171
public:
171172
void calc_legacy_ranks();

src/test/mon/test_election.cc

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ struct Owner : public ElectionOwner, RankProvider {
103103
bool timer_election; // the timeout is for normal election, or victory
104104
bool rank_deleted = false;
105105
string prefix_str;
106+
set<int> stretch_marked_down_mons;
107+
int tiebreaker_mon_rank;
106108
Owner(int r, ElectionLogic::election_strategy es, double tracker_halflife,
107109
Election *p) : parent(p), rank(r), persisted_epoch(0),
108110
ever_joined(false),
@@ -187,6 +189,18 @@ struct Owner : public ElectionOwner, RankProvider {
187189
quorum = members;
188190
victory_accepters = 1;
189191
}
192+
bool is_stretch_marked_down_mons(int rank) const {
193+
for (auto& i : stretch_marked_down_mons) {
194+
if (i == rank) {
195+
return true;
196+
}
197+
}
198+
return false;
199+
}
200+
bool is_tiebreaker(int rank) const
201+
{
202+
return tiebreaker_mon_rank == rank;
203+
}
190204
bool is_current_member(int r) const { return quorum.count(r) != 0; }
191205
void receive_propose(int from, epoch_t e, ConnectionTracker *oct) {
192206
if (rank_deleted) return;

0 commit comments

Comments
 (0)