Publish node metadata in a protected ets table

This allows for far cheaper access to the zone information.  The
fallback to gen_server:calls is only for the initial hot upgrade and can
be removed afterwards along with the code_change.

BugzID: 13606


Project: http://git-wip-us.apache.org/repos/asf/couchdb-mem3/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb-mem3/commit/f92f9cc6
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-mem3/tree/f92f9cc6
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-mem3/diff/f92f9cc6

Branch: refs/heads/import
Commit: f92f9cc6ef0d46148a6bb76c62f894ca5f72387a
Parents: 8ca5c51
Author: Adam Kocoloski <a...@cloudant.com>
Authored: Mon May 21 21:32:22 2012 -0400
Committer: Adam Kocoloski <a...@cloudant.com>
Committed: Wed May 23 17:07:03 2012 -0400

----------------------------------------------------------------------
 src/mem3_nodes.erl | 65 ++++++++++++++++++++++++++++++-------------------
 1 file changed, 40 insertions(+), 25 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-mem3/blob/f92f9cc6/src/mem3_nodes.erl
----------------------------------------------------------------------
diff --git a/src/mem3_nodes.erl b/src/mem3_nodes.erl
index 460a006..454b5f9 100644
--- a/src/mem3_nodes.erl
+++ b/src/mem3_nodes.erl
@@ -22,37 +22,48 @@
 -include("mem3.hrl").
 -include_lib("couch/include/couch_db.hrl").
 
--record(state, {changes_pid, update_seq, nodes}).
+-record(state, {changes_pid, update_seq}).
 
 start_link() ->
     gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
 
 get_nodelist() ->
-    gen_server:call(?MODULE, get_nodelist).
+    try
+        lists:sort([N || {N,_} <- ets:tab2list(?MODULE)])
+    catch error:badarg ->
+        gen_server:call(?MODULE, get_nodelist)
+    end.
 
 get_node_info(Node, Key) ->
-    gen_server:call(?MODULE, {get_node_info, Node, Key}).
+    try
+        couch_util:get_value(Key, ets:lookup_element(?MODULE, Node, 2))
+    catch error:badarg ->
+        gen_server:call(?MODULE, {get_node_info, Node, Key})
+    end.
 
 init([]) ->
-    {Nodes, UpdateSeq} = initialize_nodelist(),
+    ets:new(?MODULE, [named_table, {read_concurrency, true}]),
+    UpdateSeq = initialize_nodelist(),
     {Pid, _} = spawn_monitor(fun() -> listen_for_changes(UpdateSeq) end),
-    {ok, #state{changes_pid = Pid, update_seq = UpdateSeq, nodes = Nodes}}.
+    {ok, #state{changes_pid = Pid, update_seq = UpdateSeq}}.
 
 handle_call(get_nodelist, _From, State) ->
-    {reply, lists:sort(dict:fetch_keys(State#state.nodes)), State};
+    {reply, lists:sort([N || {N,_} <- ets:tab2list(?MODULE)]), State};
 handle_call({get_node_info, Node, Key}, _From, State) ->
-    case dict:find(Node, State#state.nodes) of
-        {ok, NodeInfo} ->
-            {reply, couch_util:get_value(Key, NodeInfo), State};
-        error ->
-            {reply, error, State}
-    end;
+    Resp = try
+        couch_util:get_value(Key, ets:lookup_element(?MODULE, Node, 2))
+    catch error:badarg ->
+        error
+    end,
+    {reply, Resp, State};
 handle_call({add_node, Node, NodeInfo}, _From, #state{nodes=Nodes} = State) ->
     gen_event:notify(mem3_events, {add_node, Node}),
-    {reply, ok, State#state{nodes = dict:store(Node, NodeInfo, Nodes)}};
+    ets:insert(?MODULE, Node, NodeInfo),
+    {reply, ok, State};
 handle_call({remove_node, Node}, _From, #state{nodes=Nodes} = State) ->
     gen_event:notify(mem3_events, {remove_node, Node}),
-    {reply, ok, State#state{nodes = dict:erase(Node, Nodes)}};
+    ets:delete(?MODULE, Node),
+    {reply, ok, State};
 handle_call(_Call, _From, State) ->
     {noreply, State}.
 
@@ -74,6 +85,10 @@ handle_info(_Info, State) ->
 terminate(_Reason, _State) ->
     ok.
 
+code_change(_OldVsn, {state, ChangesPid, UpdateSeq, _}, _Extra) ->
+    ets:new(?MODULE, [named_table, {read_concurrency, true}]),
+    initialize_nodelist(),
+    {ok, #state{changes_pid = ChangesPid, update_seq = UpdateSeq}};
 code_change(_OldVsn, State, _Extra) ->
     {ok, State}.
 
@@ -82,27 +97,27 @@ code_change(_OldVsn, State, _Extra) ->
 initialize_nodelist() ->
     DbName = couch_config:get("mem3", "node_db", "nodes"),
     {ok, Db} = mem3_util:ensure_exists(DbName),
-    {ok, _, {_, Nodes0}} = couch_btree:fold(Db#db.id_tree, fun first_fold/3,
-                                       {Db, dict:new()}, []),
+    {ok, _, Db} = couch_btree:fold(Db#db.id_tree, fun first_fold/3, Db, []),
     % add self if not already present
-    case dict:find(node(), Nodes0) of
-    {ok, _} ->
-        Nodes = Nodes0;
-    error ->
+    case ets:lookup(?MODULE, node()) of
+    [_] ->
+        ok;
+    [] ->
+        ets:insert(?MODULE, {node(), []}),
         Doc = #doc{id = couch_util:to_binary(node())},
-        {ok, _} = couch_db:update_doc(Db, Doc, []),
-        Nodes = dict:store(node(), [], Nodes0)
+        {ok, _} = couch_db:update_doc(Db, Doc, [])
     end,
     couch_db:close(Db),
-    {Nodes, Db#db.update_seq}.
+    Db#db.update_seq.
 
 first_fold(#full_doc_info{id = <<"_design/", _/binary>>}, _, Acc) ->
     {ok, Acc};
 first_fold(#full_doc_info{deleted=true}, _, Acc) ->
     {ok, Acc};
-first_fold(#full_doc_info{id=Id}=DocInfo, _, {Db, Dict}) ->
+first_fold(#full_doc_info{id=Id}=DocInfo, _, Db) ->
     {ok, #doc{body={Props}}} = couch_db:open_doc(Db, DocInfo),
-    {ok, {Db, dict:store(mem3_util:to_atom(Id), Props, Dict)}}.
+    ets:insert(?MODULE, {mem3_util:to_atom(Id), Props}),
+    {ok, Db}.
 
 listen_for_changes(Since) ->
     DbName = couch_config:get("mem3", "node_db", "nodes"),

Reply via email to