feat(dsrepl): allow to subscribe to DB metadata changes
Currently, only shard metadata changes are announced to the subscribers.
This commit is contained in:
parent
a07295d3bc
commit
d6058b7f51
|
@ -57,6 +57,12 @@
|
||||||
target_set/2
|
target_set/2
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
%% Subscriptions to changes:
|
||||||
|
-export([
|
||||||
|
subscribe/2,
|
||||||
|
unsubscribe/1
|
||||||
|
]).
|
||||||
|
|
||||||
%% gen_server
|
%% gen_server
|
||||||
-export([start_link/0, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
|
-export([start_link/0, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2]).
|
||||||
|
|
||||||
|
@ -125,6 +131,9 @@
|
||||||
| {error, {nonexistent_sites, [site()]}}
|
| {error, {nonexistent_sites, [site()]}}
|
||||||
| {error, _}.
|
| {error, _}.
|
||||||
|
|
||||||
|
%% Subject of the subscription:
|
||||||
|
-type subject() :: emqx_ds:db().
|
||||||
|
|
||||||
%% Peristent term key:
|
%% Peristent term key:
|
||||||
-define(emqx_ds_builtin_site, emqx_ds_builtin_site).
|
-define(emqx_ds_builtin_site, emqx_ds_builtin_site).
|
||||||
|
|
||||||
|
@ -336,11 +345,21 @@ target_set(DB, Shard) ->
|
||||||
undefined
|
undefined
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%%================================================================================
|
||||||
|
|
||||||
|
subscribe(Pid, Subject) ->
|
||||||
|
gen_server:call(?SERVER, {subscribe, Pid, Subject}, infinity).
|
||||||
|
|
||||||
|
unsubscribe(Pid) ->
|
||||||
|
gen_server:call(?SERVER, {unsubscribe, Pid}, infinity).
|
||||||
|
|
||||||
%%================================================================================
|
%%================================================================================
|
||||||
%% behavior callbacks
|
%% behavior callbacks
|
||||||
%%================================================================================
|
%%================================================================================
|
||||||
|
|
||||||
-record(s, {}).
|
-record(s, {
|
||||||
|
subs = #{} :: #{pid() => {subject(), _Monitor :: reference()}}
|
||||||
|
}).
|
||||||
|
|
||||||
init([]) ->
|
init([]) ->
|
||||||
process_flag(trap_exit, true),
|
process_flag(trap_exit, true),
|
||||||
|
@ -348,14 +367,24 @@ init([]) ->
|
||||||
ensure_tables(),
|
ensure_tables(),
|
||||||
ensure_site(),
|
ensure_site(),
|
||||||
S = #s{},
|
S = #s{},
|
||||||
|
{ok, _Node} = mnesia:subscribe({table, ?SHARD_TAB, simple}),
|
||||||
{ok, S}.
|
{ok, S}.
|
||||||
|
|
||||||
|
handle_call({subscribe, Pid, Subject}, _From, S) ->
|
||||||
|
{reply, ok, handle_subscribe(Pid, Subject, S)};
|
||||||
|
handle_call({unsubscribe, Pid}, _From, S) ->
|
||||||
|
{reply, ok, handle_unsubscribe(Pid, S)};
|
||||||
handle_call(_Call, _From, S) ->
|
handle_call(_Call, _From, S) ->
|
||||||
{reply, {error, unknown_call}, S}.
|
{reply, {error, unknown_call}, S}.
|
||||||
|
|
||||||
handle_cast(_Cast, S) ->
|
handle_cast(_Cast, S) ->
|
||||||
{noreply, S}.
|
{noreply, S}.
|
||||||
|
|
||||||
|
handle_info({mnesia_table_event, {write, #?SHARD_TAB{shard = {DB, Shard}}, _}}, S) ->
|
||||||
|
ok = notify_subscribers(DB, {shard, DB, Shard}, S),
|
||||||
|
{noreply, S};
|
||||||
|
handle_info({'DOWN', _MRef, process, Pid, _Reason}, S) ->
|
||||||
|
{noreply, handle_unsubscribe(Pid, S)};
|
||||||
handle_info(_Info, S) ->
|
handle_info(_Info, S) ->
|
||||||
{noreply, S}.
|
{noreply, S}.
|
||||||
|
|
||||||
|
@ -613,6 +642,38 @@ transaction(Fun, Args) ->
|
||||||
{error, Reason}
|
{error, Reason}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%%====================================================================
|
||||||
|
|
||||||
|
handle_subscribe(Pid, Subject, S = #s{subs = Subs0}) ->
|
||||||
|
case maps:is_key(Pid, Subs0) of
|
||||||
|
false ->
|
||||||
|
MRef = erlang:monitor(process, Pid),
|
||||||
|
Subs = Subs0#{Pid => {Subject, MRef}},
|
||||||
|
S#s{subs = Subs};
|
||||||
|
true ->
|
||||||
|
S
|
||||||
|
end.
|
||||||
|
|
||||||
|
handle_unsubscribe(Pid, S = #s{subs = Subs0}) ->
|
||||||
|
case maps:take(Pid, Subs0) of
|
||||||
|
{{_Subject, MRef}, Subs} ->
|
||||||
|
_ = erlang:demonitor(MRef, [flush]),
|
||||||
|
S#s{subs = Subs};
|
||||||
|
error ->
|
||||||
|
S
|
||||||
|
end.
|
||||||
|
|
||||||
|
notify_subscribers(EventSubject, Event, #s{subs = Subs}) ->
|
||||||
|
maps:foreach(
|
||||||
|
fun(Pid, {Subject, _MRef}) ->
|
||||||
|
Subject == EventSubject andalso
|
||||||
|
erlang:send(Pid, {changed, Event})
|
||||||
|
end,
|
||||||
|
Subs
|
||||||
|
).
|
||||||
|
|
||||||
|
%%====================================================================
|
||||||
|
|
||||||
%% @doc Intersperse elements of two lists.
|
%% @doc Intersperse elements of two lists.
|
||||||
%% Example: intersperse([1, 2], [3, 4, 5]) -> [1, 3, 2, 4, 5].
|
%% Example: intersperse([1, 2], [3, 4, 5]) -> [1, 3, 2, 4, 5].
|
||||||
-spec intersperse([X], [Y]) -> [X | Y].
|
-spec intersperse([X], [Y]) -> [X | Y].
|
||||||
|
|
Loading…
Reference in New Issue