Browse Source

Ensure to always call the process exit callback.

Roberto Ostinelli 9 years ago
parent
commit
eac073fb29
4 changed files with 52 additions and 5 deletions
  1. 3 0
      README.md
  2. 11 3
      src/syn_backbone.erl
  3. 7 0
      src/syn_consistency.erl
  4. 31 2
      test/syn_consistency_SUITE.erl

+ 3 - 0
README.md

@@ -224,6 +224,9 @@ Set it in the options:
 ```
 If you don't set this option, no callback will be triggered.
 
+> If a process dies as a consequence of a conflict resolution, the process exit callback will still be called but the Key and Meta values will both be `undefined`.
+
+
 #### Conflict resolution by callback
 In case of race conditions, or during net splits, a Key might be registered simultaneously on two different nodes. In this case, the cluster experiences a naming conflict.
 

+ 11 - 3
src/syn_backbone.erl

@@ -230,20 +230,25 @@ handle_info({'EXIT', Pid, Reason}, #state{
         %% check if pid is in table
         case i_find_by_pid(Pid) of
             undefined ->
+                %% log
                 case Reason of
                     normal -> ok;
                     killed -> ok;
                     _ ->
                         error_logger:warning_msg("Received an exit message from an unlinked process ~p with reason: ~p", [Pid, Reason])
+                end,
+
+                %% callback
+                case ProcessExitCallbackModule of
+                    undefined -> ok;
+                    _ -> ProcessExitCallbackModule:ProcessExitCallbackFunction(undefined, Pid, undefined, Reason)
                 end;
+
             Process ->
                 %% get process info
                 Key = Process#syn_processes_table.key,
                 Meta = Process#syn_processes_table.meta,
 
-                %% delete from table
-                remove_process_by_key(Key),
-
                 %% log
                 case Reason of
                     normal -> ok;
@@ -252,6 +257,9 @@ handle_info({'EXIT', Pid, Reason}, #state{
                         error_logger:error_msg("Process with key ~p exited with reason: ~p", [Key, Reason])
                 end,
 
+                %% delete from table
+                remove_process_by_key(Key),
+
                 %% callback
                 case ProcessExitCallbackModule of
                     undefined -> ok;

+ 7 - 0
src/syn_consistency.erl

@@ -139,6 +139,13 @@ handle_info({purge_double_processes, DoubleProcessesInfo}, #state{
     conflicting_process_callback_module = ConflictingProcessCallbackModule,
     conflicting_process_callback_function = ConflictingProcessCallbackFunction
 } = State) ->
+    %% is in a test environment, wait to ensure we can get the process exit callback messages
+    case application:get_env(is_test) of
+        undefined -> ok;
+        {ok, true} -> timer:sleep(2000);
+        _ -> ok
+    end,
+
     error_logger:warning_msg("About to purge double processes after netsplit"),
     purge_double_processes(ConflictingProcessCallbackModule, ConflictingProcessCallbackFunction, DoubleProcessesInfo),
     {noreply, State};

+ 31 - 2
test/syn_consistency_SUITE.erl

@@ -47,6 +47,7 @@
 %% internal
 -export([process_reply_main/0]).
 -export([conflicting_process_callback_dummy/3]).
+-export([process_exit_callback_dummy/4]).
 
 %% include
 -include_lib("common_test/include/ct.hrl").
@@ -268,6 +269,12 @@ two_nodes_netsplit_kill_resolution_when_there_are_conflicts(Config) ->
     SlaveNode = proplists:get_value(slave_node, Config),
     CurrentNode = node(),
 
+    %% set process callback env variable
+    ok = application:set_env(syn, process_exit_callback, [syn_consistency_SUITE, process_exit_callback_dummy]),
+    ok = rpc:call(SlaveNode, application, set_env, [syn, process_exit_callback, [syn_consistency_SUITE, process_exit_callback_dummy]]),
+    ok = application:set_env(syn, is_test, true),
+    ok = rpc:call(SlaveNode, application, set_env, [syn, is_test, true]),
+
     %% start syn
     ok = syn:start(),
     ok = syn:init(),
@@ -275,6 +282,10 @@ two_nodes_netsplit_kill_resolution_when_there_are_conflicts(Config) ->
     ok = rpc:call(SlaveNode, syn, init, []),
     timer:sleep(100),
 
+    %% register global process
+    ResultPid = self(),
+    global:register_name(syn_consistency_SUITE_result, ResultPid),
+
     %% start processes
     LocalPid = syn_test_suite_helper:start_process(),
     SlavePid = syn_test_suite_helper:start_process(SlaveNode),
@@ -316,9 +327,21 @@ two_nodes_netsplit_kill_resolution_when_there_are_conflicts(Config) ->
     FoundPid = syn:find_by_key(conflicting_key),
     true = lists:member(FoundPid, [LocalPid, SlavePid]),
 
+    KilledPid = lists:nth(1, lists:delete(FoundPid, [LocalPid, SlavePid])),
+    receive
+        {exited, undefined, KilledPid, undefined, killed} -> ok;
+        Other ->
+            ok = Other
+    after 2000 ->
+        ok = process_exit_callback_was_not_called_from_local_node
+    end,
+
     %% kill processes
     syn_test_suite_helper:kill_process(LocalPid),
-    syn_test_suite_helper:kill_process(SlavePid).
+    syn_test_suite_helper:kill_process(SlavePid),
+
+    %% unregister
+    global:unregister_name(syn_consistency_SUITE_result).
 
 two_nodes_netsplit_callback_resolution_when_there_are_conflicts(Config) ->
     %% get slave
@@ -392,7 +415,10 @@ two_nodes_netsplit_callback_resolution_when_there_are_conflicts(Config) ->
 
     %% kill processes
     syn_test_suite_helper:kill_process(LocalPid),
-    syn_test_suite_helper:kill_process(SlavePid).
+    syn_test_suite_helper:kill_process(SlavePid),
+
+    %% unregister
+    global:unregister_name(syn_consistency_SUITE_result).
 
 three_nodes_netsplit_kill_resolution_when_there_are_conflicts(Config) ->
     %% get slaves
@@ -479,3 +505,6 @@ process_reply_main() ->
 
 conflicting_process_callback_dummy(_Key, Pid, Meta) ->
     Pid ! {shutdown, Meta}.
+
+process_exit_callback_dummy(Key, Pid, Meta, Reason) ->
+    global:send(syn_consistency_SUITE_result, {exited, Key, Pid, Meta, Reason}).