Browse Source

Start adding an event handler.

Roberto Ostinelli 5 years ago
parent
commit
6a2ea1df06
2 changed files with 106 additions and 10 deletions
  1. 62 0
      src/syn_event_handler.erl
  2. 44 10
      src/syn_registry.erl

+ 62 - 0
src/syn_event_handler.erl

@@ -0,0 +1,62 @@
+%% ==========================================================================================================
+%% Syn - A global Process Registry and Process Group manager.
+%%
+%% The MIT License (MIT)
+%%
+%% Copyright (c) 2019 Roberto Ostinelli <roberto@ostinelli.net> and Neato Robotics, Inc.
+%%
+%% Portions of code from Ulf Wiger's unsplit server module:
+%% <https://github.com/uwiger/unsplit/blob/master/src/unsplit_server.erl>
+%%
+%% Permission is hereby granted, free of charge, to any person obtaining a copy
+%% of this software and associated documentation files (the "Software"), to deal
+%% in the Software without restriction, including without limitation the rights
+%% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+%% copies of the Software, and to permit persons to whom the Software is
+%% furnished to do so, subject to the following conditions:
+%%
+%% The above copyright notice and this permission notice shall be included in
+%% all copies or substantial portions of the Software.
+%%
+%% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+%% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+%% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+%% AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+%% LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+%% OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+%% THE SOFTWARE.
+%% ==========================================================================================================
+-module(syn_event_handler).
+
+-export([resolve_registry_conflict/4]).
+
+-callback on_process_exit(
+    Name :: any(),
+    Pid :: pid(),
+    Meta :: any(),
+    Reason :: any()
+) -> any().
+
+-callback on_group_process_exit(
+    GroupName :: any(),
+    Pid :: pid(),
+    Meta :: any(),
+    Reason :: any()
+) -> any().
+
+-callback resolve_registry_conflict(
+    Name :: any(),
+    {Pid1 :: pid(), Meta1 :: any()},
+    {Pid2 :: pid(), Meta2 :: any()}
+) -> PidToKeep :: pid() | undefined.
+
+-optional_callbacks([on_process_exit/4, on_group_process_exit/4, resolve_registry_conflict/3]).
+
+-spec resolve_registry_conflict(
+    Name :: any(),
+    {Pid1 :: pid(), Meta1 :: any()},
+    {Pid2 :: pid(), Meta2 :: any()},
+    CustomEventHandler :: module()
+) -> ok.
+resolve_registry_conflict(_Name, {LocalPid, _LocalMeta}, {_RemotePid, _RemoteMeta}, _CustomEventHandler) ->
+    LocalPid.

+ 44 - 10
src/syn_registry.erl

@@ -42,7 +42,12 @@
 -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
 
 %% records
--record(state, {}).
+-record(state, {
+    custom_event_handler = undefined :: module()
+}).
+
+%% macros
+-define(DEFAULT_EVENT_HANDLER_MODULE, syn_event_handler).
 
 %% includes
 -include("syn.hrl").
@@ -139,8 +144,12 @@ init([]) ->
         ok ->
             %% monitor nodes
             ok = net_kernel:monitor_nodes(true),
+            %% get handler
+            CustomEventHandler = application:get_env(syn, event_handler, ?DEFAULT_EVENT_HANDLER_MODULE),
             %% init
-            {ok, #state{}};
+            {ok, #state{
+                custom_event_handler = CustomEventHandler
+            }};
         Reason ->
             {stop, {error_waiting_for_registry_table, Reason}}
     end.
@@ -271,7 +280,7 @@ handle_info({nodeup, RemoteNode}, State) ->
                 "Syn(~p): Received ~p registry entrie(s) from remote node ~p, writing to local~n",
                 [node(), length(RegistryTuples), RemoteNode]
             ),
-            sync_registry_tuples(RemoteNode, RegistryTuples),
+            sync_registry_tuples(RemoteNode, RegistryTuples, State),
             %% exit
             error_logger:warning_msg("Syn(~p): REGISTRY AUTOMERGE <---- Done for remote node ~p~n", [node(), RemoteNode])
         end
@@ -378,8 +387,10 @@ log_process_exit(Name, Pid, Reason) ->
             end
     end.
 
--spec sync_registry_tuples(RemoteNode :: node(), RegistryTuples :: [syn_registry_tuple()]) -> ok.
-sync_registry_tuples(RemoteNode, RegistryTuples) ->
+-spec sync_registry_tuples(RemoteNode :: node(), RegistryTuples :: [syn_registry_tuple()], #state{}) -> ok.
+sync_registry_tuples(RemoteNode, RegistryTuples, #state{
+    custom_event_handler = CustomEventHandler
+}) ->
     %% ensure that registry doesn't have any joining node's entries (here again for race conditions)
     purge_registry_entries_for_remote_node(RemoteNode),
     %% loop
@@ -389,21 +400,44 @@ sync_registry_tuples(RemoteNode, RegistryTuples) ->
             undefined ->
                 %% no conflict
                 register_on_node(Name, RemotePid, RemoteMeta);
+
             Entry ->
+                LocalPid = Entry#syn_registry_table.pid,
+                LocalMeta = Entry#syn_registry_table.meta,
+
                 error_logger:warning_msg(
                     "Syn(~p): Conflicting name process found for: ~p, processes are ~p, ~p~n",
-                    [node(), Name, Entry#syn_registry_table.pid, RemotePid]
+                    [node(), Name, LocalPid, RemotePid]
                 ),
                 %% unregister local
                 unregister_on_node(Name),
                 %% unregister remote
                 ok = rpc:call(RemoteNode, syn_registry, unregister_on_node, [Name]),
 
-                %% TODO: call conflict resolution fun, for now kill the remote one
-                exit(RemotePid, kill),
+                %% call conflict resolution
+                PidToKeep = syn_event_handler:resolve_registry_conflict(
+                    Name,
+                    {LocalPid, LocalMeta},
+                    {RemotePid, RemoteMeta},
+                    CustomEventHandler
+                ),
 
-                %% register local
-                register_on_node(Name, Entry#syn_registry_table.pid, Entry#syn_registry_table.meta)
+                %% keep chosen one
+                case PidToKeep of
+                    LocalPid ->
+                        %% keep local
+                        exit(RemotePid, kill),
+                        register_on_node(Name, LocalPid, LocalMeta);
+
+                    RemotePid ->
+                        %% keep remote
+                        exit(LocalPid, kill),
+                        register_on_node(Name, RemotePid, RemoteMeta);
+
+                    _ ->
+                        % don't keep any of the two
+                        ok
+                end
         end
     end,
     %% add to table