Browse Source

Add local group members.

Roberto Ostinelli 5 years ago
parent
commit
ed12197a52
4 changed files with 136 additions and 8 deletions
  1. 14 0
      src/syn.erl
  2. 2 2
      src/syn_backbone.erl
  3. 40 0
      src/syn_groups.erl
  4. 80 6
      test/syn_groups_SUITE.erl

+ 14 - 0
src/syn.erl

@@ -35,6 +35,8 @@
 -export([leave/2]).
 -export([get_members/1, get_members/2]).
 -export([member/2]).
+-export([get_local_members/1, get_local_members/2]).
+-export([local_member/2]).
 
 %% gen_server via interface
 -export([register_name/2, unregister_name/1, whereis_name/1, send/2]).
@@ -133,3 +135,15 @@ get_members(GroupName, with_meta) ->
 -spec member(GroupName :: any(), Pid :: pid()) -> boolean().
 member(GroupName, Pid) ->
     syn_groups:member(GroupName, Pid).
+
+-spec get_local_members(GroupName :: any()) -> [pid()].
+get_local_members(GroupName) ->
+    syn_groups:get_local_members(GroupName).
+
+-spec get_local_members(GroupName :: any(), with_meta) -> [{pid(), Meta :: any()}].
+get_local_members(GroupName, with_meta) ->
+    syn_groups:get_local_members(GroupName, with_meta).
+
+-spec local_member(GroupName :: any(), Pid :: pid()) -> boolean().
+local_member(GroupName, Pid) ->
+    syn_groups:local_member(GroupName, Pid).

+ 2 - 2
src/syn_backbone.erl

@@ -70,6 +70,6 @@ create_groups_table() ->
     mnesia:create_table(syn_groups_table, [
         {type, bag},
         {attributes, record_info(fields, syn_groups_table)},
-        {index, [#syn_groups_table.pid]},
-        {storage_properties, [{ets, [{read_concurrency, true}]}]}
+        {index, [#syn_groups_table.pid, #syn_groups_table.node]},
+        {storage_properties, [{ets, [{read_concurrency, true}, {write_concurrency, true}]}]}
     ]).

+ 40 - 0
src/syn_groups.erl

@@ -32,6 +32,8 @@
 -export([leave/2]).
 -export([get_members/1, get_members/2]).
 -export([member/2]).
+-export([get_local_members/1, get_local_members/2]).
+-export([local_member/2]).
 
 %% sync API
 -export([sync_join/3, sync_leave/2]).
@@ -91,6 +93,44 @@ member(Pid, GroupName) ->
         _ -> true
     end.
 
+-spec get_local_members(Name :: any()) -> [pid()].
+get_local_members(GroupName) ->
+    %% build name guard
+    NameGuard = case is_tuple(GroupName) of
+        true -> {'==', '$1', {GroupName}};
+        _ -> {'=:=', '$1', GroupName}
+    end,
+    %% build match specs
+    MatchHead = #syn_groups_table{name = '$1', node = '$2', pid = '$3', _ = '_'},
+    Guards = [NameGuard, {'=:=', '$2', node()}],
+    Result = '$3',
+    %% select
+    Pids = mnesia:dirty_select(syn_groups_table, [{MatchHead, Guards, [Result]}]),
+    lists:sort(Pids).
+
+-spec get_local_members(GroupName :: any(), with_meta) -> [{pid(), Meta :: any()}].
+get_local_members(GroupName, with_meta) ->
+    %% build name guard
+    NameGuard = case is_tuple(GroupName) of
+        true -> {'==', '$1', {GroupName}};
+        _ -> {'=:=', '$1', GroupName}
+    end,
+    %% build match specs
+    MatchHead = #syn_groups_table{name = '$1', node = '$2', pid = '$3', meta = '$4', _ = '_'},
+    Guards = [NameGuard, {'=:=', '$2', node()}],
+    Result = {{'$3', '$4'}},
+    %% select
+    PidsWithMeta = mnesia:dirty_select(syn_groups_table, [{MatchHead, Guards, [Result]}]),
+    lists:keysort(1, PidsWithMeta).
+
+-spec local_member(Pid :: pid(), GroupName :: any()) -> boolean().
+local_member(Pid, GroupName) ->
+    case find_process_entry_by_name_and_pid(GroupName, Pid) of
+        undefined -> false;
+        Entry when Entry#syn_groups_table.node =:= node() -> true;
+        _ -> false
+    end.
+
 -spec sync_join(GroupName :: any(), Pid :: pid(), Meta :: any()) -> ok.
 sync_join(GroupName, Pid, Meta) ->
     gen_server:cast(?MODULE, {sync_join, GroupName, Pid, Meta}).

+ 80 - 6
test/syn_groups_SUITE.erl

@@ -38,7 +38,8 @@
     single_node_join_errors/1
 ]).
 -export([
-    two_nodes_join_monitor_and_unregister/1
+    two_nodes_join_monitor_and_unregister/1,
+    two_nodes_local_members/1
 ]).
 
 %% include
@@ -82,8 +83,8 @@ groups() ->
             single_node_join_errors
         ]},
         {two_nodes_groups, [shuffle], [
-            two_nodes_join_monitor_and_unregister
-%%            two_nodes_local_members
+            two_nodes_join_monitor_and_unregister,
+            two_nodes_local_members
         ]}
     ].
 %% -------------------------------------------------------------------
