arangodb erlang数据库驱动
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

278 строки
11 KiB

-module(agVstCli).
-include("agVstCli.hrl").
-include("eArango.hrl").
-compile(inline).
-compile({inline_size, 128}).
-export([
%% Common Request API
callAgency/5
, callAgency/6
, callAgency/7
, castAgency/5
, castAgency/6
, castAgency/7
, castAgency/8
, receiveRequestRet/2
%% Pools API
, startPool/2
, startPool/3
, stopPool/1
%% Single Process DbAPI
, connectDb/1
, disConnectDb/1
, getCurDbInfo/1
, useDatabase/2
]).
-spec callAgency(poolNameOrSocket(), method(), path(), headers(), body()) -> term() | {error, term()}.
callAgency(PoolNameOrSocket, Method, Path, Headers, Body) ->
callAgency(PoolNameOrSocket, Method, Path, Headers, Body, false, ?DEFAULT_TIMEOUT).
-spec callAgency(poolNameOrSocket(), method(), path(), headers(), body(), boolean()) -> term() | {error, atom()}.
callAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem) ->
callAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem, ?DEFAULT_TIMEOUT).
-spec callAgency(poolNameOrSocket(), method(), path(), headers(), body(), boolean(), timeout()) -> term() | {error, atom()}.
callAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem, Timeout) ->
case castAgency(PoolNameOrSocket, Method, Path, Headers, Body, self(), IsSystem, Timeout) of
{waitRRT, RequestId, MonitorRef} ->
receiveRequestRet(RequestId, MonitorRef);
{error, _Reason} = Err ->
Err;
Ret ->
Ret
end.
-spec castAgency(poolNameOrSocket(), method(), path(), headers(), body()) -> {ok, messageId()} | {error, atom()}.
castAgency(PoolNameOrSocket, Method, Path, Headers, Body) ->
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, self(), false, ?DEFAULT_TIMEOUT).
-spec castAgency(poolNameOrSocket(), method(), path(), headers(), body(), boolean()) -> {ok, messageId()} | {error, atom()}.
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem) ->
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, self(), IsSystem, ?DEFAULT_TIMEOUT).
-spec castAgency(poolNameOrSocket(), method(), path(), headers(), body(), boolean(), timeout()) -> {ok, messageId()} | {error, atom()}.
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, IsSystem, Timeout) ->
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, self(), IsSystem, Timeout).
-spec castAgency(poolNameOrSocket(), method(), path(), headers(), body(), pid(), boolean(), timeout()) -> {ok, messageId()} | {error, atom()}.
castAgency(PoolNameOrSocket, Method, Path, Headers, Body, Pid, IsSystem, Timeout) ->
OverTime =
case Timeout of
infinity -> infinity;
_ ->
erlang:monotonic_time(millisecond) + Timeout
end,
case erlang:is_atom(PoolNameOrSocket) of
true ->
case agAgencyPoolMgrIns:getOneAgency(PoolNameOrSocket) of
{error, pool_not_found} = Err ->
Err;
undefined ->
{error, undefined_server};
AgencyName ->
MonitorRef = erlang:monitor(process, AgencyName),
RequestId = {AgencyName, MonitorRef},
catch AgencyName ! #agReq{method = Method, path = Path, headers = Headers, body = Body, messageId = RequestId, fromPid = Pid, overTime = OverTime, isSystem = IsSystem},
{waitRRT, RequestId, MonitorRef}
end;
_ ->
case getCurDbInfo(PoolNameOrSocket) of
{DbName, UserPassWord, Host, Protocol} ->
Request = agVstProtocol:request(IsSystem, Body, Method, Host, DbName, Path, [UserPassWord | Headers]),
case Protocol of
tcp ->
case gen_tcp:send(PoolNameOrSocket, Request) of
ok ->
receiveTcpData(undefined, PoolNameOrSocket, binary:compile_pattern(<<"\r\n">>), binary:compile_pattern(<<"\r\n\r\n">>), Method == ?AgHead);
{error, Reason} = Err ->
?AgWarn(castAgency, ":gen_tcp send error: ~p ~n", [Reason]),
disConnectDb(PoolNameOrSocket),
Err
end;
ssl ->
case ssl:send(PoolNameOrSocket, Request) of
ok ->
receiveSslData(undefined, PoolNameOrSocket, binary:compile_pattern(<<"\r\n">>), binary:compile_pattern(<<"\r\n\r\n">>), Method == ?AgHead);
{error, Reason} = Err ->
?AgWarn(castAgency, ":ssl send error: ~p ~n", [Reason]),
disConnectDb(PoolNameOrSocket),
Err
end
end;
_ ->
{error, dbinfoNotFound}
end
end.
-spec receiveRequestRet(messageId(), reference()) -> {StatusCode :: non_neg_integer(), Body :: binary(), Headers :: binary()} | {error, term()}.
receiveRequestRet(RequestId, MonitorRef) ->
receive
#agReqRet{messageId = RequestId, reply = Reply} ->
erlang:demonitor(MonitorRef),
case Reply of
{_StatusCode, Body, _Headers} ->
case Body of
<<>> ->
erlang:setelement(2, Reply, #{});
_ ->
erlang:setelement(2, Reply, jiffy:decode(Body, [return_maps, copy_strings]))
end;
_ ->
Reply
end;
{'DOWN', MonitorRef, process, _Pid, Reason} ->
{error, {agencyDown, Reason}}
end.
-spec receiveTcpData(recvState() | undefined, socket(), binary:cp(), binary:cp(), boolean()) -> {ok, term(), term()} | {error, term()}.
receiveTcpData(RecvState, Socket, Rn, RnRn, IsHeadMethod) ->
receive
{tcp, Socket, Data} ->
try agVstProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} ->
case Body of
<<>> ->
{ok, #{}, StatusCode, Headers};
_ ->
{ok, jiffy:decode(Body, [return_maps, copy_strings]), StatusCode, Headers}
end;
{ok, NewRecvState} ->
receiveTcpData(NewRecvState, Socket, Rn, RnRn, IsHeadMethod);
{error, Reason} ->
?AgWarn(receiveTcpData, "handle tcp data error: ~p ~n", [Reason]),
disConnectDb(Socket),
{error, {tcpDataError, Reason}}
catch
E:R:S ->
?AgWarn(receiveTcpData, "handle tcp data crash: ~p:~p~n~p ~n ", [E, R, S]),
disConnectDb(Socket),
{error, handledataError}
end;
{tcp_closed, Socket} ->
disConnectDb(Socket),
{error, tcp_closed};
{tcp_error, Socket, Reason} ->
disConnectDb(Socket),
{error, {tcp_error, Reason}}
end.
-spec receiveSslData(recvState() | undefined, socket(), binary:cp(), binary:cp(), boolean()) -> {ok, term(), term()} | {error, term()}.
receiveSslData(RecvState, Socket, Rn, RnRn, IsHeadMethod) ->
receive
{ssl, Socket, Data} ->
try agVstProtocol:response(RecvState, Rn, RnRn, Data, IsHeadMethod) of
{done, #recvState{statusCode = StatusCode, headers = Headers, body = Body}} ->
case Body of
<<>> ->
{ok, #{}, StatusCode, Headers};
_ ->
{ok, jiffy:decode(Body, [return_maps, copy_strings]), StatusCode, Headers}
end;
{ok, NewRecvState} ->
receiveSslData(NewRecvState, Socket, Rn, RnRn, IsHeadMethod);
{error, Reason} ->
?AgWarn(receiveSslData, "handle tcp data error: ~p ~n", [Reason]),
disConnectDb(Socket),
{error, {sslDataError, Reason}}
catch
E:R:S ->
?AgWarn(receiveSslData, "handle tcp data crash: ~p:~p~n~p ~n ", [E, R, S]),
disConnectDb(Socket),
{error, handledataError}
end;
{ssl_closed, Socket} ->
disConnectDb(Socket),
{error, ssl_closed};
{ssl_error, Socket, Reason} ->
disConnectDb(Socket),
{error, {ssl_error, Reason}}
end.
-spec startPool(poolName(), dbCfgs()) -> ok | {error, poolNameUsed}.
startPool(PoolName, DbCfgs) ->
agAgencyPoolMgrIns:startPool(PoolName, DbCfgs, []).
-spec startPool(poolName(), dbCfgs(), agencyCfgs()) -> ok | {error, poolNameUsed}.
startPool(PoolName, DbCfgs, AgencyCfgs) ->
agAgencyPoolMgrIns:startPool(PoolName, DbCfgs, AgencyCfgs).
-spec stopPool(poolName()) -> ok | {error, poolNotStarted}.
stopPool(PoolName) ->
agAgencyPoolMgrIns:stopPool(PoolName).
-spec connectDb(dbCfgs()) -> {ok, socket()} | {error, term()}.
connectDb(DbCfgs) ->
#dbOpts{
host = Host,
port = Port,
hostname = HostName,
dbName = DbName,
protocol = Protocol,
userPassword = UserPassword,
socketOpts = SocketOpts
} = agMiscUtils:dbOpts(DbCfgs),
case inet:getaddrs(HostName, inet) of
{ok, IPList} ->
Ip = agMiscUtils:randomElement(IPList),
case Protocol of
tcp ->
case gen_tcp:connect(Ip, Port, SocketOpts, ?AgDefConnTimeout) of
{ok, Socket} ->
setCurDbInfo(Socket, DbName, UserPassword, Host, Protocol),
{ok, Socket};
{error, Reason} = Err ->
?AgWarn(connectDb, "connect error: ~p~n", [Reason]),
Err
end;
ssl ->
case ssl:connect(Ip, Port, SocketOpts, ?AgDefConnTimeout) of
{ok, Socket} ->
setCurDbInfo(Socket, DbName, UserPassword, Host, Protocol),
{ok, Socket};
{error, Reason} = Err ->
?AgWarn(connectDb, "connect error: ~p~n", [Reason]),
Err
end
end;
{error, Reason} = Err ->
?AgWarn(connectDb, "getaddrs error: ~p~n", [Reason]),
Err
end.
-spec disConnectDb(socket()) -> ok | {error, term()}.
disConnectDb(Socket) ->
case erlang:erase({'$agDbInfo', Socket}) of
undefined ->
ignore;
{_DbName, _UserPassword, _Host, Protocol} ->
case Protocol of
tcp ->
gen_tcp:close(Socket);
ssl ->
ssl:close(Socket)
end
end.
-spec setCurDbInfo(socket(), binary(), tuple(), host(), protocol()) -> term().
setCurDbInfo(Socket, DbName, UserPassword, Host, Protocol) ->
erlang:put({'$agDbInfo', Socket}, {DbName, UserPassword, Host, Protocol}).
-spec getCurDbInfo(socket()) -> term().
getCurDbInfo(Socket) ->
erlang:get({'$agDbInfo', Socket}).
-spec useDatabase(socket(), binary()) -> ok.
useDatabase(Socket, NewDbName) ->
case erlang:get({'$agDbInfo', Socket}) of
undefined ->
ignore;
{_DbName, UserPassword, Host, Protocol} ->
erlang:put({'$agDbInfo', Socket}, {<<"/_db/", NewDbName/binary>>, UserPassword, Host, Protocol})
end,
ok.