Skip to content

Commit

Permalink
Add current term to server status cache
Browse files Browse the repository at this point in the history
Summary:
Add the ability to query a local RAFT server's current term from the
`wa_raft_info` module which caches the RAFT server's most recently seen
term in ETS to avoid requiring a status request against the RAFT server.
Additional cleanup to the `wa_raft_info` module.

Differential Revision: D53275651

fbshipit-source-id: f3e25ee87cbb2f36493c0b3ee02e5805c3b0440e
  • Loading branch information
hsun324 authored and facebook-github-bot committed Jan 31, 2024
1 parent 33b56ae commit 3f8b9d3
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 43 deletions.
111 changes: 69 additions & 42 deletions src/wa_raft_info.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,74 +3,101 @@
%%% This source code is licensed under the Apache 2.0 license found in
%%% the LICENSE file in the root directory of this source tree.
%%%
%%% Module to interface with raft metadata
%%% API for accessing certain useful information about the state of local
%%% RAFT servers without requiring a status request against the RAFT server
%%% itself.

-module(wa_raft_info).
-compile(warn_missing_spec_all).

%% Public API
-export([
get_state/2,
get_current_term/2,
get_leader/2,
get_stale/2
]).

%% Internal API
-export([
init_tables/0,
set_state/3,
get_state/2,
delete_state/2,
set_current_term/3,
set_leader/3,
get_leader/2,
set_stale/3,
get_stale/2
set_stale/3
]).

%% Raft leader node
-define(RAFT_LEADER_NODE(Table, Partition), {leader, Table, Partition}).
%% Raft server state
-define(RAFT_SERVER_STATE(Table, Partition), {state, Table, Partition}).
%% Raft stale flag
-define(RAFT_STALE_FLAG(Table, Partition), {stale, Table, Partition}).
%% Local RAFT server's current FSM state
-define(RAFT_SERVER_STATE_KEY(Table, Partition), {state, Table, Partition}).
%% Local RAFT server's most recently known term
-define(RAFT_CURRENT_TERM_KEY(Table, Partition), {term, Table, Partition}).
%% Local RAFT server's most recently known leader node
-define(RAFT_CURRENT_LEADER_KEY(Table, Partition), {leader, Table, Partition}).
%% Local RAFT server's current stale flag - indicates if the server thinks its data is stale
-define(RAFT_STALE_KEY(Table, Partition), {stale, Table, Partition}).

%%-------------------------------------------------------------------
%% RAFT Info - Public API
%%-------------------------------------------------------------------

-spec get(term(), Default) -> Default.
get(Key, Default) ->
try
ets:lookup_element(?MODULE, Key, 2)
catch error:badarg ->
Default
end.

-spec get_leader(wa_raft:table(), wa_raft:partition()) -> node() | undefined.
get_leader(Table, Partition) ->
get(?RAFT_CURRENT_LEADER_KEY(Table, Partition), undefined).

-spec get_current_term(wa_raft:table(), wa_raft:partition()) -> wa_raft_log:log_term() | undefined.
get_current_term(Table, Partition) ->
get(?RAFT_CURRENT_TERM_KEY(Table, Partition), undefined).

-spec get_state(wa_raft:table(), wa_raft:partition()) -> wa_raft_server:state() | undefined.
get_state(Table, Partition) ->
get(?RAFT_SERVER_STATE_KEY(Table, Partition), undefined).

-spec get_stale(wa_raft:table(), wa_raft:partition()) -> boolean().
get_stale(Table, Partition) ->
get(?RAFT_STALE_KEY(Table, Partition), true).

%%-------------------------------------------------------------------
%% RAFT Info - Internal API
%%-------------------------------------------------------------------

-spec init_tables() -> ok.
init_tables() ->
ets:new(?MODULE, [set, public, named_table, {write_concurrency, true}, {read_concurrency, true}]),
ok.

-spec set(term(), term()) -> true.
set(Key, Value) ->
ets:update_element(?MODULE, Key, {2, Value}) orelse ets:insert(?MODULE, {Key, Value}).

-spec delete(term()) -> true.
delete(Key) ->
ets:delete(?MODULE, Key).

-spec set_leader(wa_raft:table(), wa_raft:partition(), node()) -> true.
set_leader(Table, Partition, Value) ->
ets:update_element(?MODULE, ?RAFT_LEADER_NODE(Table, Partition), {2, Value})
orelse ets:insert(?MODULE, {?RAFT_LEADER_NODE(Table, Partition), Value}).
set(?RAFT_CURRENT_LEADER_KEY(Table, Partition), Value).

