Add double-backup test

Added a test with back-to-back backups.  This caused issues with the empty CDB file it created (on opening, couldn't cope with last key of empty).

So now backup won't roll the active journal if it is empty.
This commit is contained in:
Martin Sumner 2018-09-07 10:24:51 +01:00
parent daf0a1a607
commit 9a8ce88ed2
5 changed files with 85 additions and 38 deletions

View file

@ -100,7 +100,9 @@
cdb_destroy/1, cdb_destroy/1,
cdb_deletepending/1, cdb_deletepending/1,
cdb_deletepending/3, cdb_deletepending/3,
cdb_isrolling/1, cdb_isrolling/1]).
-export([finished_rolling/1,
hashtable_calc/2]). hashtable_calc/2]).
-include_lib("eunit/include/eunit.hrl"). -include_lib("eunit/include/eunit.hrl").
@ -780,6 +782,25 @@ terminate(_Reason, _StateName, _State) ->
code_change(_OldVsn, StateName, State, _Extra) -> code_change(_OldVsn, StateName, State, _Extra) ->
{ok, StateName, State}. {ok, StateName, State}.
%%%============================================================================
%%% External functions
%%%============================================================================
finished_rolling(CDB) ->
RollerFun =
fun(Sleep, FinishedRolling) ->
case FinishedRolling of
true ->
true;
false ->
timer:sleep(Sleep),
not leveled_cdb:cdb_isrolling(CDB)
end
end,
lists:foldl(RollerFun, false, [0, 1000, 10000, 100000]).
%%%============================================================================ %%%============================================================================
%%% Internal functions %%% Internal functions
%%%============================================================================ %%%============================================================================
@ -2127,6 +2148,19 @@ emptyvalue_fromdict_test() ->
?assertMatch(KVP, D_Result), ?assertMatch(KVP, D_Result),
ok = file:delete("../test/from_dict_test_ev.cdb"). ok = file:delete("../test/from_dict_test_ev.cdb").
empty_roll_test() ->
file:delete("../test/empty_roll.cdb"),
file:delete("../test/empty_roll.pnd"),
{ok, P1} = cdb_open_writer("../test/empty_roll.pnd",
#cdb_options{binary_mode=true}),
ok = cdb_roll(P1),
true = finished_rolling(P1),
{ok, P2} = cdb_open_reader("../test/empty_roll.cdb",
#cdb_options{binary_mode=true}),
ok = cdb_close(P2),
ok = file:delete("../test/empty_roll.cdb").
find_lastkey_test() -> find_lastkey_test() ->
file:delete("../test/lastkey.pnd"), file:delete("../test/lastkey.pnd"),
{ok, P1} = cdb_open_writer("../test/lastkey.pnd", {ok, P1} = cdb_open_writer("../test/lastkey.pnd",

View file

@ -168,12 +168,9 @@ writer(Manifest, ManSQN, RootPath) ->
TmpFN = filename:join(ManPath, TmpFN = filename:join(ManPath,
integer_to_list(ManSQN) ++ "." ++ ?PENDING_FILEX), integer_to_list(ManSQN) ++ "." ++ ?PENDING_FILEX),
MBin = term_to_binary(to_list(Manifest), [compressed]), MBin = term_to_binary(to_list(Manifest), [compressed]),
case filelib:is_file(NewFN) of leveled_log:log("I0016", [ManSQN]),
false -> ok = file:write_file(TmpFN, MBin),
leveled_log:log("I0016", [ManSQN]), ok = file:rename(TmpFN, NewFN),
ok = file:write_file(TmpFN, MBin),
ok = file:rename(TmpFN, NewFN)
end,
GC_SQN = ManSQN - ?MANIFESTS_TO_RETAIN, GC_SQN = ManSQN - ?MANIFESTS_TO_RETAIN,
GC_Man = filename:join(ManPath, GC_Man = filename:join(ManPath,
integer_to_list(GC_SQN) ++ "." ++ ?MANIFEST_FILEX), integer_to_list(GC_SQN) ++ "." ++ ?MANIFEST_FILEX),

View file

@ -553,18 +553,23 @@ handle_call({trim, PersistedSQN}, _From, State) ->
ok = leveled_iclerk:clerk_trim(State#state.clerk, self(), PersistedSQN), ok = leveled_iclerk:clerk_trim(State#state.clerk, self(), PersistedSQN),
{reply, ok, State}; {reply, ok, State};
handle_call(roll, _From, State) -> handle_call(roll, _From, State) ->
NewSQN = State#state.journal_sqn + 1, case leveled_cdb:cdb_lastkey(State#state.active_journaldb) of
{NewJournalP, Manifest1, NewManSQN} = empty ->
roll_active(State#state.active_journaldb, {reply, ok, State};
State#state.manifest, _ ->
NewSQN, NewSQN = State#state.journal_sqn + 1,
State#state.cdb_options, {NewJournalP, Manifest1, NewManSQN} =
State#state.root_path, roll_active(State#state.active_journaldb,
State#state.manifest_sqn), State#state.manifest,
{reply, ok, State#state{journal_sqn = NewSQN, NewSQN,
manifest = Manifest1, State#state.cdb_options,
manifest_sqn = NewManSQN, State#state.root_path,
active_journaldb = NewJournalP}}; State#state.manifest_sqn),
{reply, ok, State#state{journal_sqn = NewSQN,
manifest = Manifest1,
manifest_sqn = NewManSQN,
active_journaldb = NewJournalP}}
end;
handle_call({backup, BackupPath}, _from, State) handle_call({backup, BackupPath}, _from, State)
when State#state.is_snapshot == true -> when State#state.is_snapshot == true ->
SW = os:timestamp(), SW = os:timestamp(),
@ -576,7 +581,7 @@ handle_call({backup, BackupPath}, _from, State)
true -> true ->
BaseFN = filename:basename(FN), BaseFN = filename:basename(FN),
BackupName = filename:join(BackupJFP, BaseFN), BackupName = filename:join(BackupJFP, BaseFN),
false = when_not_rolling(PidR), true = leveled_cdb:finished_rolling(PidR),
case file:make_link(FN ++ "." ++ ?JOURNAL_FILEX, case file:make_link(FN ++ "." ++ ?JOURNAL_FILEX,
BackupName ++ "." ++ ?JOURNAL_FILEX) of BackupName ++ "." ++ ?JOURNAL_FILEX) of
ok -> ok ->
@ -712,19 +717,6 @@ start_from_file(InkOpts) ->
clerk = Clerk}}. clerk = Clerk}}.
when_not_rolling(CDB) ->
RollerFun =
fun(Sleep, WasRolling) ->
case WasRolling of
false ->
false;
true ->
timer:sleep(Sleep),
leveled_cdb:cdb_isrolling(CDB)
end
end,
lists:foldl(RollerFun, true, [0, 1000, 10000, 100000]).
-spec shutdown_snapshots(list(tuple())) -> ok. -spec shutdown_snapshots(list(tuple())) -> ok.
%% @doc %% @doc
%% Shutdown any snapshots before closing the store %% Shutdown any snapshots before closing the store
@ -983,9 +975,7 @@ open_all_manifest(Man0, RootPath, CDBOpts) ->
NewManEntry = start_new_activejournal(LastSQN + 1, NewManEntry = start_new_activejournal(LastSQN + 1,
RootPath, RootPath,
CDBOpts), CDBOpts),
leveled_imanifest:add_entry(ManToHead, leveled_imanifest:add_entry(ManToHead, NewManEntry, true);
NewManEntry,
true);
false -> false ->
{ok, HeadW} = leveled_cdb:cdb_open_writer(PendingHeadFN, {ok, HeadW} = leveled_cdb:cdb_open_writer(PendingHeadFN,
CDBOpts), CDBOpts),

