Exemplo n.º 1
0
/* is it safe to commit the grant?
 * if we didn't hear from all sites on the initial grant, we may
 * need to delay the commit
 *
 * TODO: investigate possibility to devise from history whether a
 * missing site could be holding a ticket or not
 */
static int ticket_dangerous(struct ticket_config *tk)
{
	int tdiff;
	/* we may be invoked often, don't spam the log unnecessarily
	 */
	static int no_log_delay_msg;

	if (!is_time_set(&tk->delay_commit))
		return 0;

	if (is_past(&tk->delay_commit) || all_sites_replied(tk)) {
		if (tk->leader == local) {
			tk_log_info("%s, committing to CIB",
				is_past(&tk->delay_commit) ?
				"ticket delay expired" : "all sites replied");
		}
		time_reset(&tk->delay_commit);
		no_log_delay_msg = 0;
		return 0;
	}

	tdiff = time_left(&tk->delay_commit);
	tk_log_debug("delay ticket commit for another " intfmt(tdiff));
	if (!no_log_delay_msg) {
		tk_log_info("delaying ticket commit to CIB for " intfmt(tdiff));
		tk_log_info("(or all sites are reached)");
		no_log_delay_msg = 1;
	}

	return 1;
}
Exemplo n.º 2
0
Arquivo: raft.c Projeto: jnpkrn/booth
void elections_end(struct ticket_config *tk)
{
	struct booth_site *new_leader;

	if (is_past(&tk->election_end)) {
		/* This is previous election timed out */
		tk_log_info("elections finished");
	}

	tk->in_election = 0;
	new_leader = majority_votes(tk);
	if (new_leader == local) {
		won_elections(tk);
		tk_log_info("granted successfully here");
	} else if (new_leader) {
		tk_log_info("ticket granted at %s",
				site_string(new_leader));
	} else {
		tk_log_info("nobody won elections, new elections");
		tk->outcome = RLT_MORE;
		foreach_tkt_req(tk, notify_client);
		if (!new_election(tk, NULL, is_tie(tk) ? 2 : 0, OR_AGAIN)) {
			ticket_activate_timeout(tk);
		}
	}
}
Exemplo n.º 3
0
Arquivo: raft.c Projeto: jnpkrn/booth
static int process_VOTE_FOR(
		struct ticket_config *tk,
		struct booth_site *sender,
		struct booth_site *leader,
		struct boothc_ticket_msg *msg
		)
{
	if (leader == no_leader) {
		/* leader wants to step down? */
		if (sender == tk->leader &&
			(tk->state == ST_FOLLOWER || tk->state == ST_CANDIDATE)) {
			tk_log_info("%s wants to give the ticket away (ticket release)",
				site_string(tk->leader));
			save_committed_tkt(tk);
			reset_ticket(tk);
			set_state(tk, ST_FOLLOWER);
			if (local->type == SITE) {
				ticket_write(tk);
				schedule_election(tk, OR_STEPDOWN);
			}
		} else {
			tk_log_info("%s votes for none, ignoring (duplicate ticket release?)",
				site_string(sender));
		}
		return 0;
	}

	if (tk->state != ST_CANDIDATE) {
		/* lost candidate status, somebody rejected our proposal */
		tk_log_info("candidate status lost, ignoring VtFr from %s",
			site_string(sender));
		return 0;
	}

	if (term_too_low(tk, sender, leader, msg))
		return 0;

	if (newer_term(tk, sender, leader, msg, 0)) {
		clear_election(tk);
	}

	record_vote(tk, sender, leader);

	/* only if all voted can we take the ticket now, otherwise
	 * wait for timeout in ticket_cron */
	if (!tk->acks_expected) {
		/* §5.2 */
		elections_end(tk);
	}

	return 0;
}
Exemplo n.º 4
0
Arquivo: raft.c Projeto: jnpkrn/booth
static int process_REVOKE (
		struct ticket_config *tk,
		struct booth_site *sender,
		struct booth_site *leader,
		struct boothc_ticket_msg *msg
	       )
{
	int rv;

	if (tk->state == ST_INIT && tk->leader == no_leader) {
		/* assume that our ack got lost */
		rv = send_msg(OP_ACK, tk, sender, msg);
	} else if (tk->leader != sender) {
		tk_log_error("%s wants to revoke ticket, "
				"but it is not granted there (ignoring)",
				site_string(sender));
		return 1;
	} else if (tk->state != ST_FOLLOWER) {
		tk_log_error("unexpected ticket revoke from %s "
				"(in state %s) (ignoring)",
				site_string(sender),
				state_to_string(tk->state));
		return 1;
	} else {
		tk_log_info("%s revokes ticket",
				site_string(tk->leader));
		save_committed_tkt(tk);
		reset_ticket(tk);
		set_leader(tk, no_leader);
		ticket_write(tk);
		rv = send_msg(OP_ACK, tk, sender, msg);
	}

	return rv;
}
Exemplo n.º 5
0
/** Try to get the ticket for the local site.
 * */