@@ -193,10 +194,12 @@ single_node_join_and_monitor(_Config) ->
     true = syn:member(PidWithMeta, GroupName),
     false = syn:member(PidOther, GroupName),
     true = lists:sort([Pid, PidWithMeta]) =:= lists:sort(syn:get_members(GroupName)),
-    true = lists:sort([{Pid, undefined}, {PidWithMeta, {with, meta}}]) =:= lists:sort(syn:get_members(GroupName, with_meta)),
+    true = lists:sort([{Pid, undefined}, {PidWithMeta, {with, meta}}])
+        =:= lists:sort(syn:get_members(GroupName, with_meta)),
     %% re-join
     ok = syn:join(GroupName, PidWithMeta, {with2, meta2}),
-    true = lists:sort([{Pid, undefined}, {PidWithMeta, {with2, meta2}}]) =:= lists:sort(syn:get_members(GroupName, with_meta)),
+    true = lists:sort([{Pid, undefined}, {PidWithMeta, {with2, meta2}}])
+        =:= lists:sort(syn:get_members(GroupName, with_meta)),
     %% kill process
     syn_test_suite_helper:kill_process(Pid),
     syn_test_suite_helper:kill_process(PidWithMeta),
@@ -227,7 +230,8 @@ single_node_join_and_leave(_Config) ->
     true = syn:member(Pid, GroupName),
     true = syn:member(PidWithMeta, GroupName),
     true = lists:sort([Pid, PidWithMeta]) =:= lists:sort(syn:get_members(GroupName)),
-    true = lists:sort([{Pid, undefined}, {PidWithMeta, {with, meta}}]) =:= lists:sort(syn:get_members(GroupName, with_meta)),
+    true = lists:sort([{Pid, undefined}, {PidWithMeta, {with, meta}}])
+        =:= lists:sort(syn:get_members(GroupName, with_meta)),
     %% leave
     ok = syn:leave(GroupName, Pid),
     ok = syn:leave(GroupName, PidWithMeta),
@@ -295,10 +299,13 @@ two_nodes_join_monitor_and_unregister(Config) ->
     true = syn:member(RemotePid, GroupName),
     true = syn:member(RemotePidJoinRemote, GroupName),
     false = syn:member(OtherPid, GroupName),
+    true = lists:sort([LocalPid, RemotePid, RemotePidJoinRemote]) =:= lists:sort(syn:get_members(GroupName)),
     true = rpc:call(SlaveNode, syn, member, [LocalPid, GroupName]),
     true = rpc:call(SlaveNode, syn, member, [RemotePid, GroupName]),
     true = rpc:call(SlaveNode, syn, member, [RemotePidJoinRemote, GroupName]),
     false = rpc:call(SlaveNode, syn, member, [OtherPid, GroupName]),
+    true = lists:sort([LocalPid, RemotePid, RemotePidJoinRemote])
+        =:= lists:sort(rpc:call(SlaveNode, syn, get_members, [GroupName])),
     %% leave & kill
     ok = rpc:call(SlaveNode, syn, leave, [GroupName, LocalPid]),
     ok = syn:leave(GroupName, RemotePid),