View file

@ -284,7 +284,7 @@
{info, "Journal backup completed to path=~s with file_count=~w"}}, {info, "Journal backup completed to path=~s with file_count=~w"}},
{"I0021", {"I0021",
{info, "Ingoring filename=~s with SQN=~w and JournalSQN=~w"}}, {info, "Ingoring filename=~s with SQN=~w and JournalSQN=~w"}},
{"IC001", {"IC001",
{info, "Closed for reason ~w so maybe leaving garbage"}}, {info, "Closed for reason ~w so maybe leaving garbage"}},
{"IC002", {"IC002",

View file

@ -3,6 +3,7 @@
-include("include/leveled.hrl"). -include("include/leveled.hrl").
-export([all/0]). -export([all/0]).
-export([hot_backup_simple/1, -export([hot_backup_simple/1,
hot_backup_double/1,
retain_strategy/1, retain_strategy/1,
recovr_strategy/1, recovr_strategy/1,
aae_missingjournal/1, aae_missingjournal/1,
@ -12,6 +13,7 @@
all() -> [ all() -> [
hot_backup_simple, hot_backup_simple,
hot_backup_double,
retain_strategy, retain_strategy,
recovr_strategy, recovr_strategy,
aae_missingjournal, aae_missingjournal,
@ -50,7 +52,31 @@ hot_backup_simple(_Config) ->
ok = leveled_bookie:book_close(BookBackup), ok = leveled_bookie:book_close(BookBackup),
BackupPath = testutil:reset_filestructure("backup0"). BackupPath = testutil:reset_filestructure("backup0").
hot_backup_double(_Config) ->
% As with simple test, but check that calling for backup twice doesn't have
% any side effects
RootPath = testutil:reset_filestructure(),
BookOpts = [{root_path, RootPath},
{cache_size, 1000},
{max_journalsize, 10000000},
{sync_strategy, testutil:sync_strategy()}],
{ok, Spcl1, LastV1} = rotating_object_check(BookOpts, "Bucket1", 4000),
{ok, Book1} = leveled_bookie:book_start(BookOpts),
{async, BackupFun1} = leveled_bookie:book_hotbackup(Book1),
BackupPath = testutil:reset_filestructure("backup0"),
ok = BackupFun1(BackupPath),
{async, BackupFun2} = leveled_bookie:book_hotbackup(Book1),
ok = BackupFun2(BackupPath),
ok = leveled_bookie:book_close(Book1),
RootPath = testutil:reset_filestructure(),
BookOptsBackup = [{root_path, BackupPath},
{cache_size, 2000},
{max_journalsize, 20000000},
{sync_strategy, testutil:sync_strategy()}],
{ok, BookBackup} = leveled_bookie:book_start(BookOptsBackup),
ok = testutil:check_indexed_objects(BookBackup, "Bucket1", Spcl1, LastV1),
ok = leveled_bookie:book_close(BookBackup),
BackupPath = testutil:reset_filestructure("backup0").
retain_strategy(_Config) -> retain_strategy(_Config) ->
RootPath = testutil:reset_filestructure(), RootPath = testutil:reset_filestructure(),