couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From kocol...@apache.org
Subject [couchdb] 01/01: Enable discovery of cluster nodes through DNS SRV
Date Sun, 14 Jan 2018 02:37:35 GMT
This is an automated email from the ASF dual-hosted git repository.

kocolosk pushed a commit to branch dns-cluster-discovery
in repository https://gitbox.apache.org/repos/asf/couchdb.git

commit f3a2fb165fea4aee4f42ce0fbcd98ba461c26c84
Author: Adam Kocoloski <kocolosk@apache.org>
AuthorDate: Fri Jan 12 22:16:49 2018 -0500

    Enable discovery of cluster nodes through DNS SRV
    
    This patch enables CouchDB nodes to check SRV records in DNS for cluster
    peers and automatically add any discovered peers to the nodes DB. This
    behavior is disabled by default and can be enabled as follows:
    
    [mem3]
    use_dns_service_discovery = true
    service_record_name = _couchdb._tcp.example.net
    
    If the `service_record_name` is omitted we attempt to generate the
    appropriate record for the query by prepending _couchdb._tcp. to the DNS
    domain name portion of the host / VM / container FQDN.
---
 src/mem3/src/mem3_dns.erl   | 109 ++++++++++++++++++++++++++++++++++++++++++++
 src/mem3/src/mem3_nodes.erl |   1 +
 src/mem3/src/mem3_sup.erl   |   1 +
 3 files changed, 111 insertions(+)

diff --git a/src/mem3/src/mem3_dns.erl b/src/mem3/src/mem3_dns.erl
new file mode 100644
index 0000000..0a954fd
--- /dev/null
+++ b/src/mem3/src/mem3_dns.erl
@@ -0,0 +1,109 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+%   http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(mem3_dns).
+-behaviour(gen_server).
+-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
+    code_change/3]).
+
+-export([start_link/0]).
+-export([discover_peers/0]).
+
+-include_lib("couch/include/couch_db.hrl").
+
+-record(state, {
+    record_name,
+    discovered_names,
+    last_error,
+    timeout = 2000,
+    max_tries = 10
+}).
+
+start_link() ->
+    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
+
+discover_peers() ->
+    case config:get("mem3", "use_dns_service_discovery", "false") of
+    "true" ->
+        gen_server:cast(?MODULE, discover_peers);
+    _ ->
+        ok
+    end.
+
+init([]) ->
+    {ok, ServiceRecord} = construct_service_record(),
+    {ok, #state{record_name = ServiceRecord}}.
+
+handle_call(_Call, _From, State) ->
+    {reply, ok, State}.
+
+handle_cast(discover_peers, #state{max_tries = 0} = State) ->
+    {ok, State};
+
+handle_cast(discover_peers, St) ->
+    case query_dns(St#state.record_name) of
+    {ok, Hostnames} ->
+        add_nodes(Hostnames),
+        {ok, St#state{discovered_names = Hostnames}};
+    {error, Error} ->
+        #state{max_tries = Max, timeout = Timeout} = St,
+        NewSt = St#state{
+            last_error = Error,
+            max_tries = Max-1,
+            timeout = 2*Timeout
+        },
+        {ok, NewSt, Timeout}
+    end.
+
+handle_info(timeout, State) ->
+    handle_cast(discover_peers, State).
+
+terminate(_Reason, _State) ->
+    ok.
+
+code_change(_OldVsn, #state{}=State, _Extra) ->
+    {ok, State}.
+
+query_dns(ServiceRecord) ->
+    Opts = [{edns, 0}],
+    case inet_res:resolve(ServiceRecord, in, srv, Opts) of
+    {ok, DnsMsg} ->
+        Hostnames = lists:foldl(fun(Answer, Acc) ->
+            case inet_dns:rr(Answer, type) of
+            srv ->
+                {_, _, _, Service} = inet_dns:rr(Answer, data),
+                [Service | Acc];
+            _ ->
+                Acc
+            end
+        end, [], inet_dns:msg(DnsMsg, anlist)),
+        {ok, Hostnames};
+    Error ->
+        Error
+    end.
+
+construct_service_record() ->
+    case config:get("mem3", "service_record_name") of
+    undefined ->
+        [_ | Domain] = string:tokens(net_adm:localhost(), "."),
+        {ok, string:join(["_couchdb", "_tcp" | Domain], ".")};
+    ServiceRecord ->
+        {ok, ServiceRecord}
+    end.
+
+add_nodes(Hostnames) ->
+    DbName = config:get("mem3", "nodes_db", "_nodes"),
+    {ok, Db} = mem3_util:ensure_exists(DbName),
+    Peers = lists:map(fun(Hostname) ->
+        #doc{id = couch_util:to_binary("couchdb@" ++ Hostname)}
+    end, Hostnames),
+    couch_db:update_docs(Db, Peers, []).
diff --git a/src/mem3/src/mem3_nodes.erl b/src/mem3/src/mem3_nodes.erl
index 555389b..02f1a2d 100644
--- a/src/mem3/src/mem3_nodes.erl
+++ b/src/mem3/src/mem3_nodes.erl
@@ -43,6 +43,7 @@ get_node_info(Node, Key) ->
 init([]) ->
     ets:new(?MODULE, [named_table, {read_concurrency, true}]),
     UpdateSeq = initialize_nodelist(),
+    mem3_dns:discover_peers(),
     {Pid, _} = spawn_monitor(fun() -> listen_for_changes(UpdateSeq) end),
     {ok, #state{changes_pid = Pid, update_seq = UpdateSeq}}.
 
diff --git a/src/mem3/src/mem3_sup.erl b/src/mem3/src/mem3_sup.erl
index 80b8ca3..0647447 100644
--- a/src/mem3/src/mem3_sup.erl
+++ b/src/mem3/src/mem3_sup.erl
@@ -20,6 +20,7 @@ start_link() ->
 init(_Args) ->
     Children = [
         child(mem3_events),
+        child(mem3_dns),
         child(mem3_nodes),
         child(mem3_sync_nodes), % Order important?
         child(mem3_sync),

-- 
To stop receiving notification emails like this one, please contact
"commits@couchdb.apache.org" <commits@couchdb.apache.org>.

Mime
View raw message