@@ -315,3 +322,70 @@ two_nodes_join_monitor_and_unregister(Config) ->
     %% kill processes
     syn_test_suite_helper:kill_process(LocalPid),
     syn_test_suite_helper:kill_process(RemotePid).
+
+two_nodes_local_members(Config) ->
+    GroupName = "my group",
+    %% get slave
+    SlaveNode = proplists:get_value(slave_node, Config),
+    %% start
+    ok = syn:start(),
+    ok = rpc:call(SlaveNode, syn, start, []),
+    timer:sleep(100),
+    %% start processes
+    LocalPid = syn_test_suite_helper:start_process(),
+    RemotePid = syn_test_suite_helper:start_process(SlaveNode),
+    RemotePidJoinRemote = syn_test_suite_helper:start_process(SlaveNode),
+    OtherPid = syn_test_suite_helper:start_process(),
+    %% local members
+    [] = syn:get_local_members(GroupName),
+    [] = syn:get_local_members(GroupName, with_meta),
+    false = syn:local_member(LocalPid, GroupName),
+    false = syn:local_member(RemotePid, GroupName),
+    false = syn:local_member(RemotePidJoinRemote, GroupName),
+    false = syn:local_member(OtherPid, GroupName),
+    %% remote members
+    [] = rpc:call(SlaveNode, syn, get_local_members, [GroupName]),
+    [] = rpc:call(SlaveNode, syn, get_local_members, [GroupName, with_meta]),
+    false = rpc:call(SlaveNode, syn, local_member, [LocalPid, GroupName]),
+    false = rpc:call(SlaveNode, syn, local_member, [RemotePid, GroupName]),
+    false = rpc:call(SlaveNode, syn, local_member, [RemotePidJoinRemote, GroupName]),
+    false = rpc:call(SlaveNode, syn, local_member, [OtherPid, GroupName]),
+    %% join
+    ok = syn:join(GroupName, LocalPid),
+    ok = syn:join(GroupName, RemotePid, {meta, 2}),
+    ok = rpc:call(SlaveNode, syn, join, [GroupName, RemotePidJoinRemote]),
+    ok = syn:join({"other-group"}, OtherPid),
+    timer:sleep(200),
+    %% local members
+    [LocalPid] = syn:get_local_members(GroupName),
+    [{LocalPid, undefined}] = syn:get_local_members(GroupName, with_meta),
+    [OtherPid] = syn:get_local_members({"other-group"}),
+    true = syn:local_member(LocalPid, GroupName),
+    false = syn:local_member(RemotePid, GroupName),
+    false = syn:local_member(RemotePidJoinRemote, GroupName),
+    false = syn:local_member(OtherPid, GroupName),
+    true = syn:local_member(OtherPid, {"other-group"}),
+    %% remote members
+    true = lists:sort([RemotePid, RemotePidJoinRemote])
+        =:= lists:sort(rpc:call(SlaveNode, syn, get_local_members, [GroupName])),
+    true = lists:sort([{RemotePid, {meta, 2}}, {RemotePidJoinRemote, undefined}])
+        =:= lists:sort(rpc:call(SlaveNode, syn, get_local_members, [GroupName, with_meta])),
+    false = rpc:call(SlaveNode, syn, local_member, [LocalPid, GroupName]),
+    true = rpc:call(SlaveNode, syn, local_member, [RemotePid, GroupName]),
+    true = rpc:call(SlaveNode, syn, local_member, [RemotePidJoinRemote, GroupName]),
+    false = rpc:call(SlaveNode, syn, local_member, [OtherPid, GroupName]),
+    %% leave & kill
+    ok = rpc:call(SlaveNode, syn, leave, [GroupName, LocalPid]),
+    ok = syn:leave(GroupName, RemotePid),
+    syn_test_suite_helper:kill_process(RemotePidJoinRemote),
+    syn_test_suite_helper:kill_process(OtherPid),
+    timer:sleep(200),
+    %% local members
+    [] = syn:get_local_members(GroupName),
+    [] = syn:get_local_members(GroupName, with_meta),
+    %% remote members
+    [] = rpc:call(SlaveNode, syn, get_local_members, [GroupName]),
+    [] = rpc:call(SlaveNode, syn, get_local_members, [GroupName, with_meta]),
+    %% kill processes
+    syn_test_suite_helper:kill_process(LocalPid),
+    syn_test_suite_helper:kill_process(RemotePid).