int do_grant_ticket(struct ticket_config *tk, int options)
{
	int rv;

	tk_log_info("granting ticket");

	if (tk->leader == local)
		return RLT_SUCCESS;
	if (is_owned(tk))
		return RLT_OVERGRANT;

	set_future_time(&tk->delay_commit, tk->term_duration + tk->acquire_after);

	if (options & OPT_IMMEDIATE) {
		tk_log_warn("granting ticket immediately! If there are "
				"unreachable sites, _hope_ you are sure that they don't "
				"have the ticket!");
		time_reset(&tk->delay_commit);
	}

	rv = acquire_ticket(tk, OR_ADMIN);
	if (rv) {
		time_reset(&tk->delay_commit);
		return rv;
	} else {
		return RLT_MORE;
	}
}
Exemplo n.º 6
0
Arquivo: raft.c Projeto: jnpkrn/booth
static int newer_term(struct ticket_config *tk,
		struct booth_site *sender,
		struct booth_site *leader,
		struct boothc_ticket_msg *msg,
		int in_election)
{
	uint32_t term;

	/* it may happen that we hear about our newer term */
	if (leader == local)
		return 0;

	term = ntohl(msg->ticket.term);
	/* §5.1 */
	if (term > tk->current_term) {
		set_state(tk, ST_FOLLOWER);
		if (!in_election) {
			set_leader(tk, leader);
			tk_log_info("from %s: higher term %d vs. %d, following %s",
					site_string(sender),
					term, tk->current_term,
					ticket_leader_string(tk));
		} else {
			tk_log_debug("from %s: higher term %d vs. %d (election)",
					site_string(sender),
					term, tk->current_term);
		}

		tk->current_term = term;
		return 1;
	}

	return 0;
}
Exemplo n.º 7
0
static void start_revoke_ticket(struct ticket_config *tk)
{
	tk_log_info("revoking ticket");

	save_committed_tkt(tk);
	reset_ticket_and_set_no_leader(tk);
	ticket_write(tk);
	ticket_broadcast(tk, OP_REVOKE, OP_ACK, RLT_SUCCESS, OR_ADMIN);
}
Exemplo n.º 8
0
/** Ticket revoke.
 * Only to be started from the leader. */
int do_revoke_ticket(struct ticket_config *tk)
{
	if (tk->acks_expected) {
		tk_log_info("delay ticket revoke until the current operation finishes");
		set_next_state(tk, ST_INIT);
		return RLT_MORE;
	} else {
		start_revoke_ticket(tk);
		return RLT_SUCCESS;
	}
}
Exemplo n.º 9
0
Arquivo: raft.c Projeto: jnpkrn/booth
static void update_ticket_from_msg(struct ticket_config *tk,
		struct booth_site *sender,
		struct boothc_ticket_msg *msg)
{
	int duration;

