146 lines
6.9 KiB
Erlang
146 lines
6.9 KiB
Erlang
%% -------------------------------------------------------------------
|
|
%%
|
|
%% 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.
|
|
%%
|
|
%% -------------------------------------------------------------------
|
|
|
|
-module(machi_cr_client_test).
|
|
|
|
-include("machi.hrl").
|
|
-include("machi_projection.hrl").
|
|
|
|
-ifdef(TEST).
|
|
-ifndef(PULSE).
|
|
|
|
-include_lib("eunit/include/eunit.hrl").
|
|
|
|
smoke_test_() -> {timeout, 1*60, fun() -> smoke_test2() end}.
|
|
|
|
smoke_test2() ->
|
|
os:cmd("rm -rf ./data.a ./data.b ./data.c"),
|
|
{ok, SupPid} = machi_flu_sup:start_link(),
|
|
error_logger:tty(false),
|
|
try
|
|
Prefix = <<"pre">>,
|
|
Chunk1 = <<"yochunk">>,
|
|
Host = "localhost",
|
|
PortBase = 64444,
|
|
Os = [{ignore_stability_time, true}, {active_mode, false}],
|
|
{ok,_}=machi_flu_psup:start_flu_package(a, PortBase+0, "./data.a", Os),
|
|
{ok,_}=machi_flu_psup:start_flu_package(b, PortBase+1, "./data.b", Os),
|
|
{ok,_}=machi_flu_psup:start_flu_package(c, PortBase+2, "./data.c", Os),
|
|
D = orddict:from_list(
|
|
[{a,{p_srvr,a,machi_flu1_client,"localhost",PortBase+0,[]}},
|
|
{b,{p_srvr,b,machi_flu1_client,"localhost",PortBase+1,[]}},
|
|
{c,{p_srvr,c,machi_flu1_client,"localhost",PortBase+2,[]}}]),
|
|
%% Force the chain to repair & fully assemble as quickly as possible.
|
|
%% 1. Use set_chain_members() on all 3
|
|
%% 2. Force a to run repair in a tight loop
|
|
%% 3. Stop as soon as we see UPI=[a,b,c] and also author=c.
|
|
%% Otherwise, we can have a race with later, minor
|
|
%% projection changes which will change our understanding of
|
|
%% the epoch id. (C is the author with highest weight.)
|
|
%% 4. Wait until all others are using epoch id from #3.
|
|
%%
|
|
%% Damn, this is a pain to make 100% deterministic, bleh.
|
|
ok = machi_chain_manager1:set_chain_members(a_chmgr, D),
|
|
ok = machi_chain_manager1:set_chain_members(b_chmgr, D),
|
|
ok = machi_chain_manager1:set_chain_members(c_chmgr, D),
|
|
TickAll = fun() -> [begin
|
|
Pid ! tick_check_environment,
|
|
timer:sleep(50)
|
|
end || Pid <- [a_chmgr,b_chmgr,c_chmgr] ]
|
|
end,
|
|
_ = lists:foldl(
|
|
fun(_, [{c,[a,b,c]}]=Acc) -> Acc;
|
|
(_, _Acc) ->
|
|
TickAll(), % has some sleep time inside
|
|
Xs = [begin
|
|
{ok, Prj} = machi_projection_store:read_latest_projection(PStore, private),
|
|
{Prj#projection_v1.author_server,
|
|
Prj#projection_v1.upi}
|
|
end || PStore <- [a_pstore,b_pstore,c_pstore] ],
|
|
lists:usort(Xs)
|
|
end, undefined, lists:seq(1,10000)),
|
|
%% Everyone is settled on the same damn epoch id.
|
|
{ok, EpochID} = machi_flu1_client:get_latest_epochid(Host, PortBase+0,
|
|
private),
|
|
{ok, EpochID} = machi_flu1_client:get_latest_epochid(Host, PortBase+1,
|
|
private),
|
|
{ok, EpochID} = machi_flu1_client:get_latest_epochid(Host, PortBase+2,
|
|
private),
|
|
|
|
%% Whew ... ok, now start some damn tests.
|
|
{ok, C1} = machi_cr_client:start_link([P || {_,P}<-orddict:to_list(D)]),
|
|
machi_cr_client:append_chunk(C1, Prefix, Chunk1),
|
|
{ok, {Off1,Size1,File1}} =
|
|
machi_cr_client:append_chunk(C1, Prefix, Chunk1),
|
|
Chunk1_badcs = {<<?CSUM_TAG_CLIENT_GEN:8, 0:(8*20)>>, Chunk1},
|
|
{error, bad_checksum} =
|
|
machi_cr_client:append_chunk(C1, Prefix, Chunk1_badcs),
|
|
{ok, Chunk1} = machi_cr_client:read_chunk(C1, File1, Off1, Size1),
|
|
{ok, PPP} = machi_flu1_client:read_latest_projection(Host, PortBase+0,
|
|
private),
|
|
%% Verify that the client's CR wrote to all of them.
|
|
[{ok, Chunk1} = machi_flu1_client:read_chunk(
|
|
Host, PortBase+X, EpochID, File1, Off1, Size1) ||
|
|
X <- [0,1,2] ],
|
|
|
|
%% Test read repair: Manually write to head, then verify that
|
|
%% read-repair fixes all.
|
|
FooOff1 = Off1 + (1024*1024),
|
|
[{error, not_written} = machi_flu1_client:read_chunk(
|
|
Host, PortBase+X, EpochID,
|
|
File1, FooOff1, Size1) || X <- [0,1,2] ],
|
|
ok = machi_flu1_client:write_chunk(Host, PortBase+0, EpochID,
|
|
File1, FooOff1, Chunk1),
|
|
{ok, Chunk1} = machi_cr_client:read_chunk(C1, File1, FooOff1, Size1),
|
|
[{X,{ok, Chunk1}} = {X,machi_flu1_client:read_chunk(
|
|
Host, PortBase+X, EpochID,
|
|
File1, FooOff1, Size1)} || X <- [0,1,2] ],
|
|
|
|
%% Test read repair: Manually write to middle, then same checking.
|
|
FooOff2 = Off1 + (2*1024*1024),
|
|
Chunk2 = <<"Middle repair chunk">>,
|
|
Size2 = size(Chunk2),
|
|
ok = machi_flu1_client:write_chunk(Host, PortBase+1, EpochID,
|
|
File1, FooOff2, Chunk2),
|
|
{ok, Chunk2} = machi_cr_client:read_chunk(C1, File1, FooOff2, Size2),
|
|
[{X,{ok, Chunk2}} = {X,machi_flu1_client:read_chunk(
|
|
Host, PortBase+X, EpochID,
|
|
File1, FooOff2, Size2)} || X <- [0,1,2] ],
|
|
|
|
%% Misc API smoke & minor regression checks
|
|
{error, not_written} = machi_cr_client:read_chunk(C1, <<"no">>,
|
|
999999999, 1),
|
|
{error, partial_read} = machi_cr_client:read_chunk(C1, File1,
|
|
Off1, 88888888),
|
|
%% Checksum lists are 3-tuples
|
|
{ok, [{_,_,_}|_]} = machi_cr_client:checksum_list(C1, File1),
|
|
{error, no_such_file} = machi_cr_client:checksum_list(C1, <<"!!!!">>),
|
|
%% Exactly one file right now
|
|
{ok, [_]} = machi_cr_client:list_files(C1),
|
|
|
|
ok
|
|
after
|
|
error_logger:tty(true),
|
|
catch application:stop(machi),
|
|
exit(SupPid, normal)
|
|
end.
|
|
|
|
-endif. % !PULSE
|
|
-endif. % TEST.
|