Browse Source

Allow to reregister.

Roberto Ostinelli 5 years ago
parent
commit
2bbda172f7
4 changed files with 73 additions and 2 deletions
  1. 18 0
      README.md
  2. 9 0
      src/syn.erl
  3. 23 0
      src/syn_registry.erl
  4. 23 2
      test/syn_registry_SUITE.erl

+ 18 - 0
README.md

@@ -165,6 +165,24 @@ Types:
 
 > You don't need to unregister names of processes that are about to die, since they are monitored by Syn and they will be removed automatically. If you manually unregister a process just before it dies, the callback on process exit (see here below) might not get called.
 
+To reregister a a previously registered Name with a different Pid:
+
+```erlang
+syn:reregister(Name, Pid) ->
+    syn:reregister(Name, Pid, undefined).
+```
+
+```erlang
+syn:reregister(Name, Pid, Meta) -> ok.
+
+Types:
+    Name = any()
+    Pid = pid()
+    Meta = any()
+```
+
+> Re-registering is specifically useful if you are re-registering a process on a different node from where the process currently registered with Name is running on, as `reregister` will ensure that the registration succeeds.
+
 To retrieve the count of total registered processes running in the cluster:
 
 ```erlang

+ 9 - 0
src/syn.erl

@@ -28,6 +28,7 @@
 %% API
 -export([start/0, stop/0]).
 -export([register/2, register/3]).
+-export([reregister/2, reregister/3]).
 -export([unregister/1]).
 -export([whereis/1, whereis/2]).
 -export([registry_count/0, registry_count/1]).
@@ -66,6 +67,14 @@ register(Name, Pid) ->
 register(Name, Pid, Meta) ->
     syn_registry:register(Name, Pid, Meta).
 
+-spec reregister(Name :: any(), Pid :: pid()) -> ok | {error, Reason :: any()}.
+reregister(Name, Pid) ->
+    syn_registry:reregister(Name, Pid).
+
+-spec reregister(Name :: any(), Pid :: pid(), Meta :: any()) -> ok | {error, Reason :: any()}.
+reregister(Name, Pid, Meta) ->
+    syn_registry:reregister(Name, Pid, Meta).
+
 -spec unregister(Name :: any()) -> ok | {error, Reason :: any()}.
 unregister(Name) ->
     syn_registry:unregister(Name).

+ 23 - 0
src/syn_registry.erl

@@ -29,6 +29,7 @@
 %% API
 -export([start_link/0]).
 -export([register/2, register/3]).
+-export([reregister/2, reregister/3]).
 -export([unregister/1]).
 -export([whereis/1, whereis/2]).
 -export([count/0, count/1]).
@@ -69,6 +70,28 @@ register(Name, Pid, Meta) when is_pid(Pid) ->
     Node = node(Pid),
     gen_server:call({?MODULE, Node}, {register_on_node, Name, Pid, Meta}).
 
+-spec reregister(Name :: any(), Pid :: pid()) -> ok | {error, Reason :: any()}.
+reregister(Name, Pid) ->
+    reregister(Name, Pid, undefined).
+
+-spec reregister(Name :: any(), Pid :: pid(), Meta :: any()) -> ok | {error, Reason :: any()}.
+reregister(Name, Pid, Meta) when is_pid(Pid) ->
+    reregister(Name, Pid, Meta, 0).
+
+-spec reregister(Name :: any(), Pid :: pid(), Meta :: any(), RetryCount :: non_neg_integer()) ->
+    ok | {error, Reason :: any()}.
+reregister(Name, Pid, Meta, RetryCount) when RetryCount > 50 ->
+    exit(self(), {timeout, {gen_server, call, [?MODULE, reregister, {Name, Pid, Meta}]}});
+reregister(Name, Pid, Meta, RetryCount) when is_pid(Pid) ->
+    ?MODULE:unregister(Name),
+    case find_registry_tuple_by_name(Name) of
+        undefined ->
+            register(Name, Pid, Meta);
+        {Name, _Pid, _Meta} ->
+            timer:sleep(100),
+            reregister(Name, Pid, Meta, RetryCount + 1)
+    end.
+
 -spec unregister(Name :: any()) -> ok | {error, Reason :: any()}.
 unregister(Name) ->
     % get process' node

+ 23 - 2
test/syn_registry_SUITE.erl

@@ -50,7 +50,8 @@
     two_nodes_registration_race_condition_conflict_resolution_keep_remote/1,
     two_nodes_registration_race_condition_conflict_resolution_when_process_died/1,
     two_nodes_registry_full_cluster_sync_on_boot_node_added_later/1,
-    two_nodes_registry_full_cluster_sync_on_boot_syn_started_later/1
+    two_nodes_registry_full_cluster_sync_on_boot_syn_started_later/1,
+    two_nodes_reregister/1
 ]).
 -export([
     three_nodes_partial_netsplit_consistency/1,
@@ -124,7 +125,8 @@ groups() ->
             two_nodes_registration_race_condition_conflict_resolution_keep_remote,
             two_nodes_registration_race_condition_conflict_resolution_when_process_died,
             two_nodes_registry_full_cluster_sync_on_boot_node_added_later,
-            two_nodes_registry_full_cluster_sync_on_boot_syn_started_later
+            two_nodes_registry_full_cluster_sync_on_boot_syn_started_later,
+            two_nodes_reregister
         ]},
         {three_nodes_process_registration, [shuffle], [
             three_nodes_partial_netsplit_consistency,
@@ -619,6 +621,25 @@ two_nodes_registry_full_cluster_sync_on_boot_syn_started_later(Config) ->
     Pid = syn:whereis(<<"proc">>),
     Pid = rpc:call(SlaveNode, syn, whereis, [<<"proc">>]).
 
+two_nodes_reregister(Config) ->
+    Name = "common name",
+    %% get slave
+    SlaveNode = proplists:get_value(slave_node, Config),
+    %% start
+    ok = syn:start(),
+    ok = rpc:call(SlaveNode, syn, start, []),
+    timer:sleep(100),
+    %% start processes
+    PidLocal = syn_test_suite_helper:start_process(),
+    PidRemote = syn_test_suite_helper:start_process(SlaveNode),
+    ok = rpc:call(SlaveNode, syn, register, [Name, PidRemote]),
+    timer:sleep(1000),
+    %% fast unreg-reg
+    ok = syn:reregister(Name, PidLocal),
+    ok = rpc:call(SlaveNode, syn, reregister, [Name, PidRemote, some_meta]),
+    timer:sleep(250),
+    {PidRemote, some_meta} = syn:whereis(PidRemote).
+
 three_nodes_partial_netsplit_consistency(Config) ->
     %% get slaves
     SlaveNode1 = proplists:get_value(slave_node_1, Config),