	tk_log_info("updating from %s (%d/%d)",
		site_string(sender),
		ntohl(msg->ticket.term), msg_term_time(msg));
	duration = min(tk->term_duration, msg_term_time(msg));
	set_ticket_expiry(tk, duration);
	update_term_from_msg(tk, msg);
}
Exemplo n.º 10
0
Arquivo: raft.c Projeto: jnpkrn/booth
static int msg_term_invalid(struct ticket_config *tk,
		struct booth_site *sender,
		struct booth_site *leader,
		struct boothc_ticket_msg *msg)
{
	uint32_t term;

	term = ntohl(msg->ticket.term);
	/* §5.1 */
	if (is_term_invalid(tk, term)) {
		tk_log_info("got invalid term from %s "
			"(%d), ignoring", site_string(sender), term);
		return 1;
	}

	return 0;
}
Exemplo n.º 11
0
void update_ticket_state(struct ticket_config *tk, struct booth_site *sender)
{
	if (tk->state == ST_CANDIDATE) {
		tk_log_info("learned from %s about "
				"newer ticket, stopping elections",
				site_string(sender));
		/* there could be rejects coming from others; don't log
		 * warnings unnecessarily */
		tk->expect_more_rejects = 1;
	}

	if (tk->leader == local || tk->is_granted) {
		/* message from a live leader with valid ticket? */
		if (sender == tk->leader && term_time_left(tk)) {
			if (tk->is_granted) {
				tk_log_warn("ticket was granted here, "
						"but it's live at %s (revoking here)",
						site_string(sender));
			} else {
				tk_log_info("ticket live at %s",
						site_string(sender));
			}
			disown_ticket(tk);
			ticket_write(tk);
			set_state(tk, ST_FOLLOWER);
			set_next_state(tk, ST_FOLLOWER);
		} else {
			if (tk->state == ST_CANDIDATE) {
				set_state(tk, ST_FOLLOWER);
			}
			set_next_state(tk, ST_LEADER);
		}
	} else {
		if (!tk->leader || tk->leader == no_leader) {
			if (sender)
				tk_log_info("ticket is not granted");
			else
				tk_log_info("ticket is not granted (from CIB)");
			set_state(tk, ST_INIT);
		} else {
			if (sender)
				tk_log_info("ticket granted to %s (says %s)",
					site_string(tk->leader),
					tk->leader == sender ? "they" : site_string(sender));
			else
				tk_log_info("ticket granted to %s (from CIB)",
					site_string(tk->leader));
			set_state(tk, ST_FOLLOWER);
			/* just make sure that we check the ticket soon */
			set_next_state(tk, ST_FOLLOWER);
		}
	}
}
Exemplo n.º 12
0
Arquivo: raft.c Projeto: jnpkrn/booth
static int term_too_low(struct ticket_config *tk,
		struct booth_site *sender,
		struct booth_site *leader,
		struct boothc_ticket_msg *msg)
{
	uint32_t term;

	term = ntohl(msg->ticket.term);
	/* §5.1 */
	if (term < tk->current_term) {
		tk_log_info("sending reject to %s, its term too low "
			"(%d vs. %d)", site_string(sender),
			term, tk->current_term
			);
		send_reject(sender, tk, RLT_TERM_OUTDATED, msg);
		return 1;
	}

