fix(clusterlink): ensure extrouter works on replicants
This is sort of a quick fix to make things safe, but it will likely be a subject to the same drawbacks as the regular router in high-latency deployments: reduced throughput.
This commit is contained in:
parent
a53524c826
commit
4097585f5d
|
@ -20,6 +20,14 @@
|
||||||
actor_gc/1
|
actor_gc/1
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
%% Internal API
|
||||||
|
-export([
|
||||||
|
mnesia_actor_init/3,
|
||||||
|
mnesia_actor_heartbeat/3,
|
||||||
|
mnesia_clean_incarnation/1,
|
||||||
|
apply_actor_operation/5
|
||||||
|
]).
|
||||||
|
|
||||||
%% Strictly monotonically increasing integer.
|
%% Strictly monotonically increasing integer.
|
||||||
-type smint() :: integer().
|
-type smint() :: integer().
|
||||||
|
|
||||||
|
@ -127,8 +135,8 @@ match_to_route(M) ->
|
||||||
|
|
||||||
-spec actor_init(actor(), incarnation(), env()) -> {ok, state()}.
|
-spec actor_init(actor(), incarnation(), env()) -> {ok, state()}.
|
||||||
actor_init(Actor, Incarnation, Env = #{timestamp := Now}) ->
|
actor_init(Actor, Incarnation, Env = #{timestamp := Now}) ->
|
||||||
%% FIXME: Sane transactions.
|
%% TODO: Rolling upgrade safety?
|
||||||
case transaction(fun mnesia_actor_init/3, [Actor, Incarnation, Now]) of
|
case transaction(fun ?MODULE:mnesia_actor_init/3, [Actor, Incarnation, Now]) of
|
||||||
{ok, State} ->
|
{ok, State} ->
|
||||||
{ok, State};
|
{ok, State};
|
||||||
{reincarnate, Rec} ->
|
{reincarnate, Rec} ->
|
||||||
|
@ -173,17 +181,30 @@ actor_apply_operation(
|
||||||
State = #state{actor = Actor, incarnation = Incarnation, lane = Lane},
|
State = #state{actor = Actor, incarnation = Incarnation, lane = Lane},
|
||||||
_Env
|
_Env
|
||||||
) ->
|
) ->
|
||||||
_ = assert_current_incarnation(Actor, Incarnation),
|
Entry = emqx_topic_index:make_key(TopicFilter, ID),
|
||||||
_ = apply_operation(emqx_topic_index:make_key(TopicFilter, ID), OpName, Lane),
|
case mria_config:whoami() of
|
||||||
|
Role when Role /= replicant ->
|
||||||
|
apply_actor_operation(Actor, Incarnation, Entry, OpName, Lane);
|
||||||
|
replicant ->
|
||||||
|
mria:async_dirty(
|
||||||
|
?EXTROUTE_SHARD,
|
||||||
|
fun ?MODULE:apply_actor_operation/5,
|
||||||
|
[Actor, Incarnation, Entry, OpName, Lane]
|
||||||
|
)
|
||||||
|
end,
|
||||||
State;
|
State;
|
||||||
actor_apply_operation(
|
actor_apply_operation(
|
||||||
heartbeat,
|
heartbeat,
|
||||||
State = #state{actor = Actor, incarnation = Incarnation},
|
State = #state{actor = Actor, incarnation = Incarnation},
|
||||||
_Env = #{timestamp := Now}
|
_Env = #{timestamp := Now}
|
||||||
) ->
|
) ->
|
||||||
ok = transaction(fun mnesia_actor_heartbeat/3, [Actor, Incarnation, Now]),
|
ok = transaction(fun ?MODULE:mnesia_actor_heartbeat/3, [Actor, Incarnation, Now]),
|
||||||
State.
|
State.
|
||||||
|
|
||||||
|
apply_actor_operation(Actor, Incarnation, Entry, OpName, Lane) ->
|
||||||
|
_ = assert_current_incarnation(Actor, Incarnation),
|
||||||
|
apply_operation(Entry, OpName, Lane).
|
||||||
|
|
||||||
apply_operation(Entry, OpName, Lane) ->
|
apply_operation(Entry, OpName, Lane) ->
|
||||||
%% NOTE
|
%% NOTE
|
||||||
%% This is safe sequence of operations only on core nodes. On replicants,
|
%% This is safe sequence of operations only on core nodes. On replicants,
|
||||||
|
@ -259,7 +280,7 @@ mnesia_actor_heartbeat(Actor, Incarnation, TS) ->
|
||||||
end.
|
end.
|
||||||
|
|
||||||
clean_incarnation(Rec) ->
|
clean_incarnation(Rec) ->
|
||||||
transaction(fun mnesia_clean_incarnation/1, [Rec]).
|
transaction(fun ?MODULE:mnesia_clean_incarnation/1, [Rec]).
|
||||||
|
|
||||||
mnesia_clean_incarnation(#actor{id = Actor, incarnation = Incarnation, lane = Lane}) ->
|
mnesia_clean_incarnation(#actor{id = Actor, incarnation = Incarnation, lane = Lane}) ->
|
||||||
case mnesia:read(?EXTROUTE_ACTOR_TAB, Actor, write) of
|
case mnesia:read(?EXTROUTE_ACTOR_TAB, Actor, write) of
|
||||||
|
|
|
@ -33,6 +33,12 @@ end_per_testcase(TC, Config) ->
|
||||||
init_db() ->
|
init_db() ->
|
||||||
mria:wait_for_tables(emqx_cluster_link_extrouter:create_tables()).
|
mria:wait_for_tables(emqx_cluster_link_extrouter:create_tables()).
|
||||||
|
|
||||||
|
init_db_nodes(Nodes) ->
|
||||||
|
ok = lists:foreach(
|
||||||
|
fun(Node) -> ok = erpc:call(Node, ?MODULE, init_db, []) end,
|
||||||
|
Nodes
|
||||||
|
).
|
||||||
|
|
||||||
%%
|
%%
|
||||||
|
|
||||||
t_consistent_routing_view(_Config) ->
|
t_consistent_routing_view(_Config) ->
|
||||||
|
@ -174,20 +180,15 @@ t_consistent_routing_view_concurrent_updates(_Config) ->
|
||||||
|
|
||||||
t_consistent_routing_view_concurrent_cluster_updates('init', Config) ->
|
t_consistent_routing_view_concurrent_cluster_updates('init', Config) ->
|
||||||
Specs = [
|
Specs = [
|
||||||
{emqx_external_router1, #{role => core}},
|
{emqx_cluster_link_extrouter1, #{role => core}},
|
||||||
{emqx_external_router2, #{role => core}},
|
{emqx_cluster_link_extrouter2, #{role => core}},
|
||||||
{emqx_external_router3, #{role => core}}
|
{emqx_cluster_link_extrouter3, #{role => core}}
|
||||||
],
|
],
|
||||||
Cluster = emqx_cth_cluster:start(
|
Cluster = emqx_cth_cluster:start(
|
||||||
Specs,
|
Specs,
|
||||||
#{work_dir => emqx_cth_suite:work_dir(?FUNCTION_NAME, Config)}
|
#{work_dir => emqx_cth_suite:work_dir(?FUNCTION_NAME, Config)}
|
||||||
),
|
),
|
||||||
ok = lists:foreach(
|
ok = init_db_nodes(Cluster),
|
||||||
fun(Node) ->
|
|
||||||
ok = erpc:call(Node, ?MODULE, init_db, [])
|
|
||||||
end,
|
|
||||||
Cluster
|
|
||||||
),
|
|
||||||
[{cluster, Cluster} | Config];
|
[{cluster, Cluster} | Config];
|
||||||
t_consistent_routing_view_concurrent_cluster_updates('end', Config) ->
|
t_consistent_routing_view_concurrent_cluster_updates('end', Config) ->
|
||||||
ok = emqx_cth_cluster:stop(?config(cluster, Config)).
|
ok = emqx_cth_cluster:stop(?config(cluster, Config)).
|
||||||
|
@ -236,6 +237,24 @@ t_consistent_routing_view_concurrent_cluster_updates(Config) ->
|
||||||
erpc:call(N1, ?MODULE, topics_sorted, [])
|
erpc:call(N1, ?MODULE, topics_sorted, [])
|
||||||
).
|
).
|
||||||
|
|
||||||
|
t_consistent_routing_view_concurrent_cluster_replicant_updates('init', Config) ->
|
||||||
|
Specs = [
|
||||||
|
{emqx_cluster_link_extrouter_repl1, #{role => core}},
|
||||||
|
{emqx_cluster_link_extrouter_repl2, #{role => core}},
|
||||||
|
{emqx_cluster_link_extrouter_repl3, #{role => replicant}}
|
||||||
|
],
|
||||||
|
Cluster = emqx_cth_cluster:start(
|
||||||
|
Specs,
|
||||||
|
#{work_dir => emqx_cth_suite:work_dir(?FUNCTION_NAME, Config)}
|
||||||
|
),
|
||||||
|
ok = init_db_nodes(Cluster),
|
||||||
|
[{cluster, Cluster} | Config];
|
||||||
|
t_consistent_routing_view_concurrent_cluster_replicant_updates('end', Config) ->
|
||||||
|
ok = emqx_cth_cluster:stop(?config(cluster, Config)).
|
||||||
|
|
||||||
|
t_consistent_routing_view_concurrent_cluster_replicant_updates(Config) ->
|
||||||
|
t_consistent_routing_view_concurrent_cluster_updates(Config).
|
||||||
|
|
||||||
run_remote_actor({Node, Run}) ->
|
run_remote_actor({Node, Run}) ->
|
||||||
erlang:spawn_monitor(Node, ?MODULE, run_actor, [Run]).
|
erlang:spawn_monitor(Node, ?MODULE, run_actor, [Run]).
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue