Basic infrastructure and testing for Tango-style streams
This commit is contained in:
parent
fe79df48b5
commit
c5b4bf8d7b
2 changed files with 263 additions and 0 deletions
110
prototype/tango-prototype/src/tango.erl
Normal file
110
prototype/tango-prototype/src/tango.erl
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
%% -------------------------------------------------------------------
|
||||||
|
%%
|
||||||
|
%% Copyright (c) 2014 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.
|
||||||
|
%%
|
||||||
|
%% -------------------------------------------------------------------
|
||||||
|
|
||||||
|
%% A prototype implementation of Tango over CORFU.
|
||||||
|
|
||||||
|
-module(tango).
|
||||||
|
|
||||||
|
-export([pack_v1/3, unpack_v1/2,
|
||||||
|
add_back_pointer/3,
|
||||||
|
scan_backward/4,
|
||||||
|
pad_bin/2]).
|
||||||
|
|
||||||
|
-define(MAGIC_NUMBER_V1, 16#88990011).
|
||||||
|
|
||||||
|
%% TODO: for version 2: add strong checksum
|
||||||
|
|
||||||
|
pack_v1(StreamList, Page, PageSize) when is_list(StreamList), is_binary(Page) ->
|
||||||
|
StreamListBin = term_to_binary(StreamList),
|
||||||
|
StreamListSize = byte_size(StreamListBin),
|
||||||
|
PageActualSize = byte_size(Page),
|
||||||
|
pad_bin(PageSize,
|
||||||
|
list_to_binary([<<?MAGIC_NUMBER_V1:32/big>>,
|
||||||
|
<<StreamListSize:16/big>>,
|
||||||
|
StreamListBin,
|
||||||
|
<<PageActualSize:16/big>>,
|
||||||
|
Page])).
|
||||||
|
|
||||||
|
unpack_v1(<<?MAGIC_NUMBER_V1:32/big,
|
||||||
|
StreamListSize:16/big, StreamListBin:StreamListSize/binary,
|
||||||
|
PageActualSize:16/big, Page:PageActualSize/binary,
|
||||||
|
_/binary>>, Part) ->
|
||||||
|
if Part == stream_list ->
|
||||||
|
binary_to_term(StreamListBin);
|
||||||
|
Part == page ->
|
||||||
|
Page
|
||||||
|
end.
|
||||||
|
|
||||||
|
pad_bin(Size, Bin) when byte_size(Bin) >= Size ->
|
||||||
|
Bin;
|
||||||
|
pad_bin(Size, Bin) ->
|
||||||
|
PadSize = Size - byte_size(Bin),
|
||||||
|
<<Bin/binary, 0:(PadSize*8)>>.
|
||||||
|
|
||||||
|
add_back_pointer(StreamNum, BackPs, NewBackP) ->
|
||||||
|
case proplists:get_value(StreamNum, BackPs) of
|
||||||
|
undefined ->
|
||||||
|
[{StreamNum, [NewBackP]}];
|
||||||
|
IndividualBackPs ->
|
||||||
|
[{StreamNum, add_back_pointer(IndividualBackPs, NewBackP)}
|
||||||
|
|lists:keydelete(StreamNum, 1, BackPs)]
|
||||||
|
end.
|
||||||
|
|
||||||
|
add_back_pointer([A,B,C,_D|_], New) ->
|
||||||
|
[New,A,B,C];
|
||||||
|
add_back_pointer([], New) ->
|
||||||
|
[New];
|
||||||
|
add_back_pointer(BackPs, New) ->
|
||||||
|
[New|BackPs].
|
||||||
|
|
||||||
|
scan_backward(Proj, Stream, LastLPN, _WithPagesP) ->
|
||||||
|
lists:reverse(scan_backward2(Proj, Stream, LastLPN, _WithPagesP)).
|
||||||
|
|
||||||
|
scan_backward2(Proj, Stream, LastLPN, WithPagesP) ->
|
||||||
|
case corfurl:read_page(Proj, LastLPN) of
|
||||||
|
{ok, FullPage} ->
|
||||||
|
case proplists:get_value(Stream, unpack_v1(FullPage, stream_list)) of
|
||||||
|
undefined ->
|
||||||
|
{gahh, lpn, LastLPN, unpack_v1(FullPage, stream_list)};
|
||||||
|
%% [];
|
||||||
|
[] ->
|
||||||
|
if WithPagesP ->
|
||||||
|
[{LastLPN, unpack_v1(FullPage, page)}];
|
||||||
|
true ->
|
||||||
|
[LastLPN]
|
||||||
|
end;
|
||||||
|
BackPs ->
|
||||||
|
if WithPagesP ->
|
||||||
|
[{LastLPN, unpack_v1(FullPage, page)}|
|
||||||
|
scan_backward2(Proj, Stream,
|
||||||
|
hd(BackPs),
|
||||||
|
WithPagesP)];
|
||||||
|
true ->
|
||||||
|
SkipLPN = lists:last(BackPs),
|
||||||
|
[LastLPN] ++ (BackPs -- [SkipLPN]) ++
|
||||||
|
scan_backward2(Proj, Stream,
|
||||||
|
SkipLPN,
|
||||||
|
WithPagesP)
|
||||||
|
end
|
||||||
|
end;
|
||||||
|
Err ->
|
||||||
|
Err
|
||||||
|
end.
|
||||||
|
|
153
prototype/tango-prototype/test/tango_test.erl
Normal file
153
prototype/tango-prototype/test/tango_test.erl
Normal file
|
@ -0,0 +1,153 @@
|
||||||
|
%% -------------------------------------------------------------------
|
||||||
|
%%
|
||||||
|
%% Copyright (c) 2014 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(tango_test).
|
||||||
|
|
||||||
|
-compile(export_all).
|
||||||
|
|
||||||
|
-include("corfurl.hrl").
|
||||||
|
|
||||||
|
-ifdef(TEST).
|
||||||
|
-include_lib("eunit/include/eunit.hrl").
|
||||||
|
-compile(export_all).
|
||||||
|
-ifdef(PULSE).
|
||||||
|
-compile({parse_transform, pulse_instrument}).
|
||||||
|
-endif.
|
||||||
|
-endif.
|
||||||
|
|
||||||
|
-define(SEQ, corfurl_sequencer).
|
||||||
|
-define(T, tango).
|
||||||
|
|
||||||
|
-ifdef(TEST).
|
||||||
|
-ifndef(PULSE).
|
||||||
|
|
||||||
|
pack_v1_test() ->
|
||||||
|
[begin
|
||||||
|
Packed = ?T:pack_v1(StreamList, term_to_binary(Term), Size),
|
||||||
|
StreamList = ?T:unpack_v1(Packed, stream_list),
|
||||||
|
TermBin = ?T:unpack_v1(Packed, page),
|
||||||
|
Term = binary_to_term(TermBin)
|
||||||
|
end || StreamList <- [[], [1], [1,2,4]],
|
||||||
|
Term <- [foo, {bar, baz, <<"yo">>}],
|
||||||
|
Size <- lists:seq(100, 5000, 500)].
|
||||||
|
|
||||||
|
run_test(Name, PageSize, NumPages, NumFLUs, FUN) ->
|
||||||
|
PDir = "./tmp." ++ Name,
|
||||||
|
BaseDir = "/tmp/" ++ atom_to_list(?MODULE) ++ ".",
|
||||||
|
MyDir = fun(X) -> BaseDir ++ integer_to_list(X) end,
|
||||||
|
Del = fun() -> [ok = corfurl_util:delete_dir(MyDir(X)) ||
|
||||||
|
X <- lists:seq(1, NumFLUs)] end,
|
||||||
|
|
||||||
|
Del(),
|
||||||
|
FLUs = [begin
|
||||||
|
element(2, corfurl_flu:start_link(MyDir(X),
|
||||||
|
PageSize, NumPages*PageSize))
|
||||||
|
end || X <- lists:seq(1, NumFLUs)],
|
||||||
|
|
||||||
|
try
|
||||||
|
{ok, Seq} = ?SEQ:start_link(FLUs),
|
||||||
|
try
|
||||||
|
P0 = corfurl:new_simple_projection(PDir, 1, 1, 1*100, [FLUs]),
|
||||||
|
P1 = P0#proj{seq={Seq, unused, unused}},
|
||||||
|
FUN(PageSize, Seq, P1)
|
||||||
|
after
|
||||||
|
?SEQ:stop(Seq)
|
||||||
|
end
|
||||||
|
after
|
||||||
|
[ok = corfurl_flu:stop(FLU) || FLU <- FLUs],
|
||||||
|
Del()
|
||||||
|
end.
|
||||||
|
|
||||||
|
smoke_test() ->
|
||||||
|
ok = run_test("projection", 4096, 5*1024, 1, fun smoke_test_fun/3).
|
||||||
|
|
||||||
|
smoke_test_fun(PageSize, Seq, P1) ->
|
||||||
|
ok = ?SEQ:set_tails(Seq, [{42,4242}, {43,4343}]),
|
||||||
|
{ok, [4242, 4343]} = ?SEQ:get_tails(Seq, [42, 43]),
|
||||||
|
|
||||||
|
LPN_Pgs = [{X, ?T:pad_bin(PageSize, term_to_binary({smoke, X}))} ||
|
||||||
|
X <- lists:seq(1, 5)],
|
||||||
|
[begin
|
||||||
|
{{ok, LPN}, _} = corfurl_client:append_page(P1, Pg)
|
||||||
|
end || {LPN, Pg} <- LPN_Pgs],
|
||||||
|
[begin
|
||||||
|
{ok, Pg} = corfurl:read_page(P1, LPN)
|
||||||
|
end || {LPN, Pg} <- LPN_Pgs],
|
||||||
|
|
||||||
|
ok.
|
||||||
|
|
||||||
|
write_forward_test() ->
|
||||||
|
ok = run_test("write_forward", 4096, 5*1024, 1, fun write_forward_test_fun/3).
|
||||||
|
|
||||||
|
write_forward_test_fun(PageSize, _Seq, P1) ->
|
||||||
|
StreamNum = 0,
|
||||||
|
NumPages = 10,
|
||||||
|
Pages = [term_to_binary({smoke, X}) || X <- lists:seq(1, NumPages)],
|
||||||
|
BackPs0 = [{StreamNum, []}],
|
||||||
|
{P2, BackPs1} = write_stream_pages(P1, Pages, PageSize, BackPs0, StreamNum),
|
||||||
|
{_P3, _BackPs2} = write_stream_pages(P2, Pages, PageSize, BackPs1, StreamNum),
|
||||||
|
|
||||||
|
ok.
|
||||||
|
|
||||||
|
write_stream_pages(Proj0, Pages, PageSize, InitialBackPs, StreamNum) ->
|
||||||
|
F = fun(Page, {Proj1, BackPs}) ->
|
||||||
|
FullPage = tango:pack_v1(BackPs, Page, PageSize),
|
||||||
|
{{ok, LPN}, Proj2} =
|
||||||
|
corfurl_client:append_page(Proj1, FullPage),
|
||||||
|
{Proj2, tango:add_back_pointer(StreamNum, BackPs, LPN)}
|
||||||
|
end,
|
||||||
|
{_Px, BackPs} = Res = lists:foldl(F, {Proj0, InitialBackPs}, Pages),
|
||||||
|
io:format(user, "BackPs ~p\n", [BackPs]),
|
||||||
|
Res.
|
||||||
|
|
||||||
|
scan_backward_test() ->
|
||||||
|
ok = run_test("scan_backward", 4096, 5*1024, 1, fun scan_backward_test_fun/3).
|
||||||
|
|
||||||
|
scan_backward_test_fun(PageSize, _Seq, P1) ->
|
||||||
|
StreamNum = 0,
|
||||||
|
NumPages = 10,
|
||||||
|
PageSeq = lists:seq(1, NumPages),
|
||||||
|
Pages = [term_to_binary({smoke, X}) || X <- PageSeq],
|
||||||
|
BackPs0 = [{StreamNum, []}],
|
||||||
|
{P2, BackPs1} = write_stream_pages(P1, Pages, PageSize, BackPs0, StreamNum),
|
||||||
|
LastLPN = hd(proplists:get_value(StreamNum, BackPs1)),
|
||||||
|
io:format(user, "\nLastLPN ~p\n", [LastLPN]),
|
||||||
|
|
||||||
|
LastLPN=LastLPN,
|
||||||
|
[begin
|
||||||
|
ShouldBe = lists:seq(1, BackwardStartLPN),
|
||||||
|
ShouldBePages = lists:zip(ShouldBe, lists:sublist(Pages, BackwardStartLPN)),
|
||||||
|
|
||||||
|
%% If we scan backward, we should get a list of LPNs in
|
||||||
|
%% oldest -> newest (i.e. smallest LPN to largest LPN) order.
|
||||||
|
ShouldBe = tango:scan_backward(P2, StreamNum, BackwardStartLPN,
|
||||||
|
false),
|
||||||
|
|
||||||
|
%% If we scan backward, we should get a list of LPNs in
|
||||||
|
%% oldest -> newest (i.e. smallest LPN to largest LPN) order
|
||||||
|
%% together with the actual page data.
|
||||||
|
ShouldBePages = tango:scan_backward(P2, StreamNum, BackwardStartLPN,
|
||||||
|
true)
|
||||||
|
end || BackwardStartLPN <- lists:seq(1, NumPages)],
|
||||||
|
|
||||||
|
ok.
|
||||||
|
|
||||||
|
-endif. % not PULSE
|
||||||
|
-endif. % TEST
|
Loading…
Reference in a new issue