summaryrefslogtreecommitdiff
path: root/src/fabric/src/fabric2_server.erl
diff options
context:
space:
mode:
Diffstat (limited to 'src/fabric/src/fabric2_server.erl')
-rw-r--r--src/fabric/src/fabric2_server.erl203
1 files changed, 199 insertions, 4 deletions
diff --git a/src/fabric/src/fabric2_server.erl b/src/fabric/src/fabric2_server.erl
index be674b10e..0da2b79e9 100644
--- a/src/fabric/src/fabric2_server.erl
+++ b/src/fabric/src/fabric2_server.erl
@@ -27,7 +27,8 @@
maybe_remove/1,
fdb_directory/0,
- fdb_cluster/0
+ fdb_cluster/0,
+ get_retry_limit/0
]).
@@ -42,9 +43,12 @@
-include_lib("couch/include/couch_db.hrl").
+-include_lib("kernel/include/file.hrl").
+-include_lib("kernel/include/logger.hrl").
-
--define(CLUSTER_FILE, "/usr/local/etc/foundationdb/fdb.cluster").
+-define(CLUSTER_FILE_MACOS, "/usr/local/etc/foundationdb/fdb.cluster").
+-define(CLUSTER_FILE_LINUX, "/etc/foundationdb/fdb.cluster").
+-define(CLUSTER_FILE_WIN32, "C:/ProgramData/foundationdb/fdb.cluster").
-define(FDB_DIRECTORY, fdb_directory).
-define(FDB_CLUSTER, fdb_cluster).
-define(DEFAULT_FDB_DIRECTORY, <<"couchdb">>).
@@ -192,6 +196,12 @@ fdb_directory() ->
fdb_cluster() ->
get_env(?FDB_CLUSTER).
+
+get_retry_limit() ->
+ Default = list_to_integer(?DEFAULT_RETRY_LIMIT),
+ config:get_integer(?TX_OPTIONS_SECTION, "retry_limit", Default).
+
+
get_env(Key) ->
case get(Key) of
undefined ->
@@ -212,7 +222,7 @@ get_db_and_cluster(EunitDbOpts) ->
{ok, true} ->
{<<"eunit_test">>, erlfdb_util:get_test_db(EunitDbOpts)};
undefined ->
- ClusterFileStr = config:get("erlfdb", "cluster_file", ?CLUSTER_FILE),
+ ClusterFileStr = get_cluster_file_path(),
{ok, ConnectionStr} = file:read_file(ClusterFileStr),
DbHandle = erlfdb:open(iolist_to_binary(ClusterFileStr)),
{string:trim(ConnectionStr), DbHandle}
@@ -220,6 +230,106 @@ get_db_and_cluster(EunitDbOpts) ->
apply_tx_options(Db, config:get(?TX_OPTIONS_SECTION)),
{Cluster, Db}.
+get_cluster_file_path() ->
+ Locations = [
+ {custom, config:get("erlfdb", "cluster_file")},
+ {custom, os:getenv("FDB_CLUSTER_FILE", undefined)}
+ ] ++ default_locations(os:type()),
+ case find_cluster_file(Locations) of
+ {ok, Location} ->
+ Location;
+ {error, Reason} ->
+ erlang:error(Reason)
+ end.
+
+
+default_locations({unix, _}) ->
+ [
+ {default, ?CLUSTER_FILE_MACOS},
+ {default, ?CLUSTER_FILE_LINUX}
+ ];
+
+default_locations({win32, _}) ->
+ [
+ {default, ?CLUSTER_FILE_WIN32}
+ ].
+
+
+find_cluster_file([]) ->
+ {error, cluster_file_missing};
+
+find_cluster_file([{custom, undefined} | Rest]) ->
+ find_cluster_file(Rest);
+
+find_cluster_file([{Type, Location} | Rest]) ->
+ Msg = #{
+ what => fdb_connection_setup,
+ configuration_type => Type,
+ cluster_file => Location
+ },
+ case file:read_file_info(Location, [posix]) of
+ {ok, #file_info{access = read_write}} ->
+ ?LOG_INFO(Msg#{status => ok}),
+ couch_log:info(
+ "Using ~s FDB cluster file: ~s",
+ [Type, Location]
+ ),
+ {ok, Location};
+ {ok, #file_info{access = read}} ->
+ ?LOG_WARNING(Msg#{
+ status => read_only_file,
+ details => "If coordinators are changed without updating this "
+ "file CouchDB may be unable to connect to the FDB cluster!"
+ }),
+ couch_log:warning(
+ "Using read-only ~s FDB cluster file: ~s -- if coordinators "
+ "are changed without updating this file CouchDB may be unable "
+ "to connect to the FDB cluster!",
+ [Type, Location]
+ ),
+ {ok, Location};
+ {ok, _} ->
+ ?LOG_ERROR(Msg#{
+ status => permissions_error,
+ details => "CouchDB needs read/write access to FDB cluster file"
+ }),
+ couch_log:error(
+ "CouchDB needs read/write access to FDB cluster file: ~s",
+ [Location]
+ ),
+ {error, cluster_file_permissions};
+ {error, Reason} when Type =:= custom ->
+ ?LOG_ERROR(Msg#{
+ status => Reason,
+ details => file:format_error(Reason)
+ }),
+ couch_log:error(
+ "Encountered ~p error looking for FDB cluster file: ~s",
+ [Reason, Location]
+ ),
+ {error, Reason};
+ {error, enoent} when Type =:= default ->
+ ?LOG_INFO(Msg#{
+ status => enoent,
+ details => file:format_error(enoent)
+ }),
+ couch_log:info(
+ "No FDB cluster file found at ~s",
+ [Location]
+ ),
+ find_cluster_file(Rest);
+ {error, Reason} when Type =:= default ->
+ ?LOG_WARNING(Msg#{
+ status => Reason,
+ details => file:format_error(Reason)
+ }),
+ couch_log:warning(
+ "Encountered ~p error looking for FDB cluster file: ~s",
+ [Reason, Location]
+ ),
+ find_cluster_file(Rest)
+ end.
+
apply_tx_options(Db, Cfg) ->
maps:map(fun(Option, {Type, Default}) ->
@@ -240,6 +350,11 @@ apply_tx_option(Db, Option, Val, integer) ->
set_option(Db, Option, list_to_integer(Val))
catch
error:badarg ->
+ ?LOG_ERROR(#{
+ what => invalid_transaction_option_value,
+ option => Option,
+ value => Val
+ }),
Msg = "~p : Invalid integer tx option ~p = ~p",
couch_log:error(Msg, [?MODULE, Option, Val])
end;
@@ -250,6 +365,12 @@ apply_tx_option(Db, Option, Val, binary) ->
true ->
set_option(Db, Option, BinVal);
false ->
+ ?LOG_ERROR(#{
+ what => invalid_transaction_option_value,
+ option => Option,
+ value => Val,
+ details => "string transaction option must be less than 16 bytes"
+ }),
Msg = "~p : String tx option ~p is larger than 16 bytes",
couch_log:error(Msg, [?MODULE, Option])
end.
@@ -262,6 +383,11 @@ set_option(Db, Option, Val) ->
% This could happen if the option is not supported by erlfdb or
% fdbsever.
error:badarg ->
+ ?LOG_ERROR(#{
+ what => transaction_option_error,
+ option => Option,
+ value => Val
+ }),
Msg = "~p : Could not set fdb tx option ~p = ~p",
couch_log:error(Msg, [?MODULE, Option, Val])
end.
@@ -274,3 +400,72 @@ sanitize(#{} = Db) ->
security_fun := undefined,
interactive := false
}.
+
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
+setup() ->
+ meck:new(file, [unstick, passthrough]),
+ meck:expect(file, read_file_info, fun
+ ("ok.cluster", _) ->
+ {ok, #file_info{access = read_write}};
+ ("readonly.cluster", _) ->
+ {ok, #file_info{access = read}};
+ ("noaccess.cluster", _) ->
+ {ok, #file_info{access = none}};
+ ("missing.cluster", _) ->
+ {error, enoent};
+ (Path, Options) ->
+ meck:passthrough([Path, Options])
+ end).
+
+teardown(_) ->
+ meck:unload().
+
+find_cluster_file_test_() ->
+ {setup,
+ fun setup/0,
+ fun teardown/1,
+ [
+ {"ignore unspecified config", ?_assertEqual(
+ {ok, "ok.cluster"},
+ find_cluster_file([
+ {custom, undefined},
+ {custom, "ok.cluster"}
+ ])
+ )},
+
+ {"allow read-only file", ?_assertEqual(
+ {ok, "readonly.cluster"},
+ find_cluster_file([
+ {custom, "readonly.cluster"}
+ ])
+ )},
+
+ {"fail if no access to configured cluster file", ?_assertEqual(
+ {error, cluster_file_permissions},
+ find_cluster_file([
+ {custom, "noaccess.cluster"}
+ ])
+ )},
+
+ {"fail if configured cluster file is missing", ?_assertEqual(
+ {error, enoent},
+ find_cluster_file([
+ {custom, "missing.cluster"},
+ {default, "ok.cluster"}
+ ])
+ )},
+
+ {"check multiple default locations", ?_assertEqual(
+ {ok, "ok.cluster"},
+ find_cluster_file([
+ {default, "missing.cluster"},
+ {default, "ok.cluster"}
+ ])
+ )}
+ ]
+ }.
+
+-endif.