-spec get_leader(wa_raft:table(), wa_raft:partition()) -> node() | undefined.
get_leader(Table, Partition) ->
try
ets:lookup_element(?MODULE, ?RAFT_LEADER_NODE(Table, Partition), 2)
catch error:badarg ->
undefined
end.
-spec set_current_term(wa_raft:table(), wa_raft:partition(), wa_raft_log:log_term()) -> true.
set_current_term(Table, Partition, Term) ->
set(?RAFT_CURRENT_TERM_KEY(Table, Partition), Term).

-spec set_state(wa_raft:table(), wa_raft:partition(), wa_raft_server:state()) -> true.
set_state(Table, Partition, State) ->
ets:update_element(?MODULE, ?RAFT_SERVER_STATE(Table, Partition), {2, State})
orelse ets:insert(?MODULE, {?RAFT_SERVER_STATE(Table, Partition), State}).

-spec get_state(wa_raft:table(), wa_raft:partition()) -> wa_raft_server:state() | undefined.
get_state(Table, Partition) ->
try
ets:lookup_element(?MODULE, ?RAFT_SERVER_STATE(Table, Partition), 2)
catch error:badarg ->
undefined
end.
set(?RAFT_SERVER_STATE_KEY(Table, Partition), State).

-spec delete_state(wa_raft:table(), wa_raft:partition()) -> true.
delete_state(Table, Partition) ->
ets:delete(?MODULE, ?RAFT_SERVER_STATE(Table, Partition)).
delete(?RAFT_SERVER_STATE_KEY(Table, Partition)).

%% Set to true if data on current node is stale. Read on this node may return out-of-dated data
-spec set_stale(wa_raft:table(), wa_raft:partition(), boolean()) -> true.
set_stale(Table, Partition, Stale) ->
ets:update_element(?MODULE, ?RAFT_STALE_FLAG(Table, Partition), {2, Stale})
orelse ets:insert(?MODULE, {?RAFT_STALE_FLAG(Table, Partition), Stale}).

-spec get_stale(wa_raft:table(), wa_raft:partition()) -> boolean().
get_stale(Table, Partition) ->
try
ets:lookup_element(?MODULE, ?RAFT_STALE_FLAG(Table, Partition), 2)
catch error:badarg ->
true
end.
set(?RAFT_STALE_KEY(Table, Partition), Stale).
6 changes: 5 additions & 1 deletion src/wa_raft_server.erl
Original file line number Diff line number Diff line change
Expand Up @@ -435,6 +435,7 @@ init(#raft_options{application = Application, table = Table, partition = Partiti
{ok, NewState} -> NewState;
_ -> State1
end,
true = wa_raft_info:set_current_term(Table, Partition, State2#raft_state.current_term),
% 1. Begin as disabled if a disable reason is set
% 2. Begin as witness if configured
% 3. Begin as stalled if there is no data
Expand Down Expand Up @@ -2094,7 +2095,9 @@ check_stale_upon_entry(_StateName, _Now, #raft_state{table = Table, partition =

%% Set a new current term and voted-for peer and clear any state that is associated with the previous term.
-spec advance_term(StateName :: state(), NewerTerm :: wa_raft_log:log_term(), VotedFor :: undefined | node(), State :: #raft_state{}) -> #raft_state{}.
advance_term(StateName, NewerTerm, VotedFor, #raft_state{current_term = CurrentTerm} = State0) when NewerTerm > CurrentTerm ->
advance_term(StateName, NewerTerm, VotedFor,
#raft_state{table = Table, partition = Partition,
current_term = CurrentTerm} = State0) when NewerTerm > CurrentTerm ->
State1 = clear_leader(StateName, State0),
State2 = State1#raft_state{
current_term = NewerTerm,
Expand All @@ -2107,6 +2110,7 @@ advance_term(StateName, NewerTerm, VotedFor, #raft_state{current_term = CurrentT
handover = undefined
},
ok = wa_raft_durable_state:store(State2),
true = wa_raft_info:set_current_term(Table, Partition, NewerTerm),
State2.

%%-------------------------------------------------------------------
Expand Down

0 comments on commit 3f8b9d3

Please sign in to comment.