2015-03-31 07:46:03 +00:00
|
|
|
%% -------------------------------------------------------------------
|
|
|
|
%%
|
|
|
|
%% Copyright (c) 2007-2015 Basho Technologies, Inc. All Rights Reserved.
|
|
|
|
%%
|
|
|
|
%% This file is provided to you 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.
|
|
|
|
%%
|
|
|
|
%% -------------------------------------------------------------------
|
|
|
|
|
2015-04-08 05:24:07 +00:00
|
|
|
%% @doc The Machi FLU file server + file location sequencer.
|
|
|
|
%%
|
|
|
|
%% This module implements only the Machi FLU file server and its
|
|
|
|
%% implicit sequencer.
|
|
|
|
%% Please see the EDoc "Overview" for details about the FLU as a
|
|
|
|
%% primitive file server process vs. the larger Machi design of a FLU
|
|
|
|
%% as a sequencer + file server + chain manager group of processes.
|
|
|
|
%%
|
|
|
|
%% For the moment, this module also implements a rudimentary TCP-based
|
|
|
|
%% protocol as the sole supported access method to the server,
|
|
|
|
%% sequencer, and projection store. Conceptually, those three
|
|
|
|
%% services are independent and ought to have their own protocols. As
|
|
|
|
%% a practical matter, there is no need for wire protocol
|
|
|
|
%% compatibility. Furthermore, from the perspective of failure
|
|
|
|
%% detection, it is very convenient that all three FLU-related
|
|
|
|
%% services are accessed using the same single TCP port.
|
|
|
|
%%
|
|
|
|
%% The FLU is named after the CORFU server "FLU" or "FLash Unit" server.
|
|
|
|
%%
|
2015-04-30 06:15:43 +00:00
|
|
|
%% TODO There is a major missing feature in this FLU implementation:
|
2015-04-08 05:24:07 +00:00
|
|
|
%% there is no "write-once" enforcement for any position in a Machi
|
|
|
|
%% file. At the moment, we rely on correct behavior of the client
|
|
|
|
%% & the sequencer to avoid overwriting data. In the Real World,
|
|
|
|
%% however, all Machi file data is supposed to be exactly write-once
|
|
|
|
%% to avoid problems with bugs, wire protocol corruption, malicious
|
|
|
|
%% clients, etc.
|
2015-04-30 06:15:43 +00:00
|
|
|
%%
|
|
|
|
%% TODO The per-file metadata tuple store is missing from this implementation.
|
|
|
|
%%
|
|
|
|
%% TODO Section 4.1 ("The FLU") of the Machi design doc suggests that
|
|
|
|
%% the FLU keep track of the epoch number of the last file write (and
|
|
|
|
%% perhaps last metadata write), as an optimization for inter-FLU data
|
|
|
|
%% replication/chain repair.
|
|
|
|
%%
|
|
|
|
%% TODO Section 4.2 ("The Sequencer") says that the sequencer must
|
|
|
|
%% change its file assignments to new & unique names whenever we move
|
|
|
|
%% to wedge state. This is not yet implemented. In the current
|
|
|
|
%% Erlang process scheme (which will probably be changing soon), a
|
|
|
|
%% simple implementation would stop all existing processes that are
|
|
|
|
%% running run_seq_append_server().
|
2015-04-08 05:24:07 +00:00
|
|
|
|
2015-03-31 07:46:03 +00:00
|
|
|
-module(machi_flu1).
|
|
|
|
|
|
|
|
-include_lib("kernel/include/file.hrl").
|
|
|
|
|
|
|
|
-include("machi.hrl").
|
2015-04-03 08:10:52 +00:00
|
|
|
-include("machi_projection.hrl").
|
2015-03-31 07:46:03 +00:00
|
|
|
|
2015-05-17 15:59:24 +00:00
|
|
|
-define(SERVER_CMD_READ_TIMEOUT, 600*1000).
|
|
|
|
|
2015-05-08 09:17:41 +00:00
|
|
|
-export([start_link/1, stop/1,
|
|
|
|
update_wedge_state/3]).
|
2015-05-02 07:59:28 +00:00
|
|
|
-export([make_listener_regname/1, make_projection_server_regname/1]).
|
2015-03-31 07:46:03 +00:00
|
|
|
|
2015-04-02 08:16:15 +00:00
|
|
|
-record(state, {
|
2015-04-30 08:28:43 +00:00
|
|
|
flu_name :: atom(),
|
2015-04-03 08:10:52 +00:00
|
|
|
proj_store :: pid(),
|
|
|
|
append_pid :: pid(),
|
2015-04-02 08:16:15 +00:00
|
|
|
tcp_port :: non_neg_integer(),
|
|
|
|
data_dir :: string(),
|
2015-05-08 09:17:41 +00:00
|
|
|
wedged = true :: boolean(),
|
2015-05-08 10:50:47 +00:00
|
|
|
etstab :: ets:tid(),
|
2015-05-08 10:07:57 +00:00
|
|
|
epoch_id :: 'undefined' | pv1_epoch(),
|
2015-04-02 08:16:15 +00:00
|
|
|
dbg_props = [] :: list(), % proplist
|
|
|
|
props = [] :: list() % proplist
|
|
|
|
}).
|
|
|
|
|
|
|
|
start_link([{FluName, TcpPort, DataDir}|Rest])
|
2015-03-31 07:46:03 +00:00
|
|
|
when is_atom(FluName), is_integer(TcpPort), is_list(DataDir) ->
|
2015-04-02 08:16:15 +00:00
|
|
|
{ok, spawn_link(fun() -> main2(FluName, TcpPort, DataDir, Rest) end)}.
|
2015-03-31 07:46:03 +00:00
|
|
|
|
|
|
|
stop(Pid) ->
|
|
|
|
case erlang:is_process_alive(Pid) of
|
|
|
|
true ->
|
2015-04-06 07:49:17 +00:00
|
|
|
Pid ! killme,
|
2015-03-31 07:46:03 +00:00
|
|
|
ok;
|
|
|
|
false ->
|
|
|
|
error
|
|
|
|
end.
|
|
|
|
|
2015-05-08 09:17:41 +00:00
|
|
|
update_wedge_state(PidSpec, Boolean, EpochId)
|
|
|
|
when (Boolean == true orelse Boolean == false), is_tuple(EpochId) ->
|
|
|
|
PidSpec ! {wedge_state_change, Boolean, EpochId}.
|
|
|
|
|
2015-03-31 07:46:03 +00:00
|
|
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
|
|
|
|
2015-05-08 10:50:47 +00:00
|
|
|
ets_table_name(FluName) when is_atom(FluName) ->
|
2015-05-08 12:37:19 +00:00
|
|
|
list_to_atom(atom_to_list(FluName) ++ "_epoch").
|
|
|
|
%% ets_table_name(FluName) when is_binary(FluName) ->
|
|
|
|
%% list_to_atom(binary_to_list(FluName) ++ "_epoch").
|
2015-05-08 10:50:47 +00:00
|
|
|
|
2015-04-30 08:28:43 +00:00
|
|
|
main2(FluName, TcpPort, DataDir, Rest) ->
|
2015-05-08 12:24:07 +00:00
|
|
|
{Props, DbgProps} = case proplists:get_value(dbg, Rest) of
|
|
|
|
undefined ->
|
|
|
|
{Rest, []};
|
|
|
|
DPs ->
|
|
|
|
{lists:keydelete(dbg, 1, Rest), DPs}
|
|
|
|
end,
|
2015-05-13 08:58:54 +00:00
|
|
|
{SendAppendPidToProj_p, ProjectionPid} =
|
|
|
|
case proplists:get_value(projection_store_registered_name, Rest) of
|
|
|
|
undefined ->
|
|
|
|
RN = make_projection_server_regname(FluName),
|
|
|
|
{ok, PP} =
|
|
|
|
machi_projection_store:start_link(RN, DataDir, undefined),
|
|
|
|
{true, PP};
|
|
|
|
RN ->
|
|
|
|
{false, whereis(RN)}
|
|
|
|
end,
|
|
|
|
InitialWedged_p = proplists:get_value(initial_wedged, DbgProps),
|
|
|
|
ProjRes = machi_projection_store:read_latest_projection(ProjectionPid,
|
|
|
|
private),
|
|
|
|
{Wedged_p, EpochId} =
|
|
|
|
if InitialWedged_p == undefined,
|
|
|
|
is_tuple(ProjRes), element(1, ProjRes) == ok ->
|
|
|
|
{ok, Proj} = ProjRes,
|
|
|
|
{false, {Proj#projection_v1.epoch_number,
|
|
|
|
Proj#projection_v1.epoch_csum}};
|
|
|
|
InitialWedged_p == false ->
|
|
|
|
{false, ?DUMMY_PV1_EPOCH};
|
|
|
|
true ->
|
|
|
|
{true, undefined}
|
|
|
|
end,
|
2015-04-30 08:28:43 +00:00
|
|
|
S0 = #state{flu_name=FluName,
|
2015-05-13 08:58:54 +00:00
|
|
|
proj_store=ProjectionPid,
|
2015-04-02 08:16:15 +00:00
|
|
|
tcp_port=TcpPort,
|
|
|
|
data_dir=DataDir,
|
2015-05-13 08:58:54 +00:00
|
|
|
wedged=Wedged_p,
|
2015-05-08 10:50:47 +00:00
|
|
|
etstab=ets_table_name(FluName),
|
2015-05-13 08:58:54 +00:00
|
|
|
epoch_id=EpochId,
|
2015-05-08 12:24:07 +00:00
|
|
|
dbg_props=DbgProps,
|
|
|
|
props=Props},
|
2015-05-08 10:50:47 +00:00
|
|
|
AppendPid = start_append_server(S0, self()),
|
|
|
|
receive
|
|
|
|
append_server_ack -> ok
|
|
|
|
end,
|
2015-05-13 08:58:54 +00:00
|
|
|
if SendAppendPidToProj_p ->
|
|
|
|
machi_projection_store:set_wedge_notify_pid(ProjectionPid,
|
|
|
|
AppendPid);
|
|
|
|
true ->
|
|
|
|
ok
|
|
|
|
end,
|
|
|
|
S1 = S0#state{append_pid=AppendPid},
|
2015-05-08 12:24:07 +00:00
|
|
|
ListenPid = start_listen_server(S1),
|
2015-04-03 08:10:52 +00:00
|
|
|
|
|
|
|
Config_e = machi_util:make_config_filename(DataDir, "unused"),
|
|
|
|
ok = filelib:ensure_dir(Config_e),
|
|
|
|
{_, Data_e} = machi_util:make_data_filename(DataDir, "unused"),
|
|
|
|
ok = filelib:ensure_dir(Data_e),
|
|
|
|
Projection_e = machi_util:make_projection_filename(DataDir, "unused"),
|
|
|
|
ok = filelib:ensure_dir(Projection_e),
|
|
|
|
|
2015-04-30 08:28:43 +00:00
|
|
|
put(flu_flu_name, FluName),
|
2015-04-02 08:42:26 +00:00
|
|
|
put(flu_append_pid, AppendPid),
|
2015-04-03 08:10:52 +00:00
|
|
|
put(flu_projection_pid, ProjectionPid),
|
2015-04-02 08:42:26 +00:00
|
|
|
put(flu_listen_pid, ListenPid),
|
2015-04-06 07:49:17 +00:00
|
|
|
receive killme -> ok end,
|
|
|
|
(catch exit(AppendPid, kill)),
|
|
|
|
(catch exit(ProjectionPid, kill)),
|
|
|
|
(catch exit(ListenPid, kill)),
|
|
|
|
ok.
|
2015-03-31 07:46:03 +00:00
|
|
|
|
2015-04-02 08:16:15 +00:00
|
|
|
start_listen_server(S) ->
|
2015-04-30 08:28:43 +00:00
|
|
|
proc_lib:spawn_link(fun() -> run_listen_server(S) end).
|
2015-03-31 07:46:03 +00:00
|
|
|
|
2015-05-08 10:50:47 +00:00
|
|
|
start_append_server(S, AckPid) ->
|
2015-04-30 08:28:43 +00:00
|
|
|
FluPid = self(),
|
2015-05-08 10:50:47 +00:00
|
|
|
proc_lib:spawn_link(fun() -> run_append_server(FluPid, AckPid, S) end).
|
2015-03-31 07:46:03 +00:00
|
|
|
|
2015-04-03 08:10:52 +00:00
|
|
|
%% start_projection_server(S) ->
|
|
|
|
%% spawn_link(fun() -> run_projection_server(S) end).
|
|
|
|
|
2015-04-30 08:28:43 +00:00
|
|
|
run_listen_server(#state{flu_name=FluName, tcp_port=TcpPort}=S) ->
|
|
|
|
register(make_listener_regname(FluName), self()),
|
2015-03-31 07:46:03 +00:00
|
|
|
SockOpts = [{reuseaddr, true},
|
|
|
|
{mode, binary}, {active, false}, {packet, line}],
|
2015-05-19 06:15:05 +00:00
|
|
|
case gen_tcp:listen(TcpPort, SockOpts) of
|
|
|
|
{ok, LSock} ->
|
|
|
|
listen_server_loop(LSock, S);
|
|
|
|
Else ->
|
|
|
|
error_logger:warning_msg("~s:run_listen_server: "
|
|
|
|
"listen to TCP port ~w: ~w\n",
|
|
|
|
[?MODULE, TcpPort, Else]),
|
|
|
|
exit({?MODULE, run_listen_server, tcp_port, TcpPort, Else})
|
|
|
|
end.
|
2015-03-31 07:46:03 +00:00
|
|
|
|
2015-05-13 09:57:38 +00:00
|
|
|
run_append_server(FluPid, AckPid, #state{flu_name=Name,
|
2015-05-13 08:58:54 +00:00
|
|
|
wedged=Wedged_p,epoch_id=EpochId}=S) ->
|
2015-05-08 09:17:41 +00:00
|
|
|
%% Reminder: Name is the "main" name of the FLU, i.e., no suffix
|
2015-03-31 07:46:03 +00:00
|
|
|
register(Name, self()),
|
2015-05-08 10:50:47 +00:00
|
|
|
TID = ets:new(ets_table_name(Name),
|
|
|
|
[set, protected, named_table, {read_concurrency, true}]),
|
2015-05-13 08:58:54 +00:00
|
|
|
%% InitialWedged = proplists:get_value(initial_wedged, DbgProps, true),
|
|
|
|
%% ets:insert(TID, {epoch, {InitialWedged, {-65, <<"bogus epoch, yo">>}}}),
|
|
|
|
ets:insert(TID, {epoch, {Wedged_p, EpochId}}),
|
2015-05-08 10:50:47 +00:00
|
|
|
AckPid ! append_server_ack,
|
|
|
|
append_server_loop(FluPid, S#state{etstab=TID}).
|
2015-03-31 07:46:03 +00:00
|
|
|
|
2015-04-02 08:16:15 +00:00
|
|
|
listen_server_loop(LSock, S) ->
|
2015-03-31 07:46:03 +00:00
|
|
|
{ok, Sock} = gen_tcp:accept(LSock),
|
2015-04-02 08:42:26 +00:00
|
|
|
spawn_link(fun() -> net_server_loop(Sock, S) end),
|
2015-04-02 08:16:15 +00:00
|
|
|
listen_server_loop(LSock, S).
|
2015-03-31 07:46:03 +00:00
|
|
|
|
2015-05-08 10:07:57 +00:00
|
|
|
append_server_loop(FluPid, #state{data_dir=DataDir,wedged=Wedged_p}=S) ->
|
2015-04-30 08:28:43 +00:00
|
|
|
AppendServerPid = self(),
|
2015-03-31 07:46:03 +00:00
|
|
|
receive
|
2015-05-17 05:10:42 +00:00
|
|
|
{seq_append, From, _Prefix, _Chunk, _CSum, _Extra} when Wedged_p ->
|
2015-05-08 10:07:57 +00:00
|
|
|
From ! wedged,
|
|
|
|
append_server_loop(FluPid, S);
|
2015-05-17 05:10:42 +00:00
|
|
|
{seq_append, From, Prefix, Chunk, CSum, Extra} ->
|
|
|
|
spawn(fun() -> append_server_dispatch(From, Prefix,
|
|
|
|
Chunk, CSum, Extra,
|
|
|
|
DataDir, AppendServerPid) end),
|
2015-04-30 08:28:43 +00:00
|
|
|
append_server_loop(FluPid, S);
|
2015-05-08 09:17:41 +00:00
|
|
|
{wedge_state_change, Boolean, EpochId} ->
|
2015-05-08 10:50:47 +00:00
|
|
|
true = ets:insert(S#state.etstab, {epoch, {Boolean, EpochId}}),
|
2015-05-08 09:17:41 +00:00
|
|
|
append_server_loop(FluPid, S#state{wedged=Boolean,
|
|
|
|
epoch_id=EpochId});
|
2015-05-08 07:53:10 +00:00
|
|
|
{wedge_status, FromPid} ->
|
2015-05-08 09:17:41 +00:00
|
|
|
#state{wedged=Wedged_p, epoch_id=EpochId} = S,
|
|
|
|
FromPid ! {wedge_status_reply, Wedged_p, EpochId},
|
2015-05-08 07:53:10 +00:00
|
|
|
append_server_loop(FluPid, S);
|
|
|
|
Else ->
|
|
|
|
io:format(user, "append_server_loop: WHA? ~p\n", [Else]),
|
|
|
|
append_server_loop(FluPid, S)
|
2015-03-31 07:46:03 +00:00
|
|
|
end.
|
|
|
|
|
2015-05-13 09:57:38 +00:00
|
|
|
-define(EpochIDSpace, ((4*2)+(20*2))). % hexencodingwhee!
|
|
|
|
|
|
|
|
decode_epoch_id(EpochIDHex) ->
|
|
|
|
<<EpochNum:(4*8)/big, EpochCSum/binary>> =
|
|
|
|
machi_util:hexstr_to_bin(EpochIDHex),
|
|
|
|
{EpochNum, EpochCSum}.
|
2015-04-02 09:08:42 +00:00
|
|
|
|
2015-04-30 08:28:43 +00:00
|
|
|
net_server_loop(Sock, #state{flu_name=FluName, data_dir=DataDir}=S) ->
|
2015-03-31 07:46:03 +00:00
|
|
|
ok = inet:setopts(Sock, [{packet, line}]),
|
2015-05-17 15:59:24 +00:00
|
|
|
%% TODO: Add testing control knob to adjust this timeout and/or inject
|
|
|
|
%% timeout condition.
|
|
|
|
case gen_tcp:recv(Sock, 0, ?SERVER_CMD_READ_TIMEOUT) of
|
2015-03-31 07:46:03 +00:00
|
|
|
{ok, Line} ->
|
|
|
|
%% machi_util:verb("Got: ~p\n", [Line]),
|
2015-05-17 05:10:42 +00:00
|
|
|
PrefixLenLF = byte_size(Line) - 2 - ?EpochIDSpace - 8 -8 - 1,
|
2015-04-02 11:49:45 +00:00
|
|
|
FileLenLF = byte_size(Line) - 2 - ?EpochIDSpace - 16 - 8 - 1,
|
|
|
|
CSumFileLenLF = byte_size(Line) - 2 - ?EpochIDSpace - 1,
|
2015-04-02 12:18:41 +00:00
|
|
|
WriteFileLenLF = byte_size(Line) - 7 - ?EpochIDSpace - 16 - 8 - 1,
|
|
|
|
DelFileLenLF = byte_size(Line) - 14 - ?EpochIDSpace - 1,
|
2015-03-31 07:46:03 +00:00
|
|
|
case Line of
|
|
|
|
%% For normal use
|
2015-04-02 09:08:42 +00:00
|
|
|
<<"A ",
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochIDHex:(?EpochIDSpace)/binary,
|
2015-05-17 05:10:42 +00:00
|
|
|
LenHex:8/binary, ExtraHex:8/binary,
|
2015-04-02 11:49:45 +00:00
|
|
|
Prefix:PrefixLenLF/binary, "\n">> ->
|
2015-05-13 09:57:38 +00:00
|
|
|
_EpochID = decode_epoch_id(EpochIDHex),
|
2015-05-17 05:10:42 +00:00
|
|
|
do_net_server_append(FluName, Sock, LenHex, ExtraHex,
|
|
|
|
Prefix);
|
2015-04-02 11:31:10 +00:00
|
|
|
<<"R ",
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochIDHex:(?EpochIDSpace)/binary,
|
2015-04-02 11:31:10 +00:00
|
|
|
OffsetHex:16/binary, LenHex:8/binary,
|
2015-04-02 11:49:45 +00:00
|
|
|
File:FileLenLF/binary, "\n">> ->
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochID = decode_epoch_id(EpochIDHex),
|
2015-05-08 10:50:47 +00:00
|
|
|
do_net_server_read(Sock, OffsetHex, LenHex, File, DataDir,
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochID, S);
|
|
|
|
<<"L ", EpochIDHex:(?EpochIDSpace)/binary, "\n">> ->
|
|
|
|
_EpochID = decode_epoch_id(EpochIDHex),
|
2015-05-08 12:37:19 +00:00
|
|
|
do_net_server_listing(Sock, DataDir, S);
|
2015-04-02 11:49:45 +00:00
|
|
|
<<"C ",
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochIDHex:(?EpochIDSpace)/binary,
|
2015-04-02 11:49:45 +00:00
|
|
|
File:CSumFileLenLF/binary, "\n">> ->
|
2015-05-13 09:57:38 +00:00
|
|
|
_EpochID = decode_epoch_id(EpochIDHex),
|
2015-05-08 12:37:19 +00:00
|
|
|
do_net_server_checksum_listing(Sock, File, DataDir, S);
|
2015-03-31 07:46:03 +00:00
|
|
|
<<"QUIT\n">> ->
|
|
|
|
catch gen_tcp:close(Sock),
|
|
|
|
exit(normal);
|
|
|
|
<<"QUIT\r\n">> ->
|
|
|
|
catch gen_tcp:close(Sock),
|
|
|
|
exit(normal);
|
|
|
|
%% For "internal" replication only.
|
2015-04-02 12:18:41 +00:00
|
|
|
<<"W-repl ",
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochIDHex:(?EpochIDSpace)/binary,
|
2015-04-02 12:18:41 +00:00
|
|
|
OffsetHex:16/binary, LenHex:8/binary,
|
2015-03-31 07:46:03 +00:00
|
|
|
File:WriteFileLenLF/binary, "\n">> ->
|
2015-05-13 09:57:38 +00:00
|
|
|
_EpochID = decode_epoch_id(EpochIDHex),
|
2015-05-08 10:50:47 +00:00
|
|
|
do_net_server_write(Sock, OffsetHex, LenHex, File, DataDir,
|
|
|
|
<<"fixme1">>, false, <<"fixme2">>);
|
2015-03-31 07:46:03 +00:00
|
|
|
%% For data migration only.
|
2015-04-02 12:18:41 +00:00
|
|
|
<<"DEL-migration ",
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochIDHex:(?EpochIDSpace)/binary,
|
2015-04-02 12:18:41 +00:00
|
|
|
File:DelFileLenLF/binary, "\n">> ->
|
2015-05-13 09:57:38 +00:00
|
|
|
_EpochID = decode_epoch_id(EpochIDHex),
|
2015-03-31 07:46:03 +00:00
|
|
|
do_net_server_delete_migration_only(Sock, File, DataDir);
|
|
|
|
%% For erasure coding hackityhack
|
2015-04-02 12:18:41 +00:00
|
|
|
<<"TRUNC-hack--- ",
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochIDHex:(?EpochIDSpace)/binary,
|
2015-04-02 12:18:41 +00:00
|
|
|
File:DelFileLenLF/binary, "\n">> ->
|
2015-05-13 09:57:38 +00:00
|
|
|
_EpochID = decode_epoch_id(EpochIDHex),
|
2015-03-31 07:46:03 +00:00
|
|
|
do_net_server_truncate_hackityhack(Sock, File, DataDir);
|
2015-04-03 08:10:52 +00:00
|
|
|
<<"PROJ ", LenHex:8/binary, "\n">> ->
|
|
|
|
do_projection_command(Sock, LenHex, S);
|
2015-05-08 07:53:10 +00:00
|
|
|
<<"WEDGE-STATUS\n">> ->
|
|
|
|
do_wedge_status(FluName, Sock);
|
2015-03-31 07:46:03 +00:00
|
|
|
_ ->
|
|
|
|
machi_util:verb("Else Got: ~p\n", [Line]),
|
2015-05-13 09:57:38 +00:00
|
|
|
io:format(user, "TODO: Else Got: ~p\n", [Line]),
|
2015-03-31 07:46:03 +00:00
|
|
|
gen_tcp:send(Sock, "ERROR SYNTAX\n"),
|
|
|
|
catch gen_tcp:close(Sock),
|
|
|
|
exit(normal)
|
|
|
|
end,
|
2015-04-02 08:16:15 +00:00
|
|
|
net_server_loop(Sock, S);
|
2015-03-31 07:46:03 +00:00
|
|
|
_ ->
|
|
|
|
catch gen_tcp:close(Sock),
|
|
|
|
exit(normal)
|
|
|
|
end.
|
|
|
|
|
2015-05-17 05:10:42 +00:00
|
|
|
append_server_dispatch(From, Prefix, Chunk, CSum, Extra, DataDir, LinkPid) ->
|
2015-04-30 08:28:43 +00:00
|
|
|
Pid = write_server_get_pid(Prefix, DataDir, LinkPid),
|
2015-05-17 05:10:42 +00:00
|
|
|
Pid ! {seq_append, From, Prefix, Chunk, CSum, Extra},
|
2015-03-31 07:46:03 +00:00
|
|
|
exit(normal).
|
|
|
|
|
2015-05-17 05:10:42 +00:00
|
|
|
do_net_server_append(FluName, Sock, LenHex, ExtraHex, Prefix) ->
|
2015-03-31 07:46:03 +00:00
|
|
|
%% TODO: robustify against other invalid path characters such as NUL
|
|
|
|
case sanitize_file_string(Prefix) of
|
|
|
|
ok ->
|
2015-05-17 05:10:42 +00:00
|
|
|
do_net_server_append2(FluName, Sock, LenHex, ExtraHex, Prefix);
|
2015-03-31 07:46:03 +00:00
|
|
|
_ ->
|
|
|
|
ok = gen_tcp:send(Sock, <<"ERROR BAD-ARG">>)
|
|
|
|
end.
|
|
|
|
|
|
|
|
sanitize_file_string(Str) ->
|
|
|
|
case re:run(Str, "/") of
|
|
|
|
nomatch ->
|
|
|
|
ok;
|
|
|
|
_ ->
|
|
|
|
error
|
|
|
|
end.
|
|
|
|
|
2015-05-17 05:10:42 +00:00
|
|
|
do_net_server_append2(FluName, Sock, LenHex, ExtraHex, Prefix) ->
|
2015-03-31 07:46:03 +00:00
|
|
|
<<Len:32/big>> = machi_util:hexstr_to_bin(LenHex),
|
2015-05-17 05:10:42 +00:00
|
|
|
<<Extra:32/big>> = machi_util:hexstr_to_bin(ExtraHex),
|
2015-03-31 07:46:03 +00:00
|
|
|
ok = inet:setopts(Sock, [{packet, raw}]),
|
|
|
|
{ok, Chunk} = gen_tcp:recv(Sock, Len, 60*1000),
|
2015-04-08 05:24:07 +00:00
|
|
|
CSum = machi_util:checksum_chunk(Chunk),
|
2015-03-31 07:46:03 +00:00
|
|
|
try
|
2015-05-17 05:10:42 +00:00
|
|
|
FluName ! {seq_append, self(), Prefix, Chunk, CSum, Extra}
|
2015-03-31 07:46:03 +00:00
|
|
|
catch error:badarg ->
|
|
|
|
error_logger:error_msg("Message send to ~p gave badarg, make certain server is running with correct registered name\n", [?MODULE])
|
|
|
|
end,
|
|
|
|
receive
|
|
|
|
{assignment, Offset, File} ->
|
|
|
|
OffsetHex = machi_util:bin_to_hexstr(<<Offset:64/big>>),
|
|
|
|
Out = io_lib:format("OK ~s ~s\n", [OffsetHex, File]),
|
2015-05-08 10:07:57 +00:00
|
|
|
ok = gen_tcp:send(Sock, Out);
|
|
|
|
wedged ->
|
|
|
|
ok = gen_tcp:send(Sock, <<"ERROR WEDGED\n">>)
|
2015-03-31 07:46:03 +00:00
|
|
|
after 10*1000 ->
|
|
|
|
ok = gen_tcp:send(Sock, "TIMEOUT\n")
|
|
|
|
end.
|
|
|
|
|
2015-05-08 07:53:10 +00:00
|
|
|
do_wedge_status(FluName, Sock) ->
|
|
|
|
FluName ! {wedge_status, self()},
|
|
|
|
Reply = receive
|
|
|
|
{wedge_status_reply, Bool, EpochId} ->
|
|
|
|
BoolHex = if Bool == false -> <<"00">>;
|
|
|
|
Bool == true -> <<"01">>
|
|
|
|
end,
|
|
|
|
case EpochId of
|
|
|
|
undefined ->
|
|
|
|
EpochHex = machi_util:int_to_hexstr(0, 32),
|
|
|
|
CSumHex = machi_util:bin_to_hexstr(<<0:(20*8)/big>>);
|
|
|
|
{Epoch, EpochCSum} ->
|
|
|
|
EpochHex = machi_util:int_to_hexstr(Epoch, 32),
|
|
|
|
CSumHex = machi_util:bin_to_hexstr(EpochCSum)
|
|
|
|
end,
|
|
|
|
[<<"OK ">>, BoolHex, 32, EpochHex, 32, CSumHex, 10]
|
|
|
|
after 30*1000 ->
|
|
|
|
<<"give_it_up\n">>
|
|
|
|
end,
|
|
|
|
ok = gen_tcp:send(Sock, Reply).
|
|
|
|
|
2015-05-08 10:50:47 +00:00
|
|
|
do_net_server_read(Sock, OffsetHex, LenHex, FileBin, DataDir,
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochID, S) ->
|
2015-05-08 12:37:19 +00:00
|
|
|
{Wedged_p, CurrentEpochId} = ets:lookup_element(S#state.etstab, epoch, 2),
|
2015-03-31 07:46:03 +00:00
|
|
|
DoItFun = fun(FH, Offset, Len) ->
|
|
|
|
case file:pread(FH, Offset, Len) of
|
|
|
|
{ok, Bytes} when byte_size(Bytes) == Len ->
|
|
|
|
gen_tcp:send(Sock, ["OK\n", Bytes]);
|
|
|
|
{ok, Bytes} ->
|
|
|
|
machi_util:verb("ok read but wanted ~p got ~p: ~p @ offset ~p\n",
|
|
|
|
[Len, size(Bytes), FileBin, Offset]),
|
|
|
|
ok = gen_tcp:send(Sock, "ERROR PARTIAL-READ\n");
|
|
|
|
eof ->
|
|
|
|
perhaps_do_net_server_ec_read(Sock, FH);
|
|
|
|
_Else2 ->
|
|
|
|
machi_util:verb("Else2 ~p ~p ~P\n",
|
|
|
|
[Offset, Len, _Else2, 20]),
|
|
|
|
ok = gen_tcp:send(Sock, "ERROR BAD-READ\n")
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
do_net_server_readwrite_common(Sock, OffsetHex, LenHex, FileBin, DataDir,
|
2015-05-08 10:50:47 +00:00
|
|
|
[read, binary, raw], DoItFun,
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochID, Wedged_p, CurrentEpochId).
|
2015-03-31 07:46:03 +00:00
|
|
|
|
|
|
|
do_net_server_readwrite_common(Sock, OffsetHex, LenHex, FileBin, DataDir,
|
2015-05-08 10:50:47 +00:00
|
|
|
FileOpts, DoItFun,
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochID, Wedged_p, CurrentEpochId) ->
|
2015-05-08 10:50:47 +00:00
|
|
|
case {Wedged_p, sanitize_file_string(FileBin)} of
|
|
|
|
{false, ok} ->
|
2015-03-31 07:46:03 +00:00
|
|
|
do_net_server_readwrite_common2(Sock, OffsetHex, LenHex, FileBin,
|
2015-05-08 10:50:47 +00:00
|
|
|
DataDir, FileOpts, DoItFun,
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochID, Wedged_p, CurrentEpochId);
|
2015-05-08 10:50:47 +00:00
|
|
|
{true, _} ->
|
|
|
|
ok = gen_tcp:send(Sock, <<"ERROR WEDGED\n">>);
|
|
|
|
{_, __} ->
|
2015-03-31 07:46:03 +00:00
|
|
|
ok = gen_tcp:send(Sock, <<"ERROR BAD-ARG\n">>)
|
|
|
|
end.
|
|
|
|
|
|
|
|
do_net_server_readwrite_common2(Sock, OffsetHex, LenHex, FileBin, DataDir,
|
2015-05-08 10:50:47 +00:00
|
|
|
FileOpts, DoItFun,
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochID, Wedged_p, CurrentEpochId) ->
|
2015-03-31 07:46:03 +00:00
|
|
|
<<Offset:64/big>> = machi_util:hexstr_to_bin(OffsetHex),
|
|
|
|
<<Len:32/big>> = machi_util:hexstr_to_bin(LenHex),
|
|
|
|
{_, Path} = machi_util:make_data_filename(DataDir, FileBin),
|
|
|
|
OptsHasWrite = lists:member(write, FileOpts),
|
2015-05-18 14:26:21 +00:00
|
|
|
OptsHasRead = lists:member(read, FileOpts),
|
2015-03-31 07:46:03 +00:00
|
|
|
case file:open(Path, FileOpts) of
|
|
|
|
{ok, FH} ->
|
|
|
|
try
|
|
|
|
DoItFun(FH, Offset, Len)
|
|
|
|
after
|
|
|
|
file:close(FH)
|
|
|
|
end;
|
|
|
|
{error, enoent} when OptsHasWrite ->
|
|
|
|
do_net_server_readwrite_common(
|
|
|
|
Sock, OffsetHex, LenHex, FileBin, DataDir,
|
2015-05-08 10:50:47 +00:00
|
|
|
FileOpts, DoItFun,
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochID, Wedged_p, CurrentEpochId);
|
2015-05-18 14:26:21 +00:00
|
|
|
{error, enoent} when OptsHasRead ->
|
|
|
|
ok = gen_tcp:send(Sock, <<"ERROR NO-SUCH-FILE\n">>);
|
2015-03-31 07:46:03 +00:00
|
|
|
_Else ->
|
|
|
|
ok = gen_tcp:send(Sock, <<"ERROR BAD-IO\n">>)
|
|
|
|
end.
|
|
|
|
|
|
|
|
|
2015-05-08 10:50:47 +00:00
|
|
|
do_net_server_write(Sock, OffsetHex, LenHex, FileBin, DataDir,
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochID, Wedged_p, CurrentEpochId) ->
|
2015-03-31 07:46:03 +00:00
|
|
|
CSumPath = machi_util:make_checksum_filename(DataDir, FileBin),
|
|
|
|
case file:open(CSumPath, [append, raw, binary, delayed_write]) of
|
|
|
|
{ok, FHc} ->
|
2015-05-08 10:50:47 +00:00
|
|
|
do_net_server_write2(Sock, OffsetHex, LenHex, FileBin, DataDir, FHc,
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochID, Wedged_p, CurrentEpochId);
|
2015-03-31 07:46:03 +00:00
|
|
|
{error, enoent} ->
|
|
|
|
ok = filelib:ensure_dir(CSumPath),
|
2015-05-08 10:50:47 +00:00
|
|
|
do_net_server_write(Sock, OffsetHex, LenHex, FileBin, DataDir,
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochID, Wedged_p, CurrentEpochId)
|
2015-03-31 07:46:03 +00:00
|
|
|
end.
|
|
|
|
|
2015-05-08 10:50:47 +00:00
|
|
|
do_net_server_write2(Sock, OffsetHex, LenHex, FileBin, DataDir, FHc,
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochID, Wedged_p, CurrentEpochId) ->
|
2015-03-31 07:46:03 +00:00
|
|
|
DoItFun = fun(FHd, Offset, Len) ->
|
|
|
|
ok = inet:setopts(Sock, [{packet, raw}]),
|
|
|
|
{ok, Chunk} = gen_tcp:recv(Sock, Len),
|
2015-04-08 05:24:07 +00:00
|
|
|
CSum = machi_util:checksum_chunk(Chunk),
|
2015-03-31 07:46:03 +00:00
|
|
|
case file:pwrite(FHd, Offset, Chunk) of
|
|
|
|
ok ->
|
|
|
|
CSumHex = machi_util:bin_to_hexstr(CSum),
|
|
|
|
CSum_info = [OffsetHex, 32, LenHex, 32, CSumHex, 10],
|
|
|
|
ok = file:write(FHc, CSum_info),
|
|
|
|
ok = file:close(FHc),
|
|
|
|
gen_tcp:send(Sock, <<"OK\n">>);
|
|
|
|
_Else3 ->
|
|
|
|
machi_util:verb("Else3 ~p ~p ~p\n",
|
|
|
|
[Offset, Len, _Else3]),
|
|
|
|
ok = gen_tcp:send(Sock, "ERROR BAD-PWRITE\n")
|
|
|
|
end
|
|
|
|
end,
|
|
|
|
do_net_server_readwrite_common(Sock, OffsetHex, LenHex, FileBin, DataDir,
|
2015-05-08 10:50:47 +00:00
|
|
|
[write, read, binary, raw], DoItFun,
|
2015-05-13 09:57:38 +00:00
|
|
|
EpochID, Wedged_p, CurrentEpochId).
|
2015-03-31 07:46:03 +00:00
|
|
|
|
|
|
|
perhaps_do_net_server_ec_read(Sock, FH) ->
|
|
|
|
case file:pread(FH, 0, ?MINIMUM_OFFSET) of
|
|
|
|
{ok, Bin} when byte_size(Bin) == ?MINIMUM_OFFSET ->
|
|
|
|
decode_and_reply_net_server_ec_read(Sock, Bin);
|
|
|
|
{ok, _AnythingElse} ->
|
|
|
|
ok = gen_tcp:send(Sock, "ERROR PARTIAL-READ2\n");
|
|
|
|
_AnythingElse ->
|
|
|
|
ok = gen_tcp:send(Sock, "ERROR BAD-PREAD\n")
|
|
|
|
end.
|
|
|
|
|
|
|
|
decode_and_reply_net_server_ec_read(Sock, <<"a ", Rest/binary>>) ->
|
|
|
|
decode_and_reply_net_server_ec_read_version_a(Sock, Rest);
|
|
|
|
decode_and_reply_net_server_ec_read(Sock, <<0:8, _/binary>>) ->
|
|
|
|
ok = gen_tcp:send(Sock, <<"ERROR NOT-ERASURE\n">>).
|
|
|
|
|
|
|
|
decode_and_reply_net_server_ec_read_version_a(Sock, Rest) ->
|
|
|
|
%% <<BodyLenHex:4/binary, " ", StripeWidthHex:16/binary, " ",
|
|
|
|
%% OrigFileLenHex:16/binary, " ", _/binary>> = Rest,
|
|
|
|
HdrLen = 80 - 2 - 4 - 1,
|
|
|
|
<<BodyLenHex:4/binary, " ", Hdr:HdrLen/binary, Rest2/binary>> = Rest,
|
|
|
|
<<BodyLen:16/big>> = machi_util:hexstr_to_bin(BodyLenHex),
|
|
|
|
<<Body:BodyLen/binary, _/binary>> = Rest2,
|
|
|
|
ok = gen_tcp:send(Sock, ["ERASURE ", BodyLenHex, " ", Hdr, Body]).
|
|
|
|
|
2015-05-08 12:37:19 +00:00
|
|
|
do_net_server_listing(Sock, DataDir, S) ->
|
|
|
|
{Wedged_p, _CurrentEpochId} = ets:lookup_element(S#state.etstab, epoch, 2),
|
|
|
|
if Wedged_p ->
|
|
|
|
ok = gen_tcp:send(Sock, <<"ERROR WEDGED\n">>);
|
|
|
|
true ->
|
|
|
|
do_net_server_listing2(Sock, DataDir)
|
|
|
|
end.
|
|
|
|
|
|
|
|
do_net_server_listing2(Sock, DataDir) ->
|
2015-04-03 08:10:52 +00:00
|
|
|
{_, WildPath} = machi_util:make_data_filename(DataDir, ""),
|
|
|
|
Files = filelib:wildcard("*", WildPath),
|
2015-03-31 07:46:03 +00:00
|
|
|
Out = ["OK\n",
|
|
|
|
[begin
|
2015-04-03 08:10:52 +00:00
|
|
|
{ok, FI} = file:read_file_info(WildPath ++ "/" ++ File),
|
2015-03-31 07:46:03 +00:00
|
|
|
Size = FI#file_info.size,
|
|
|
|
SizeBin = <<Size:64/big>>,
|
|
|
|
[machi_util:bin_to_hexstr(SizeBin), <<" ">>,
|
|
|
|
list_to_binary(File), <<"\n">>]
|
|
|
|
end || File <- Files],
|
|
|
|
".\n"
|
|
|
|
],
|
|
|
|
ok = gen_tcp:send(Sock, Out).
|
|
|
|
|
2015-05-08 12:37:19 +00:00
|
|
|
do_net_server_checksum_listing(Sock, File, DataDir, S) ->
|
|
|
|
{Wedged_p, _CurrentEpochId} = ets:lookup_element(S#state.etstab, epoch, 2),
|
|
|
|
case {Wedged_p, sanitize_file_string(File)} of
|
|
|
|
{true, _} ->
|
|
|
|
ok = gen_tcp:send(Sock, <<"ERROR WEDGED\n">>);
|
|
|
|
{false, ok} ->
|
2015-03-31 07:46:03 +00:00
|
|
|
do_net_server_checksum_listing2(Sock, File, DataDir);
|
|
|
|
_ ->
|
|
|
|
ok = gen_tcp:send(Sock, <<"ERROR BAD-ARG\n">>)
|
|
|
|
end.
|
|
|
|
|
|
|
|
do_net_server_checksum_listing2(Sock, File, DataDir) ->
|
2015-04-01 08:59:40 +00:00
|
|
|
ok = sync_checksum_file(File),
|
|
|
|
|
2015-03-31 07:46:03 +00:00
|
|
|
CSumPath = machi_util:make_checksum_filename(DataDir, File),
|
|
|
|
case file:open(CSumPath, [read, raw, binary]) of
|
|
|
|
{ok, FH} ->
|
|
|
|
{ok, FI} = file:read_file_info(CSumPath),
|
|
|
|
Len = FI#file_info.size,
|
|
|
|
LenHex = list_to_binary(machi_util:bin_to_hexstr(<<Len:64/big>>)),
|
|
|
|
%% Client has option of line-by-line with "." terminator,
|
|
|
|
%% or using the offset in the OK message to slurp things
|
|
|
|
%% down by exact byte size.
|
|
|
|
ok = gen_tcp:send(Sock, [<<"OK ">>, LenHex, <<"\n">>]),
|
|
|
|
do_net_copy_bytes(FH, Sock),
|
|
|
|
ok = file:close(FH),
|
|
|
|
ok = gen_tcp:send(Sock, ".\n");
|
|
|
|
{error, enoent} ->
|
|
|
|
ok = gen_tcp:send(Sock, "ERROR NO-SUCH-FILE\n");
|
|
|
|
_ ->
|
|
|
|
ok = gen_tcp:send(Sock, "ERROR\n")
|
|
|
|
end.
|
|
|
|
|
2015-04-01 08:59:40 +00:00
|
|
|
sync_checksum_file(File) ->
|
|
|
|
Prefix = re:replace(File, "\\..*", "", [{return, binary}]),
|
|
|
|
case write_server_find_pid(Prefix) of
|
|
|
|
undefined ->
|
|
|
|
ok;
|
|
|
|
Pid ->
|
|
|
|
Ref = make_ref(),
|
|
|
|
Pid ! {sync_stuff, self(), Ref},
|
|
|
|
receive
|
|
|
|
{sync_finished, Ref} ->
|
|
|
|
ok
|
|
|
|
after 5000 ->
|
|
|
|
case write_server_find_pid(Prefix) of
|
|
|
|
undefined ->
|
|
|
|
ok;
|
|
|
|
Pid2 when Pid2 /= Pid ->
|
|
|
|
ok;
|
|
|
|
_Pid2 ->
|
|
|
|
error
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end.
|
|
|
|
|
2015-03-31 07:46:03 +00:00
|
|
|
do_net_copy_bytes(FH, Sock) ->
|
|
|
|
case file:read(FH, 1024*1024) of
|
|
|
|
{ok, Bin} ->
|
|
|
|
ok = gen_tcp:send(Sock, Bin),
|
|
|
|
do_net_copy_bytes(FH, Sock);
|
|
|
|
eof ->
|
|
|
|
ok
|
|
|
|
end.
|
|
|
|
|
|
|
|
do_net_server_delete_migration_only(Sock, File, DataDir) ->
|
|
|
|
case sanitize_file_string(File) of
|
|
|
|
ok ->
|
|
|
|
do_net_server_delete_migration_only2(Sock, File, DataDir);
|
|
|
|
_ ->
|
|
|
|
ok = gen_tcp:send(Sock, <<"ERROR BAD-ARG\n">>)
|
|
|
|
end.
|
|
|
|
|
|
|
|
do_net_server_delete_migration_only2(Sock, File, DataDir) ->
|
|
|
|
{_, Path} = machi_util:make_data_filename(DataDir, File),
|
|
|
|
case file:delete(Path) of
|
|
|
|
ok ->
|
|
|
|
ok = gen_tcp:send(Sock, "OK\n");
|
|
|
|
{error, enoent} ->
|
|
|
|
ok = gen_tcp:send(Sock, "ERROR NO-SUCH-FILE\n");
|
|
|
|
_ ->
|
|
|
|
ok = gen_tcp:send(Sock, "ERROR\n")
|
|
|
|
end.
|
|
|
|
|
|
|
|
do_net_server_truncate_hackityhack(Sock, File, DataDir) ->
|
|
|
|
case sanitize_file_string(File) of
|
|
|
|
ok ->
|
|
|
|
do_net_server_truncate_hackityhack2(Sock, File, DataDir);
|
|
|
|
_ ->
|
|
|
|
ok = gen_tcp:send(Sock, <<"ERROR BAD-ARG\n">>)
|
|
|
|
end.
|
|
|
|
|
|
|
|
do_net_server_truncate_hackityhack2(Sock, File, DataDir) ->
|
|
|
|
{_, Path} = machi_util:make_data_filename(DataDir, File),
|
|
|
|
case file:open(Path, [read, write, binary, raw]) of
|
|
|
|
{ok, FH} ->
|
|
|
|
try
|
|
|
|
{ok, ?MINIMUM_OFFSET} = file:position(FH, ?MINIMUM_OFFSET),
|
|
|
|
ok = file:truncate(FH),
|
|
|
|
ok = gen_tcp:send(Sock, "OK\n")
|
|
|
|
after
|
|
|
|
file:close(FH)
|
|
|
|
end;
|
|
|
|
{error, enoent} ->
|
|
|
|
ok = gen_tcp:send(Sock, "ERROR NO-SUCH-FILE\n");
|
|
|
|
_ ->
|
|
|
|
ok = gen_tcp:send(Sock, "ERROR\n")
|
|
|
|
end.
|
|
|
|
|
2015-04-30 08:28:43 +00:00
|
|
|
write_server_get_pid(Prefix, DataDir, LinkPid) ->
|
2015-04-01 08:59:40 +00:00
|
|
|
case write_server_find_pid(Prefix) of
|
2015-03-31 07:46:03 +00:00
|
|
|
undefined ->
|
2015-04-30 08:28:43 +00:00
|
|
|
start_seq_append_server(Prefix, DataDir, LinkPid),
|
2015-03-31 07:46:03 +00:00
|
|
|
timer:sleep(1),
|
2015-04-30 08:28:43 +00:00
|
|
|
write_server_get_pid(Prefix, DataDir, LinkPid);
|
2015-03-31 07:46:03 +00:00
|
|
|
Pid ->
|
|
|
|
Pid
|
|
|
|
end.
|
|
|
|
|
2015-04-01 08:59:40 +00:00
|
|
|
write_server_find_pid(Prefix) ->
|
2015-04-30 08:28:43 +00:00
|
|
|
FluName = machi_util:make_regname(Prefix),
|
|
|
|
whereis(FluName).
|
|
|
|
|
|
|
|
start_seq_append_server(Prefix, DataDir, AppendServerPid) ->
|
|
|
|
proc_lib:spawn_link(fun() ->
|
|
|
|
%% The following is only necessary to
|
|
|
|
%% make nice process relationships in
|
|
|
|
%% 'appmon' and related tools.
|
|
|
|
put('$ancestors', [AppendServerPid]),
|
|
|
|
put('$initial_call', {x,y,3}),
|
|
|
|
link(AppendServerPid),
|
|
|
|
run_seq_append_server(Prefix, DataDir)
|
|
|
|
end).
|
2015-03-31 07:46:03 +00:00
|
|
|
|
|
|
|
run_seq_append_server(Prefix, DataDir) ->
|
|
|
|
true = register(machi_util:make_regname(Prefix), self()),
|
|
|
|
run_seq_append_server2(Prefix, DataDir).
|
|
|
|
|
|
|
|
run_seq_append_server2(Prefix, DataDir) ->
|
|
|
|
FileNum = machi_util:read_max_filenum(DataDir, Prefix) + 1,
|
|
|
|
case machi_util:increment_max_filenum(DataDir, Prefix) of
|
|
|
|
ok ->
|
|
|
|
machi_util:increment_max_filenum(DataDir, Prefix),
|
|
|
|
machi_util:info_msg("start: ~p server at file ~w\n",
|
|
|
|
[Prefix, FileNum]),
|
|
|
|
seq_append_server_loop(DataDir, Prefix, FileNum);
|
|
|
|
Else ->
|
|
|
|
error_logger:error_msg("start: ~p server at file ~w: ~p\n",
|
|
|
|
[Prefix, FileNum, Else]),
|
|
|
|
exit(Else)
|
|
|
|
|
|
|
|
end.
|
|
|
|
|
2015-04-08 09:39:55 +00:00
|
|
|
-spec seq_name_hack() -> string().
|
|
|
|
seq_name_hack() ->
|
|
|
|
lists:flatten(io_lib:format("~.36B~.36B",
|
|
|
|
[element(3,now()),
|
|
|
|
list_to_integer(os:getpid())])).
|
|
|
|
|
2015-03-31 07:46:03 +00:00
|
|
|
seq_append_server_loop(DataDir, Prefix, FileNum) ->
|
2015-04-08 09:39:55 +00:00
|
|
|
SequencerNameHack = seq_name_hack(),
|
2015-03-31 07:46:03 +00:00
|
|
|
{File, FullPath} = machi_util:make_data_filename(
|
|
|
|
DataDir, Prefix, SequencerNameHack, FileNum),
|
|
|
|
{ok, FHd} = file:open(FullPath,
|
|
|
|
[write, binary, raw]),
|
|
|
|
%% [write, binary, raw, delayed_write]),
|
|
|
|
CSumPath = machi_util:make_checksum_filename(
|
|
|
|
DataDir, Prefix, SequencerNameHack, FileNum),
|
|
|
|
{ok, FHc} = file:open(CSumPath, [append, raw, binary, delayed_write]),
|
|
|
|
seq_append_server_loop(DataDir, Prefix, File, {FHd,FHc}, FileNum,
|
|
|
|
?MINIMUM_OFFSET).
|
|
|
|
|
|
|
|
seq_append_server_loop(DataDir, Prefix, _File, {FHd,FHc}, FileNum, Offset)
|
|
|
|
when Offset > ?MAX_FILE_SIZE ->
|
|
|
|
ok = file:close(FHd),
|
|
|
|
ok = file:close(FHc),
|
|
|
|
machi_util:info_msg("rollover: ~p server at file ~w offset ~w\n",
|
|
|
|
[Prefix, FileNum, Offset]),
|
|
|
|
run_seq_append_server2(Prefix, DataDir);
|
|
|
|
seq_append_server_loop(DataDir, Prefix, File, {FHd,FHc}=FH_, FileNum, Offset) ->
|
|
|
|
receive
|
2015-05-17 05:10:42 +00:00
|
|
|
{seq_append, From, Prefix, Chunk, CSum, Extra} ->
|
|
|
|
if Chunk /= <<>> ->
|
|
|
|
ok = file:pwrite(FHd, Offset, Chunk);
|
|
|
|
true ->
|
|
|
|
ok
|
|
|
|
end,
|
2015-03-31 07:46:03 +00:00
|
|
|
From ! {assignment, Offset, File},
|
|
|
|
Len = byte_size(Chunk),
|
|
|
|
OffsetHex = machi_util:bin_to_hexstr(<<Offset:64/big>>),
|
|
|
|
LenHex = machi_util:bin_to_hexstr(<<Len:32/big>>),
|
|
|
|
CSumHex = machi_util:bin_to_hexstr(CSum),
|
|
|
|
CSum_info = [OffsetHex, 32, LenHex, 32, CSumHex, 10],
|
|
|
|
ok = file:write(FHc, CSum_info),
|
|
|
|
seq_append_server_loop(DataDir, Prefix, File, FH_,
|
2015-05-17 05:10:42 +00:00
|
|
|
FileNum, Offset + Len + Extra);
|
2015-04-01 08:59:40 +00:00
|
|
|
{sync_stuff, FromPid, Ref} ->
|
|
|
|
file:sync(FHc),
|
|
|
|
FromPid ! {sync_finished, Ref},
|
|
|
|
seq_append_server_loop(DataDir, Prefix, File, FH_,
|
|
|
|
FileNum, Offset)
|
2015-03-31 07:46:03 +00:00
|
|
|
after 30*1000 ->
|
|
|
|
ok = file:close(FHd),
|
|
|
|
ok = file:close(FHc),
|
2015-04-30 08:28:43 +00:00
|
|
|
machi_util:info_msg("stop: ~p server ~p at file ~w offset ~w\n",
|
|
|
|
[Prefix, self(), FileNum, Offset]),
|
2015-03-31 07:46:03 +00:00
|
|
|
exit(normal)
|
|
|
|
end.
|
|
|
|
|
2015-04-03 08:10:52 +00:00
|
|
|
do_projection_command(Sock, LenHex, S) ->
|
|
|
|
try
|
|
|
|
Len = machi_util:hexstr_to_int(LenHex),
|
|
|
|
ok = inet:setopts(Sock, [{packet, raw}]),
|
|
|
|
{ok, ProjCmdBin} = gen_tcp:recv(Sock, Len),
|
|
|
|
ok = inet:setopts(Sock, [{packet, line}]),
|
|
|
|
ProjCmd = binary_to_term(ProjCmdBin),
|
2015-04-03 08:55:35 +00:00
|
|
|
put(hack, ProjCmd),
|
|
|
|
Res = handle_projection_command(ProjCmd, S),
|
|
|
|
ResBin = term_to_binary(Res),
|
|
|
|
ResLenHex = machi_util:int_to_hexbin(byte_size(ResBin), 32),
|
|
|
|
ok = gen_tcp:send(Sock, [<<"OK ">>, ResLenHex, <<"\n">>, ResBin])
|
2015-04-03 08:10:52 +00:00
|
|
|
catch
|
|
|
|
What:Why ->
|
2015-04-03 08:55:35 +00:00
|
|
|
io:format(user, "OOPS ~p\n", [get(hack)]),
|
|
|
|
io:format(user, "OOPS ~p ~p ~p\n", [What, Why, erlang:get_stacktrace()]),
|
2015-04-03 08:10:52 +00:00
|
|
|
WHA = list_to_binary(io_lib:format("TODO-YOLO.~w:~w-~w",
|
|
|
|
[What, Why, erlang:get_stacktrace()])),
|
|
|
|
_ = (catch gen_tcp:send(Sock, [<<"ERROR ">>, WHA, <<"\n">>]))
|
|
|
|
end.
|
|
|
|
|
2015-05-18 10:06:06 +00:00
|
|
|
handle_projection_command({get_latest_epochid, ProjType},
|
2015-04-03 08:55:35 +00:00
|
|
|
#state{proj_store=ProjStore}) ->
|
2015-05-18 10:06:06 +00:00
|
|
|
machi_projection_store:get_latest_epochid(ProjStore, ProjType);
|
2015-04-03 09:37:09 +00:00
|
|
|
handle_projection_command({read_latest_projection, ProjType},
|
|
|
|
#state{proj_store=ProjStore}) ->
|
|
|
|
machi_projection_store:read_latest_projection(ProjStore, ProjType);
|
2015-04-03 08:55:35 +00:00
|
|
|
handle_projection_command({read_projection, ProjType, Epoch},
|
|
|
|
#state{proj_store=ProjStore}) ->
|
|
|
|
machi_projection_store:read(ProjStore, ProjType, Epoch);
|
2015-04-03 08:10:52 +00:00
|
|
|
handle_projection_command({write_projection, ProjType, Proj},
|
|
|
|
#state{proj_store=ProjStore}) ->
|
|
|
|
machi_projection_store:write(ProjStore, ProjType, Proj);
|
2015-04-06 09:43:52 +00:00
|
|
|
handle_projection_command({get_all_projections, ProjType},
|
2015-04-03 09:37:09 +00:00
|
|
|
#state{proj_store=ProjStore}) ->
|
2015-04-06 09:43:52 +00:00
|
|
|
machi_projection_store:get_all_projections(ProjStore, ProjType);
|
|
|
|
handle_projection_command({list_all_projections, ProjType},
|
2015-04-03 09:37:09 +00:00
|
|
|
#state{proj_store=ProjStore}) ->
|
2015-04-06 09:43:52 +00:00
|
|
|
machi_projection_store:list_all_projections(ProjStore, ProjType);
|
2015-04-03 08:10:52 +00:00
|
|
|
handle_projection_command(Else, _S) ->
|
|
|
|
{error, unknown_cmd, Else}.
|
|
|
|
|
2015-04-30 08:28:43 +00:00
|
|
|
make_listener_regname(BaseName) ->
|
|
|
|
list_to_atom(atom_to_list(BaseName) ++ "_listener").
|
|
|
|
|
2015-05-02 07:59:28 +00:00
|
|
|
%% This is the name of the projection store that is spawned by the
|
|
|
|
%% *flu*, for use primarily in testing scenarios. In normal use, we
|
|
|
|
%% ought to be using the OTP style of managing processes, via
|
|
|
|
%% supervisors, namely via machi_flu_psup.erl, which uses a
|
|
|
|
%% *different* naming convention for the projection store name that it
|
|
|
|
%% registers.
|
|
|
|
|
2015-04-03 08:10:52 +00:00
|
|
|
make_projection_server_regname(BaseName) ->
|
2015-04-30 08:28:43 +00:00
|
|
|
list_to_atom(atom_to_list(BaseName) ++ "_pstore2").
|