	return 0;
}
Exemplo n.º 13
0
Arquivo: raft.c Projeto: jnpkrn/booth
/* For follower. */
static int answer_HEARTBEAT (
		struct ticket_config *tk,
		struct booth_site *sender,
		struct booth_site *leader,
		struct boothc_ticket_msg *msg
	       )
{
	uint32_t term;

	term = ntohl(msg->ticket.term);
	tk_log_debug("heartbeat from leader: %s, have %s; term %d vs %d",
			site_string(leader), ticket_leader_string(tk),
			term, tk->current_term);

	if (term < tk->current_term) {
		if (sender == tk->leader) {
			tk_log_info("trusting leader %s with a lower term (%d vs %d)",
				site_string(leader), term, tk->current_term);
		} else if (is_owned(tk)) {
			tk_log_warn("different leader %s with a lower term "
					"(%d vs %d), sending reject",
				site_string(leader), term, tk->current_term);
			return send_reject(sender, tk, RLT_TERM_OUTDATED, msg);
		}
	}

	/* got heartbeat, no rejects expected anymore */
	tk->expect_more_rejects = 0;

	/* Needed? */
	newer_term(tk, sender, leader, msg, 0);

	become_follower(tk, msg);
	/* Racy??? */
	assert(sender == leader || !leader);

	set_leader(tk, leader);

	/* Ack the heartbeat (we comply). */
	return send_msg(OP_ACK, tk, sender, msg);
}
Exemplo n.º 14
0
int setup_ticket(void)
{
	struct ticket_config *tk;
	int i;

	foreach_ticket(i, tk) {
		reset_ticket(tk);

		if (local->type == SITE) {
			if (!pcmk_handler.load_ticket(tk)) {
				update_ticket_state(tk, NULL);
			}
			tk->update_cib = 1;
		}

		tk_log_info("broadcasting state query");
		/* wait until all send their status (or the first
		 * timeout) */
		tk->start_postpone = 1;
		ticket_broadcast(tk, OP_STATUS, OP_MY_INDEX, RLT_SUCCESS, 0);
	}
Exemplo n.º 15
0
int ticket_write(struct ticket_config *tk)
{
	if (local->type != SITE)
		return -EINVAL;

	if (ticket_dangerous(tk))
		return 1;

	if (tk->leader == local) {
		if (tk->state != ST_LEADER) {
			tk_log_info("ticket state not yet consistent, "
				"delaying ticket grant to CIB");
			return 1;
		}
		pcmk_handler.grant_ticket(tk);
	} else {
		pcmk_handler.revoke_ticket(tk);
	}
	tk->update_cib = 0;

	return 0;
}
Exemplo n.º 16
0
/** Try to get the ticket for the local site.
 * */
int do_grant_ticket(struct ticket_config *tk, int options)
{
	int rv;

	tk_log_info("granting ticket");

	if (tk->leader == local)
		return RLT_SUCCESS;
	if (is_owned(tk)) {
		if (is_manual(tk) && (options & OPT_IMMEDIATE)) {
			/* -F flag has been used while granting a manual ticket.
			 * The ticket will be granted and may end up being granted
			 * on multiple sites */
			tk_log_warn("manual ticket forced to be granted! be aware that "
					"you may end up having two sites holding the same manual "
					"ticket! revoke the ticket from the unnecessary site!");
		} else {
			return RLT_OVERGRANT;
		}
	}

	set_future_time(&tk->delay_commit, tk->term_duration + tk->acquire_after);

	if (options & OPT_IMMEDIATE) {
		tk_log_warn("granting ticket immediately! If there are "
				"unreachable sites, _hope_ you are sure that they don't "
				"have the ticket!");
		time_reset(&tk->delay_commit);
	}

	rv = acquire_ticket(tk, OR_ADMIN);
	if (rv) {
		time_reset(&tk->delay_commit);
		return rv;
	} else {
		return RLT_MORE;
	}
}
Exemplo n.º 17
0
Arquivo: raft.c Projeto: jnpkrn/booth
/* reply to STATUS */
static int process_MY_INDEX (
		struct ticket_config *tk,
		struct booth_site *sender,
		struct booth_site *leader,
		struct boothc_ticket_msg *msg
	       )
{
	int i;
	int expired;

	expired = !msg_term_time(msg);
	/* test against the last valid(!) ticket we have */
	i = my_last_term(tk) - ntohl(msg->ticket.term);

	if (i > 0) {
		/* let them know about our newer ticket */
		send_msg(OP_MY_INDEX, tk, sender, msg);
		if (tk->state == ST_LEADER) {
			tk_log_info("sending ticket update to %s",
					site_string(sender));
			return send_msg(OP_UPDATE, tk, sender, msg);
		}
	}

	/* we have a newer or equal ticket and theirs is expired,
	 * nothing more to do here */
	if (i >= 0 && expired) {
		return 0;
	}

	if (tk->state == ST_LEADER) {
		/* we're the leader, thread carefully */
		if (expired) {
			/* if their ticket is expired,
			 * nothing more to do */
			return 0;
		}
		if (i < 0) {
			/* they have a newer ticket, trouble if we're already leader
			 * for it */
			tk_log_warn("from %s: more up to date ticket at %s",
					site_string(sender),
					site_string(leader)
					);
			return leader_handle_newer_ticket(tk, sender, leader, msg);
		} else {
			/* we have the ticket and we don't care */
			return 0;
		}
	} else if (tk->state == ST_CANDIDATE) {
		if (leader == local) {
			/* a belated MY_INDEX, we're already trying to get the
			 * ticket */
			return 0;
		}
	}

	/* their ticket is either newer or not expired, don't
	 * ignore it */
	update_ticket_from_msg(tk, sender, msg);
	set_leader(tk, leader);
	update_ticket_state(tk, sender);
	save_committed_tkt(tk);
	set_ticket_wakeup(tk);
	return 0;
}
Exemplo n.º 18
0
Arquivo: raft.c Projeto: jnpkrn/booth
int new_election(struct ticket_config *tk,
	struct booth_site *preference, int update_term, cmd_reason_t reason)
{
	struct booth_site *new_leader;

	if (local->type != SITE)
		return 0;

	if ((is_reason(OR_TKT_LOST, tk) || is_reason(OR_STEPDOWN, tk)) &&
			check_attr_prereq(tk, GRANT_AUTO)) {
		tk_log_info("attribute prerequisite not met, "
			"not starting elections");
		return 0;
	}

	/* elections were already started, but not yet finished/timed out */
	if (is_time_set(&tk->election_end) && !is_past(&tk->election_end))
		return 1;

	if (ANYDEBUG) {
		int tdiff;
		if (is_time_set(&tk->election_end)) {
			tdiff = -time_left(&tk->election_end);
			tk_log_debug("starting elections, previous finished since " intfmt(tdiff));
		} else {
			tk_log_debug("starting elections");
		}
		tk_log_debug("elections caused by %s %s",
				state_to_string(reason),
				reason == OR_AGAIN ? state_to_string(tk->election_reason) : "" );
	}

	/* §5.2 */
	/* If there was _no_ answer, don't keep incrementing the term number
	 * indefinitely. If there was no peer, there'll probably be no one
	 * listening now either. However, we don't know if we were
	 * invoked due to a timeout (caller does).
	 */
	/* increment the term only if either the current term was
	 * valid or if there was a tie (in that case update_term > 1)
	 */
	if ((update_term > 1) ||
		(update_term && tk->last_valid_tk &&
			tk->last_valid_tk->current_term >= tk->current_term)) {
		/* save the previous term, we may need to send out the
		 * MY_INDEX message */
		if (tk->state != ST_CANDIDATE) {
			save_committed_tkt(tk);
		}
		tk->current_term++;
	}

	set_future_time(&tk->election_end, tk->timeout);
	tk->in_election = 1;

	tk_log_info("starting new election (term=%d)",
			tk->current_term);
	clear_election(tk);

	if(preference)
		new_leader = preference;
	else
		new_leader = (local->type == SITE) ? local : NULL;
	record_vote(tk, local, new_leader);
	tk->voted_for = new_leader;

	set_state(tk, ST_CANDIDATE);

	/* some callers may want just to repeat on timeout */
	if (reason == OR_AGAIN) {
		reason = tk->election_reason;
	} else {
		tk->election_reason = reason;
	}

	ticket_broadcast(tk, OP_REQ_VOTE, OP_VOTE_FOR, RLT_SUCCESS, reason);
	add_random_delay(tk);
	return 0;
}