WIP: projection store: read, get latest epoch
This commit is contained in:
parent
acf54e3c21
commit
022b9c4d1f
4 changed files with 125 additions and 20 deletions
|
@ -550,24 +550,26 @@ do_projection_command(Sock, LenHex, S) ->
|
||||||
{ok, ProjCmdBin} = gen_tcp:recv(Sock, Len),
|
{ok, ProjCmdBin} = gen_tcp:recv(Sock, Len),
|
||||||
ok = inet:setopts(Sock, [{packet, line}]),
|
ok = inet:setopts(Sock, [{packet, line}]),
|
||||||
ProjCmd = binary_to_term(ProjCmdBin),
|
ProjCmd = binary_to_term(ProjCmdBin),
|
||||||
case handle_projection_command(ProjCmd, S) of
|
put(hack, ProjCmd),
|
||||||
ok ->
|
Res = handle_projection_command(ProjCmd, S),
|
||||||
ok = gen_tcp:send(Sock, <<"OK\n">>);
|
ResBin = term_to_binary(Res),
|
||||||
{error, written} ->
|
ResLenHex = machi_util:int_to_hexbin(byte_size(ResBin), 32),
|
||||||
ok = gen_tcp:send(Sock, <<"ERROR WRITTEN\n">>);
|
ok = gen_tcp:send(Sock, [<<"OK ">>, ResLenHex, <<"\n">>, ResBin])
|
||||||
{error, not_written} ->
|
|
||||||
ok = gen_tcp:send(Sock, <<"ERROR NOT-WRITTEN\n">>);
|
|
||||||
Else ->
|
|
||||||
TODO = list_to_binary(io_lib:format("TODO-YOLO-~w", [Else])),
|
|
||||||
ok = gen_tcp:send(Sock, [<<"ERROR ">>, TODO, <<"\n">>])
|
|
||||||
end
|
|
||||||
catch
|
catch
|
||||||
What:Why ->
|
What:Why ->
|
||||||
|
io:format(user, "OOPS ~p\n", [get(hack)]),
|
||||||
|
io:format(user, "OOPS ~p ~p ~p\n", [What, Why, erlang:get_stacktrace()]),
|
||||||
WHA = list_to_binary(io_lib:format("TODO-YOLO.~w:~w-~w",
|
WHA = list_to_binary(io_lib:format("TODO-YOLO.~w:~w-~w",
|
||||||
[What, Why, erlang:get_stacktrace()])),
|
[What, Why, erlang:get_stacktrace()])),
|
||||||
_ = (catch gen_tcp:send(Sock, [<<"ERROR ">>, WHA, <<"\n">>]))
|
_ = (catch gen_tcp:send(Sock, [<<"ERROR ">>, WHA, <<"\n">>]))
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
handle_projection_command({get_latest_epoch, ProjType},
|
||||||
|
#state{proj_store=ProjStore}) ->
|
||||||
|
machi_projection_store:get_latest_epoch(ProjStore, ProjType);
|
||||||
|
handle_projection_command({read_projection, ProjType, Epoch},
|
||||||
|
#state{proj_store=ProjStore}) ->
|
||||||
|
machi_projection_store:read(ProjStore, ProjType, Epoch);
|
||||||
handle_projection_command({write_projection, ProjType, Proj},
|
handle_projection_command({write_projection, ProjType, Proj},
|
||||||
#state{proj_store=ProjStore}) ->
|
#state{proj_store=ProjStore}) ->
|
||||||
machi_projection_store:write(ProjStore, ProjType, Proj);
|
machi_projection_store:write(ProjStore, ProjType, Proj);
|
||||||
|
|
|
@ -31,6 +31,8 @@
|
||||||
list_files/2, list_files/3,
|
list_files/2, list_files/3,
|
||||||
|
|
||||||
%% Projection API
|
%% Projection API
|
||||||
|
get_latest_epoch/2, get_latest_epoch/3,
|
||||||
|
read_projection/3, read_projection/4,
|
||||||
write_projection/3, write_projection/4,
|
write_projection/3, write_projection/4,
|
||||||
|
|
||||||
%% Common API
|
%% Common API
|
||||||
|
@ -146,6 +148,50 @@ list_files(Host, TcpPort, EpochID) when is_integer(TcpPort) ->
|
||||||
catch gen_tcp:close(Sock)
|
catch gen_tcp:close(Sock)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
%% @doc Get the latest epoch number from the FLU's projection store.
|
||||||
|
|
||||||
|
-spec get_latest_epoch(port(), projection_type()) ->
|
||||||
|
{ok, -1|non_neg_integer()} | {error, term()}.
|
||||||
|
get_latest_epoch(Sock, ProjType)
|
||||||
|
when ProjType == 'public' orelse ProjType == 'private' ->
|
||||||
|
get_latest_epoch2(Sock, ProjType).
|
||||||
|
|
||||||
|
%% @doc Get the latest epoch number from the FLU's projection store.
|
||||||
|
|
||||||
|
-spec get_latest_epoch(inet_host(), inet_port(),
|
||||||
|
projection_type()) ->
|
||||||
|
{ok, -1|non_neg_integer()} | {error, term()}.
|
||||||
|
get_latest_epoch(Host, TcpPort, ProjType)
|
||||||
|
when ProjType == 'public' orelse ProjType == 'private' ->
|
||||||
|
Sock = machi_util:connect(Host, TcpPort),
|
||||||
|
try
|
||||||
|
get_latest_epoch2(Sock, ProjType)
|
||||||
|
after
|
||||||
|
catch gen_tcp:close(Sock)
|
||||||
|
end.
|
||||||
|
|
||||||
|
%% @doc Read a projection `Proj' of type `ProjType'.
|
||||||
|
|
||||||
|
-spec read_projection(port(), projection_type(), epoch_num()) ->
|
||||||
|
'ok' | {error, written} | {error, term()}.
|
||||||
|
read_projection(Sock, ProjType, Epoch)
|
||||||
|
when ProjType == 'public' orelse ProjType == 'private' ->
|
||||||
|
read_projection2(Sock, ProjType, Epoch).
|
||||||
|
|
||||||
|
%% @doc Read a projection `Proj' of type `ProjType'.
|
||||||
|
|
||||||
|
-spec read_projection(inet_host(), inet_port(),
|
||||||
|
projection_type(), epoch_num()) ->
|
||||||
|
'ok' | {error, written} | {error, term()}.
|
||||||
|
read_projection(Host, TcpPort, ProjType, Epoch)
|
||||||
|
when ProjType == 'public' orelse ProjType == 'private' ->
|
||||||
|
Sock = machi_util:connect(Host, TcpPort),
|
||||||
|
try
|
||||||
|
read_projection2(Sock, ProjType, Epoch)
|
||||||
|
after
|
||||||
|
catch gen_tcp:close(Sock)
|
||||||
|
end.
|
||||||
|
|
||||||
%% @doc Write a projection `Proj' of type `ProjType'.
|
%% @doc Write a projection `Proj' of type `ProjType'.
|
||||||
|
|
||||||
-spec write_projection(port(), projection_type(), projection()) ->
|
-spec write_projection(port(), projection_type(), projection()) ->
|
||||||
|
@ -480,6 +526,14 @@ trunc_hack2(Sock, EpochID, File) ->
|
||||||
{error, {badmatch, BadMatch}}
|
{error, {badmatch, BadMatch}}
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
get_latest_epoch2(Sock, ProjType) ->
|
||||||
|
ProjCmd = {get_latest_epoch, ProjType},
|
||||||
|
do_projection_common(Sock, ProjCmd).
|
||||||
|
|
||||||
|
read_projection2(Sock, ProjType, Epoch) ->
|
||||||
|
ProjCmd = {read_projection, ProjType, Epoch},
|
||||||
|
do_projection_common(Sock, ProjCmd).
|
||||||
|
|
||||||
write_projection2(Sock, ProjType, Proj) ->
|
write_projection2(Sock, ProjType, Proj) ->
|
||||||
ProjCmd = {write_projection, ProjType, Proj},
|
ProjCmd = {write_projection, ProjType, Proj},
|
||||||
do_projection_common(Sock, ProjCmd).
|
do_projection_common(Sock, ProjCmd).
|
||||||
|
@ -492,13 +546,15 @@ do_projection_common(Sock, ProjCmd) ->
|
||||||
LenHex = machi_util:int_to_hexbin(Len, 32),
|
LenHex = machi_util:int_to_hexbin(Len, 32),
|
||||||
Cmd = [<<"PROJ ">>, LenHex, <<"\n">>],
|
Cmd = [<<"PROJ ">>, LenHex, <<"\n">>],
|
||||||
ok = gen_tcp:send(Sock, [Cmd, ProjCmdBin]),
|
ok = gen_tcp:send(Sock, [Cmd, ProjCmdBin]),
|
||||||
|
ok = inet:setopts(Sock, [{packet, line}]),
|
||||||
{ok, Line} = gen_tcp:recv(Sock, 0),
|
{ok, Line} = gen_tcp:recv(Sock, 0),
|
||||||
PathLen = byte_size(Line) - 3 - 16 - 1 - 1,
|
|
||||||
case Line of
|
case Line of
|
||||||
<<"OK\n">> ->
|
<<"OK ", ResLenHex:8/binary, "\n">> ->
|
||||||
ok;
|
ResLen = machi_util:hexstr_to_int(ResLenHex),
|
||||||
<<"ERROR WRITTEN\n">> ->
|
ok = inet:setopts(Sock, [{packet, raw}]),
|
||||||
{error, written};
|
{ok, ResBin} = gen_tcp:recv(Sock, ResLen),
|
||||||
|
ok = inet:setopts(Sock, [{packet, line}]),
|
||||||
|
binary_to_term(ResBin);
|
||||||
Else ->
|
Else ->
|
||||||
{error, Else}
|
{error, Else}
|
||||||
end
|
end
|
||||||
|
|
|
@ -25,6 +25,8 @@
|
||||||
%% API
|
%% API
|
||||||
-export([
|
-export([
|
||||||
start_link/3,
|
start_link/3,
|
||||||
|
get_latest_epoch/2, get_latest_epoch/3,
|
||||||
|
read/3, read/4,
|
||||||
write/3, write/4
|
write/3, write/4
|
||||||
]).
|
]).
|
||||||
|
|
||||||
|
@ -37,14 +39,28 @@
|
||||||
private_dir = "" :: string(),
|
private_dir = "" :: string(),
|
||||||
wedged = true :: boolean(),
|
wedged = true :: boolean(),
|
||||||
wedge_notify_pid :: pid() | atom(),
|
wedge_notify_pid :: pid() | atom(),
|
||||||
max_public_epoch = -1 :: non_neg_integer(),
|
max_public_epoch = -1 :: -1 | non_neg_integer(),
|
||||||
max_private_epoch = -1 :: non_neg_integer()
|
max_private_epoch = -1 :: -1 | non_neg_integer()
|
||||||
}).
|
}).
|
||||||
|
|
||||||
start_link(RegName, DataDir, NotifyWedgeStateChanges) ->
|
start_link(RegName, DataDir, NotifyWedgeStateChanges) ->
|
||||||
gen_server:start_link({local, RegName},
|
gen_server:start_link({local, RegName},
|
||||||
?MODULE, [DataDir, NotifyWedgeStateChanges], []).
|
?MODULE, [DataDir, NotifyWedgeStateChanges], []).
|
||||||
|
|
||||||
|
get_latest_epoch(PidSpec, ProjType) ->
|
||||||
|
get_latest_epoch(PidSpec, ProjType, infinity).
|
||||||
|
|
||||||
|
get_latest_epoch(PidSpec, ProjType, Timeout)
|
||||||
|
when ProjType == 'public' orelse ProjType == 'private' ->
|
||||||
|
g_call(PidSpec, {get_latest_epoch, ProjType}, Timeout).
|
||||||
|
|
||||||
|
read(PidSpec, ProjType, Epoch) ->
|
||||||
|
read(PidSpec, ProjType, Epoch, infinity).
|
||||||
|
|
||||||
|
read(PidSpec, ProjType, Epoch, Timeout)
|
||||||
|
when ProjType == 'public' orelse ProjType == 'private' ->
|
||||||
|
g_call(PidSpec, {read, ProjType, Epoch}, Timeout).
|
||||||
|
|
||||||
write(PidSpec, ProjType, Proj) ->
|
write(PidSpec, ProjType, Proj) ->
|
||||||
write(PidSpec, ProjType, Proj, infinity).
|
write(PidSpec, ProjType, Proj, infinity).
|
||||||
|
|
||||||
|
@ -79,6 +95,16 @@ g_call(PidSpec, Arg, Timeout) ->
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
handle_call({{get_latest_epoch, ProjType}, LC1}, _From, S) ->
|
||||||
|
LC2 = lclock_update(LC1),
|
||||||
|
Epoch = if ProjType == public -> S#state.max_public_epoch;
|
||||||
|
ProjType == private -> S#state.max_private_epoch
|
||||||
|
end,
|
||||||
|
{reply, {{ok, Epoch}, LC2}, S};
|
||||||
|
handle_call({{read, ProjType, Epoch}, LC1}, _From, S) ->
|
||||||
|
LC2 = lclock_update(LC1),
|
||||||
|
{Reply, NewS} = do_proj_read(ProjType, Epoch, S),
|
||||||
|
{reply, {Reply, LC2}, NewS};
|
||||||
handle_call({{write, ProjType, Proj}, LC1}, _From, S) ->
|
handle_call({{write, ProjType, Proj}, LC1}, _From, S) ->
|
||||||
LC2 = lclock_update(LC1),
|
LC2 = lclock_update(LC1),
|
||||||
{Reply, NewS} = do_proj_write(ProjType, Proj, S),
|
{Reply, NewS} = do_proj_write(ProjType, Proj, S),
|
||||||
|
@ -101,6 +127,19 @@ code_change(_OldVsn, S, _Extra) ->
|
||||||
|
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
do_proj_read(ProjType, Epoch, S) ->
|
||||||
|
Dir = pick_path(ProjType, S),
|
||||||
|
Path = filename:join(Dir, epoch2name(Epoch)),
|
||||||
|
case file:read_file(Path) of
|
||||||
|
{ok, Bin} ->
|
||||||
|
%% TODO and if Bin is corrupt? (even if binary_to_term() succeeds)
|
||||||
|
{{ok, binary_to_term(Bin)}, S};
|
||||||
|
{error, enoent} ->
|
||||||
|
{{error, not_written}, S};
|
||||||
|
{error, Else} ->
|
||||||
|
{{error, Else}, S}
|
||||||
|
end.
|
||||||
|
|
||||||
do_proj_write(ProjType, #projection_v1{epoch_number=Epoch}=Proj, S) ->
|
do_proj_write(ProjType, #projection_v1{epoch_number=Epoch}=Proj, S) ->
|
||||||
%% TODO: We probably ought to check the projection checksum for sanity, eh?
|
%% TODO: We probably ought to check the projection checksum for sanity, eh?
|
||||||
Dir = pick_path(ProjType, S),
|
Dir = pick_path(ProjType, S),
|
||||||
|
|
|
@ -124,11 +124,19 @@ flu_projection_smoke_test() ->
|
||||||
|
|
||||||
FLU1 = setup_test_flu(projection_test_flu, TcpPort, DataDir),
|
FLU1 = setup_test_flu(projection_test_flu, TcpPort, DataDir),
|
||||||
try
|
try
|
||||||
|
{ok, -1} = ?FLU_C:get_latest_epoch(Host, TcpPort, public),
|
||||||
|
{ok, -1} = ?FLU_C:get_latest_epoch(Host, TcpPort, private),
|
||||||
|
|
||||||
P1 = machi_projection:new(1, a, [a], [], [a], [], []),
|
P1 = machi_projection:new(1, a, [a], [], [a], [], []),
|
||||||
ok = ?FLU_C:write_projection(Host, TcpPort, public, P1),
|
ok = ?FLU_C:write_projection(Host, TcpPort, public, P1),
|
||||||
{error, written} = ?FLU_C:write_projection(Host, TcpPort, public, P1),
|
{error, written} = ?FLU_C:write_projection(Host, TcpPort, public, P1),
|
||||||
|
{ok, P1} = ?FLU_C:read_projection(Host, TcpPort, public, 1),
|
||||||
|
{error, not_written} = ?FLU_C:read_projection(Host, TcpPort, public, 2),
|
||||||
|
|
||||||
ok = ?FLU_C:write_projection(Host, TcpPort, private, P1),
|
ok = ?FLU_C:write_projection(Host, TcpPort, private, P1),
|
||||||
{error, written} = ?FLU_C:write_projection(Host, TcpPort, private, P1),
|
{error, written} = ?FLU_C:write_projection(Host, TcpPort, private, P1),
|
||||||
|
{ok, P1} = ?FLU_C:read_projection(Host, TcpPort, private, 1),
|
||||||
|
{error, not_written} = ?FLU_C:read_projection(Host, TcpPort, private, 2),
|
||||||
|
|
||||||
ok = ?FLU_C:quit(machi_util:connect(Host, TcpPort))
|
ok = ?FLU_C:quit(machi_util:connect(Host, TcpPort))
|
||||||
after
|
after
|
||||||
|
|
Loading…
Reference in a new issue