%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1999-2018. All Rights Reserved.
%%
%% Licensed 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.
%%
%% %CopyrightEnd%
%%
%%
-module(mnesia_frag_test).
-author('[email protected]').
-include("mnesia_test_lib.hrl").
-export([init_per_testcase/2, end_per_testcase/2,
init_per_group/2, end_per_group/2,
all/0, groups/0]).
-export([nice_single/1, nice_multi/1, nice_access/1, iter_access/1,
consistency/1, evil_create/1, evil_delete/1, evil_change/1, evil_combine/1,
evil_loop/1, evil_delete_db_node/1]).
-export([frag_dist/1]).
init_per_testcase(Func, Conf) ->
mnesia_test_lib:init_per_testcase(Func, Conf).
end_per_testcase(Func, Conf) ->
mnesia_test_lib:end_per_testcase(Func, Conf).
-define(match_dist(ExpectedRes, Expr),
case ?match(ExpectedRes, Expr) of
mnesia_test_lib:error(Format, Args,?FILE,?LINE)).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
all() ->
[{group, light}, {group, medium}].
groups() ->
[{light, [], [{group, nice}, {group, evil}]},
{medium, [], [consistency]},
{nice, [],
[nice_single, nice_multi, nice_access, iter_access]},
{evil, [],
[evil_create, evil_delete, evil_change, evil_combine,
evil_loop, evil_delete_db_node]}].
init_per_group(_GroupName, Config) ->
Config.
end_per_group(_GroupName, Config) ->
Config.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
nice_single(suite) -> [];
nice_single(Config) when is_list(Config) ->
[Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
%% Create a table with 2 fragments and 12 records
Tab = nice_frag,
Props = [{n_fragments, 2}, {node_pool, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Tab, [{frag_properties, Props}])),
Records = [{Tab, N, -N} || N <- lists:seq(1, 12)],
[frag_write(Tab, R) || R <- Records],
?match([{Node1, 2}], frag_dist(Tab)),
?match([8, 4], frag_rec_dist(Tab)),
%% Adding a new node to pool should not affect distribution
?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_node, Node2})),
Dist = frag_dist(Tab),
?match([{Node2, 0}, {Node1, 2}], Dist),
?match([8, 4], frag_rec_dist(Tab)),
%% Add new fragment hopefully on the new node
?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist})),
Dist2 = frag_dist(Tab),
?match([{Node2, 1}, {Node1, 2}], Dist2),
?match([3, 4, 5], frag_rec_dist(Tab)),
%% Add new fragment hopefully on the new node
?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist2})),
Dist3 = frag_dist(Tab),
?match([{Node1, 2}, {Node2, 2}], Dist3),
?match([3, 2, 5, 2], frag_rec_dist(Tab)),
%% Add new fragment hopefully on the new node
?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist3})),
Dist4 = frag_dist(Tab),
?match([{Node2, 2}, {Node1, 3}], Dist4),
?match([_, _, _, _, _], frag_rec_dist(Tab)),
%% Dropping a node in pool should not affect distribution
?match({atomic, ok}, mnesia:change_table_frag(Tab, {del_node, Node1})),
?match([{Node2, 2}, {Node1, 3}], frag_dist(Tab)),
?match([_, _, _, _, _], frag_rec_dist(Tab)),
%% Dropping a fragment
?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
Dist5 = frag_dist(Tab),
?match([{Node2, 2}, {Node1, 2}], Dist5),
?match([3, 2, 5, 2], frag_rec_dist(Tab)),
%% Add new fragment hopefully on the new node
?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist5})),
Dist6 = frag_dist(Tab),
?match([{Node2, 3}, {Node1, 2}], Dist6),
?match([_, _, _, _, _], frag_rec_dist(Tab)),
%% Dropping all fragments but one
?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
?match([3, 2, 5, 2], frag_rec_dist(Tab)),
?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
?match([3, 4, 5], frag_rec_dist(Tab)),
?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
?match([8, 4], frag_rec_dist(Tab)),
?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
?match([{Node2, 0}, {Node1, 1}], frag_dist(Tab)),
?match([12], frag_rec_dist(Tab)),
%% Defragmenting the table clears frag_properties
?match(Len when Len > 0,
length(mnesia:table_info(Tab, frag_properties))),
?match({atomic, ok}, mnesia:change_table_frag(Tab, deactivate)),
?match(0, length(mnesia:table_info(Tab, frag_properties))),
%% Making the table fragmented again
Props2 = [{n_fragments, 1}],
?match({atomic, ok}, mnesia:change_table_frag(Tab, {activate, Props2})),
?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, frag_dist(Tab)})),
Dist7 = frag_dist(Tab),
?match([{Node1, 1}, {Node2, 1}], Dist7),
?match([8, 4], frag_rec_dist(Tab)),
%% Deleting the fragmented table
?match({atomic, ok}, mnesia:delete_table(Tab)),
?match(false, lists:member(Tab, mnesia:system_info(tables))),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
nice_multi(doc) ->
["Extending the nice case with one more node, ",
"one more replica and a foreign key"];
nice_multi(suite) -> [];
nice_multi(Config) when is_list(Config) ->
[Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
%% Create a table with 2 fragments and 8 records
Tab = frag_master,
Name = frag_rec,
Type = case mnesia_test_lib:diskless(Config) of
true -> n_ram_copies;
false -> n_disc_copies
end,
Props = [{n_fragments, 2},
{Type, 2},
{node_pool, [Node2, Node1]}],
Def = [{frag_properties, Props},
{attributes, [id, data]},
{record_name, Name}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
[frag_write(Tab, {Name, Id, -Id}) || Id <- lists:seq(1, 8)],
?match([6, 2], frag_rec_dist(Tab)),
?match([{Node2, 2}, {Node1, 2}], frag_dist(Tab)),
%% And connect another table to it, via a foreign key
TabF = frag_slave,
PropsF = [{foreign_key, {Tab, foreign_id}}],
DefF = [{frag_properties, PropsF},
{attributes, [id, foreign_id]}],
?match({atomic, ok}, mnesia:create_table(TabF, DefF)),
[frag_write(TabF, {TabF, {Id}, Id}) || Id <- lists:seq(1, 16)],
?match([10, 6], frag_rec_dist(TabF)),
?match([{Node2, 2}, {Node1, 2}], frag_dist(TabF)),
%% Adding a new node to pool should not affect distribution
?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_node, Node3})),
Dist = frag_dist(Tab),
?match([{Node3, 0}, {Node2, 2}, {Node1, 2}], Dist),
?match([6, 2], frag_rec_dist(Tab)),
DistF = frag_dist(TabF),
?match([{Node3, 0}, {Node2, 2}, {Node1, 2}], DistF),
?match([10, 6], frag_rec_dist(TabF)),
%% Add new fragment hopefully on the new node
?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist})),
Dist2 = frag_dist(Tab),
?match([{Node3, 1},{Node1, 2},{Node2,3}], Dist2),
?match([_, _, _], frag_rec_dist(Tab)),
DistF2 = frag_dist(TabF),
?match([{Node3, 1},{Node1, 2},{Node2,3}], DistF2),
?match([_, _, _], frag_rec_dist(TabF)),
%% Add new fragment hopefully on the new node
?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist2})),
Dist3 = frag_dist(Tab),
?match([{Node3, 2},{Node2,3},{Node1, 3}], Dist3),
?match([3, 0, 3, 2], frag_rec_dist(Tab)),
DistF3 = frag_dist(TabF),
?match([{Node3, 2},{Node2,3},{Node1, 3}], DistF3),
?match([3, 3, 7, 3], frag_rec_dist(TabF)),
%% Add new fragment hopefully on the new node
?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist3})),
Dist4 = frag_dist(Tab),
?match([{Node1, 3}, {Node3, 3},{Node2, 4}], Dist4),
?match([_, _, _, _, _], frag_rec_dist(Tab)),
DistF4 = frag_dist(TabF),
?match([{Node1, 3}, {Node3, 3},{Node2, 4}], DistF4),
?match([_, _, _, _, _], frag_rec_dist(TabF)),
%% Dropping a node in pool should not affect distribution
?match({atomic, ok}, mnesia:change_table_frag(Tab, {del_node, Node1})),
?match([{Node3, 3},{Node2, 4}, {Node1, 3}], frag_dist(Tab)),
?match([_, _, _, _, _], frag_rec_dist(Tab)),
?match([{Node3, 3},{Node2, 4}, {Node1, 3}], frag_dist(TabF)),
?match([_, _, _, _, _], frag_rec_dist(TabF)),
%% Dropping a fragment
?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
Dist5 = frag_dist(Tab),
?match([{Node3, 2},{Node2,3},{Node1, 3}], Dist5),
?match([3, 0, 3, 2], frag_rec_dist(Tab)),
DistF5 = frag_dist(Tab),
?match([{Node3, 2},{Node2,3},{Node1, 3}], DistF5),
?match([3, 3, 7, 3], frag_rec_dist(TabF)),
%% Add new fragment hopefully on the new node
?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist5})),
Dist6 = frag_dist(Tab),
?match([{Node3, 3},{Node2, 4},{Node1, 3}], Dist6),
?match([_, _, _, _, _], frag_rec_dist(Tab)),
DistF6 = frag_dist(TabF),
?match([{Node3, 3},{Node2, 4},{Node1, 3}], DistF6),
?match([_, _, _, _, _], frag_rec_dist(TabF)),
%% Dropping all fragments but one
?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
?match([3, 0, 3, 2], frag_rec_dist(Tab)),
?match([3, 3, 7, 3], frag_rec_dist(TabF)),
?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
?match([_, _, _], frag_rec_dist(Tab)),
?match([_, _, _], frag_rec_dist(TabF)),
?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
?match([6, 2], frag_rec_dist(Tab)),
?match([10, 6], frag_rec_dist(TabF)),
?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)),
?match([{Node3, 0}, {Node2, 1}, {Node1, 1}], frag_dist(Tab)),
?match([8], frag_rec_dist(Tab)),
?match([{Node3, 0}, {Node2, 1}, {Node1, 1}], frag_dist(TabF)),
?match([16], frag_rec_dist(TabF)),
%% Defragmenting the tables clears frag_properties
?match(Len when Len > 0,
length(mnesia:table_info(TabF, frag_properties))),
?match({atomic, ok}, mnesia:change_table_frag(TabF, deactivate)),
?match(0, length(mnesia:table_info(TabF, frag_properties))),
?match(Len when Len > 0,
length(mnesia:table_info(Tab, frag_properties))),
?match({atomic, ok}, mnesia:change_table_frag(Tab, deactivate)),
?match(0, length(mnesia:table_info(Tab, frag_properties))),
%% Making the tables fragmented again
Props2 = [{n_fragments, 1}, {node_pool, [Node1, Node2]}],
?match({atomic, ok}, mnesia:change_table_frag(Tab, {activate, Props2})),
?match({atomic, ok}, mnesia:delete_table(TabF)),
?match({atomic, ok}, mnesia:create_table(TabF, DefF)),
[frag_write(TabF, {TabF, {Id}, Id}) || Id <- lists:seq(1, 16)],
?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, frag_dist(Tab)})),
?match([{Node1, 2}, {Node2, 2}], frag_dist(Tab)),
?match([6, 2], frag_rec_dist(Tab)),
?match([{Node1, 2}, {Node2, 2}], frag_dist(TabF)),
?match([10, 6], frag_rec_dist(TabF)),
%% Deleting the fragmented tables
?match({atomic, ok}, mnesia:delete_table(TabF)),
?match(false, lists:member(TabF, mnesia:system_info(tables))),
?match({atomic, ok}, mnesia:delete_table(Tab)),
?match(false, lists:member(Tab, mnesia:system_info(tables))),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
nice_access(doc) ->
["Cover entire callback interface"];
nice_access(suite) -> [];
nice_access(Config) when is_list(Config) ->
Nodes = ?acquire_nodes(3, Config),
Tab = frag_access,
Pool = lists:sort(Nodes),
Props = [{n_fragments, 20},
{n_ram_copies, 2},
{node_pool, Pool}],
Def = [{frag_properties, Props},
{type, ordered_set},
{index, [val]}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
[frag_write(Tab, {Tab, Id, Id}) || Id <- lists:seq(1, 400)],
%% And connect another table to it, via a foreign key
TabF = frag_access_slave,
PropsF = [{foreign_key, {Tab, val}}],
DefF = [{frag_properties, PropsF},
{index, [val]}],
?match({atomic, ok}, mnesia:create_table(TabF, DefF)),
[frag_write(TabF, {TabF, Id, Id}) || Id <- lists:seq(1, 400)],
?match(done, mnesia:activity(transaction, fun do_access/3, [Tab, Tab, Pool], mnesia_frag)),
?match(done, mnesia:activity(transaction, fun do_access/3, [TabF, Tab, Pool], mnesia_frag)),
?verify_mnesia(Nodes, []).
do_access(Tab, Master, Pool) ->
?match(20, mnesia:table_info(Tab, n_fragments)),
?match(Pool, mnesia:table_info(Tab, node_pool)),
?match(2, mnesia:table_info(Tab, n_ram_copies)),
?match(0, mnesia:table_info(Tab, n_disc_copies)),
?match(0, mnesia:table_info(Tab, n_disc_only_copies)),
?match(20, length(mnesia:table_info(Tab, frag_names))),
?match(20, length(mnesia:table_info(Tab, frag_size))),
?match(20, length(mnesia:table_info(Tab, frag_memory))),
PoolSize = length(Pool),
?match(PoolSize, length(mnesia:table_info(Tab, frag_dist))),
?match(400, mnesia:table_info(Tab, size)),
?match(I when is_integer(I), mnesia:table_info(Tab, memory)),
?match(Tab, mnesia:table_info(Tab, base_table)),
Foreign =
if
Master == Tab ->
?match(undefined, mnesia:table_info(Tab, foreign_key)),
?match([_], mnesia:table_info(Tab, foreigners)),
?match({'EXIT', {aborted, {combine_error, Tab, frag_properties, {foreign_key, undefined}}}},
mnesia:read({Tab, 5}, 5, read)),
fun({T, _K}) -> T end;
true ->
?match({Master, 3}, mnesia:table_info(Tab, foreign_key)),
?match([], mnesia:table_info(Tab, foreigners)),
fun({T, K}) -> {T, K} end
end,
Attr = val,
?match(400, mnesia:table_info(Tab, size)),
Count = fun(_, N) -> N + 1 end,
?match(400, mnesia:foldl(Count, 0, Tab)),
?match(400, mnesia:foldr(Count, 0, Tab)),
?match(ok, mnesia:write({Tab, [-1], 1})),
?match(401, length(mnesia:match_object(Tab, {Tab, '_', '_'}, read))),
?match(401, length(mnesia:select(Tab, [{{Tab, '_', '$1'}, [], ['$1']}], read))),
First = mnesia:select(Tab, [{{Tab, '_', '$1'}, [], ['$1']}], 10, read),
TestCont = fun('$end_of_table', Total, _This) ->
Total;
({Res,Cont1}, Total, This) ->
Cont = mnesia:select(Cont1),
This(Cont, length(Res) + Total, This)
end,
?match(401, TestCont(First, 0, TestCont)),
%% OTP
[_, Frag2|_] = frag_names(Tab),
Frag2key = mnesia:dirty_first(Frag2),
?match({[Frag2key],_},mnesia:select(Tab,[{{Tab,Frag2key,'$1'},[],['$1']}],100,read)),
?match([{Tab, [-1], 1}], mnesia:read(Foreign({Tab, 1}), [-1], read)),
?match(401, mnesia:foldl(Count, 0, Tab)),
?match(401, mnesia:foldr(Count, 0, Tab)),
?match(ok, mnesia:delete(Foreign({Tab, 2}), 2, write)),
?match([], mnesia:read(Foreign({Tab, 2}), 2, read)),
?match([{Tab, 3, 3}], mnesia:read(Foreign({Tab, 3}), 3, read)),
?match(400, mnesia:foldl(Count, 0, Tab)),
?match(400, mnesia:foldr(Count, 0, Tab)),
?match(ok, mnesia:delete_object({Tab, 3, 3})),
?match([], mnesia:read(Foreign({Tab, 3}), 3, read)),
One = lists:sort([{Tab, 1, 1}, {Tab, [-1], 1}]),
Pat = {Tab, '$1', 1},
?match(One, lists:sort(mnesia:match_object(Tab, Pat, read))),
?match([1,[-1]], lists:sort(mnesia:select(Tab, [{Pat, [], ['$1']}], read))),
?match([[[-1]]], lists:sort(mnesia:select(Tab, [{Pat, [{is_list, '$1'}], [['$1']]}], read))),
?match([[1, 100]], lists:sort(mnesia:select(Tab, [{Pat, [{is_integer, '$1'}], [['$1',100]]}], read))),
?match([1,[-1]], lists:sort(mnesia:select(Tab, [{Pat, [{is_list, '$1'}], ['$1']},{Pat, [{is_integer, '$1'}], ['$1']}], read))),
?match(One, lists:sort(mnesia:index_match_object(Tab, Pat, Attr, read) )),
?match(One, lists:sort(mnesia:index_read(Tab, 1, Attr))),
Keys = mnesia:all_keys(Tab),
?match([-1], lists:max(Keys)), %% OTP-3779
?match(399, length(Keys)),
?match(399, mnesia:foldl(Count, 0, Tab)),
?match(399, mnesia:foldr(Count, 0, Tab)),
?match(Pool, lists:sort(mnesia:lock({table, Tab}, write))),
done.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
iter_access(doc) ->
["Cover table iteration via callback interface"];
iter_access(suite) -> [];
iter_access(Config) when is_list(Config) ->
Nodes = ?acquire_nodes(3, Config),
Tab = frag_access,
Pool = lists:sort(Nodes),
Props = [{n_fragments, 20},
{n_ram_copies, 2},
{node_pool, Pool}],
Def = [{frag_properties, Props},
{type, ordered_set},
{index, [val]}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
[frag_write(Tab, {Tab, Id, Id}) || Id <- lists:seq(1, 400)],
FragNames = frag_names(Tab),
RawRead =
fun(Frag) ->
Node = mnesia:table_info(Frag, where_to_read),
{Frag, rpc:call(Node, ets, tab2list, [Frag])}
end,
?match(done, mnesia:activity(transaction, fun nice_iter_access/3, [Tab, FragNames, RawRead], mnesia_frag)),
FragNames = frag_names(Tab),
[First, Second | _] = FragNames,
[Last, LastButOne | _] = lists:reverse(FragNames),
?match({atomic, ok}, mnesia:clear_table(First)),
?match({atomic, ok}, mnesia:clear_table(Second)),
?match({atomic, ok}, mnesia:clear_table(lists:nth(8, FragNames))),
?match({atomic, ok}, mnesia:clear_table(lists:nth(9, FragNames))),
?match({atomic, ok}, mnesia:clear_table(lists:nth(10, FragNames))),
?match({atomic, ok}, mnesia:clear_table(lists:nth(11, FragNames))),
?match({atomic, ok}, mnesia:clear_table(LastButOne)),
?match({atomic, ok}, mnesia:clear_table(Last)),
?match(done, mnesia:activity(transaction, fun evil_iter_access/3, [Tab, FragNames, RawRead], mnesia_frag)),
Size = fun(Table) -> mnesia:table_info(Table, size) end,
?match(true, 0 < mnesia:activity(transaction, Size, [Tab], mnesia_frag)),
?match({atomic, ok}, mnesia:activity(ets, fun() -> mnesia:clear_table(Tab) end, mnesia_frag)),
?match(0, mnesia:activity(transaction, Size, [Tab], mnesia_frag)),
?verify_mnesia(Nodes, []).
nice_iter_access(Tab, FragNames, RawRead) ->
RawData = ?ignore(lists:map(RawRead, FragNames)),
Keys = [K || {_, Recs} <- RawData, {_, K, _} <- Recs],
ExpectedFirst = hd(Keys),
?match(ExpectedFirst, mnesia:first(Tab)),
ExpectedLast = lists:last(Keys),
?match(ExpectedLast, mnesia:last(Tab)),
ExpectedAllPrev = ['$end_of_table' | lists:droplast(Keys)],
?match(ExpectedAllPrev, lists:map(fun(K) -> mnesia:prev(Tab, K) end, Keys)),
ExpectedAllNext = tl(Keys) ++ ['$end_of_table'],
?match(ExpectedAllNext, lists:map(fun(K) -> mnesia:next(Tab, K) end, Keys)),
done.
evil_iter_access(Tab, FragNames, RawRead) ->
RawData = ?ignore(lists:map(RawRead, FragNames)),
Keys = [K || {_, Recs} <- RawData, {_, K, _} <- Recs],
ExpectedFirst = hd(Keys),
?match(ExpectedFirst, mnesia:first(Tab)),
ExpectedLast = lists:last(Keys),
?match(ExpectedLast, mnesia:last(Tab)),
ExpectedAllPrev = ['$end_of_table' | lists:droplast(Keys)],
?match(ExpectedAllPrev, lists:map(fun(K) -> mnesia:prev(Tab, K) end, Keys)),
ExpectedAllNext = tl(Keys) ++ ['$end_of_table'],
?match(ExpectedAllNext, lists:map(fun(K) -> mnesia:next(Tab, K) end, Keys)),
done.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
consistency(doc) ->
["Add and delete fragments during TPC-B"];
consistency(suite) -> [];
consistency(Config) when is_list(Config) ->
?skip("Not yet implemented (NYI).~n", []),
Nodes = ?acquire_nodes(2, Config),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
evil_create(suite) -> [];
evil_create(Config) when is_list(Config) ->
[Node1, _Node2] = Nodes = ?acquire_nodes(2, Config),
Create = fun(T, D, P) -> mnesia:create_table(T, [{frag_properties, P}| D]) end,
Tab = evil_create,
%% Props in general
?match({aborted, {badarg, Tab, {frag_properties, no_list}}},
Create(Tab, [], no_list)),
?match({aborted, {badarg,Tab , [no_tuple]}},
Create(Tab, [], [no_tuple])),
?match({aborted,{badarg, Tab, bad_key}},
Create(Tab, [], [{bad_key, 7}])),
%% n_fragments
?match({aborted,{badarg, Tab, [{n_fragments}]}},
Create(Tab, [], [{n_fragments}])),
?match({aborted,{badarg, Tab, [{n_fragments, 1, 1}]}},
Create(Tab, [], [{n_fragments, 1, 1}])),
?match({aborted, {bad_type,Tab, {n_fragments, a}}},
Create(Tab, [], [{n_fragments, a}])),
?match({aborted, {bad_type, Tab, {n_fragments, 0}}},
Create(Tab, [], [{n_fragments, 0}])),
%% *_copies
?match({aborted, {bad_type, Tab, {n_ram_copies, -1}}},
Create(Tab, [], [{n_ram_copies, -1}, {n_fragments, 1}])),
?match({aborted, {bad_type, Tab, {n_disc_copies, -1}}},
Create(Tab, [], [{n_disc_copies, -1}, {n_fragments, 1}])),
?match({aborted, {bad_type, Tab, {n_disc_only_copies, -1}}},
Create(Tab, [], [{n_disc_only_copies, -1}, {n_fragments, 1}])),
%% node_pool
?match({aborted, {bad_type, Tab, {node_pool, 0}}},
Create(Tab, [], [{node_pool, 0}])),
?match({aborted, {combine_error, Tab, "Too few nodes in node_pool"}},
Create(Tab, [], [{n_ram_copies, 2}, {node_pool, [Node1]}])),
%% foreign_key
?match({aborted, {bad_type, Tab, {foreign_key, bad_key}}},
Create(Tab, [], [{foreign_key, bad_key}])),
?match({aborted,{bad_type, Tab, {foreign_key, {bad_key}}}},
Create(Tab, [], [{foreign_key, {bad_key}}])),
?match({aborted, {no_exists, {bad_tab, frag_properties}}},
Create(Tab, [], [{foreign_key, {bad_tab, val}}])),
?match({aborted, {combine_error, Tab, {Tab, val}}},
Create(Tab, [], [{foreign_key, {Tab, val}}])),
?match({atomic, ok},
Create(Tab, [], [{n_fragments, 1}])),
?match({aborted, {already_exists, Tab}},
Create(Tab, [], [{n_fragments, 1}])),
Tab2 = evil_create2,
?match({aborted, {bad_type, no_attr}},
Create(Tab2, [], [{foreign_key, {Tab, no_attr}}])),
?match({aborted, {combine_error, Tab2, _, _, _}},
Create(Tab2, [], [{foreign_key, {Tab, val}},
{node_pool, [Node1]}])),
?match({aborted, {combine_error, Tab2, _, _, _}},
Create(Tab2, [], [{foreign_key, {Tab, val}},
{n_fragments, 2}])),
?match({atomic, ok},
Create(Tab2, [{attributes, [a, b, c]}], [{foreign_key, {Tab, c}}])),
Tab3 = evil_create3,
?match({aborted, {combine_error, Tab3, _, _, _}},
Create(Tab3, [{attributes, [a, b]}], [{foreign_key, {Tab2, b}}])),
?match({atomic, ok},
Create(Tab3, [{attributes, [a, b]}], [{foreign_key, {Tab, b}}])),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
evil_delete(suite) -> [];
evil_delete(Config) when is_list(Config) ->
?skip("Not yet implemented (NYI).~n", []),
Nodes = ?acquire_nodes(2, Config),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
evil_change(suite) -> [];
evil_change(Config) when is_list(Config) ->
[N1,N2,_N3] = Nodes = ?acquire_nodes(3, Config),
Create = fun(T, D, P) -> mnesia:create_table(T, [{frag_properties, P}| D]) end,
Props1 = [{n_fragments, 2}, {node_pool, [N1]}],
Tab1 = evil_change_ram,
?match({atomic, ok}, Create(Tab1, [], Props1)),
?match({atomic,ok}, mnesia:change_table_frag(Tab1, {add_frag, Nodes})),
Dist10 = frag_dist(Tab1),
?match([{N1,3}], Dist10),
?match({atomic, ok}, mnesia:change_table_frag(Tab1, {add_node, N2})),
Dist11 = frag_dist(Tab1),
?match([{N2,0},{N1,3}], Dist11),
mnesia_test_lib:kill_mnesia([N2]),
?match({aborted,_}, mnesia:change_table_frag(Tab1, {add_frag, [N2,N1]})),
?verbose("~p~n",[frag_dist(Tab1)]),
mnesia_test_lib:start_mnesia([N2]),
Tab2 = evil_change_disc,
?match({atomic,ok}, Create(Tab2,[],[{n_disc_copies,1},{n_fragments,1},{node_pool,[N1,N2]}])),
?verbose("~p~n", [frag_dist(Tab2)]),
?match({atomic,ok}, mnesia:change_table_frag(Tab2, {add_frag, [N1,N2]})),
_Dist20 = frag_dist(Tab2),
mnesia_test_lib:kill_mnesia([N2]),
?match({atomic,ok}, mnesia:change_table_frag(Tab2, {add_frag, [N1,N2]})),
?match({aborted,_}, mnesia:change_table_frag(Tab2, {add_frag, [N2,N1]})),
mnesia_test_lib:start_mnesia([N2]),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
evil_combine(doc) -> ["Bug in mnesia_4.1.5. and earlier"];
evil_combine(suite) -> [];
evil_combine(Config) when is_list(Config) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
?match({atomic, ok},mnesia:create_table(tab1, [{disc_copies, [Node1]},
{frag_properties, [{n_fragments, 2},
{node_pool, [Node1]},
{n_disc_copies, 1}]}])),
?match({atomic, ok},mnesia:create_table(tab2, [{disc_copies, [Node1]}])),
mnesia:wait_for_tables([tab1, tab2], infinity),
Add2 = fun() ->
mnesia:transaction(fun() ->
mnesia:write({tab2,1,1})
end)
end,
Fun = fun() ->
Add2(),
mnesia:write({tab1,9,10})
end,
?match(ok, mnesia:activity({transaction, 1}, Fun, [], mnesia_frag)),
Read = fun(T, K) ->
mnesia:read(T, K, read)
end,
?match([{tab1,9,10}],mnesia:activity(async_dirty, Read, [tab1, 9], mnesia_frag)),
?match([{tab2,1,1}],mnesia:activity(async_dirty, Read, [tab2, 1], mnesia_frag)),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
evil_loop(doc) -> ["Test select/[14]"];
evil_loop(suite) -> [];
evil_loop(Config) when is_list(Config) ->
[Node1,_Node2] = ?acquire_nodes(2, Config),
Tab1 = ss_oset,
Tab2 = ss_set,
Tab3 = ss_bag,
Tabs = [Tab1, Tab2, Tab3],
RecName = ss,
?match({atomic, ok}, mnesia:create_table([{name, Tab1},
{ram_copies, [Node1]},
{record_name, RecName},
{type, ordered_set}])),
?match({atomic, ok}, mnesia:create_table([{name, Tab2},
{record_name, RecName},
{ram_copies, [Node1]},
{type, set}])),
?match({atomic, ok}, mnesia:create_table([{name, Tab3},
{record_name, RecName},
{ram_copies, [Node1]},
{type, bag}])),
Keys = [-3, -2] ++ lists:seq(1, 5, 2) ++ lists:seq(6, 10),
Recs = [{RecName, K, K} || K <- Keys],
[mnesia:dirty_write(Tab1, R) || R <- Recs],
[mnesia:dirty_write(Tab2, R) || R <- Recs],
[mnesia:dirty_write(Tab3, R) || R <- Recs],
Activate =
fun(Tab) ->
?match({atomic, ok}, mnesia:change_table_frag(Tab, {activate, []})),
Dist = frag_dist(Tab),
?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist}))
end,
Activate(Tab1),
Activate(Tab2),
Activate(Tab3),
Match = fun(Tab) -> mnesia:match_object(Tab, {'_', '_', '_'}, write) end,
Select = fun(Tab) -> mnesia:select(Tab, [{'_', [], ['$_']}]) end,
Trans = fun(Fun, Args) -> mnesia:activity(transaction, Fun, Args, mnesia_frag) end,
LoopHelp = fun('$end_of_table',_) ->
[];
({Res,Cont},Fun) ->
Sel = mnesia:select(Cont),
Res ++ Fun(Sel, Fun)
end,
SelLoop = fun(Table) ->
Sel = mnesia:select(Table, [{'_', [], ['$_']}], 1, read),
LoopHelp(Sel, LoopHelp)
end,
R1 = {RecName, 2, 2},
R2 = {RecName, 4, 4},
R3 = {RecName, 2, 3},
R4 = {RecName, 3, 1},
R5 = {RecName, 104, 104},
W1 = fun(Tab,Search) ->
mnesia:write(Tab,R1,write),
mnesia:write(Tab,R2,write),
Search(Tab)
end,
S1 = lists:sort([R1, R2| Recs]),
?match(S1, sort_res(Trans(W1, [Tab1, Select]))),
?match(S1, sort_res(Trans(W1, [Tab1, Match]))),
?match(S1, sort_res(Trans(W1, [Tab1, SelLoop]))),
?match(S1, sort_res(Trans(W1, [Tab2, Select]))),
?match(S1, sort_res(Trans(W1, [Tab2, SelLoop]))),
?match(S1, sort_res(Trans(W1, [Tab2, Match]))),
?match(S1, sort_res(Trans(W1, [Tab3, Select]))),
?match(S1, sort_res(Trans(W1, [Tab3, SelLoop]))),
?match(S1, sort_res(Trans(W1, [Tab3, Match]))),
[mnesia:dirty_delete_object(Frag, R) || R <- [R1, R2],
Tab <- Tabs,
Frag <- frag_names(Tab)],
W2 = fun(Tab, Search) ->
mnesia:write(Tab, R3, write),
mnesia:write(Tab, R1, write),
Search(Tab)
end,
S2 = lists:sort([R1 | Recs]),
S2Bag = lists:sort([R1, R3 | Recs]),
io:format("S2 = ~p\n", [S2]),
?match(S2, sort_res(Trans(W2, [Tab1, Select]))),
?match(S2, sort_res(Trans(W2, [Tab1, SelLoop]))),
?match(S2, sort_res(Trans(W2, [Tab1, Match]))),
?match(S2, sort_res(Trans(W2, [Tab2, Select]))),
?match(S2, sort_res(Trans(W2, [Tab2, SelLoop]))),
?match(S2, sort_res(Trans(W2, [Tab2, Match]))),
io:format("S2Bag = ~p\n", [S2Bag]),
?match(S2Bag, sort_res(Trans(W2, [Tab3, Select]))),
?match(S2Bag, sort_res(Trans(W2, [Tab3, SelLoop]))),
?match(S2Bag, sort_res(Trans(W2, [Tab3, Match]))),
W3 = fun(Tab,Search) ->
mnesia:write(Tab, R4, write),
mnesia:delete(Tab, element(2, R1), write),
Search(Tab)
end,
S3Bag = lists:sort([R4 | lists:delete(R1, Recs)]),
S3 = lists:delete({RecName, 3, 3}, S3Bag),
?match(S3, sort_res(Trans(W3, [Tab1, Select]))),
?match(S3, sort_res(Trans(W3, [Tab1, SelLoop]))),
?match(S3, sort_res(Trans(W3, [Tab1, Match]))),
?match(S3, sort_res(Trans(W3, [Tab2, SelLoop]))),
?match(S3, sort_res(Trans(W3, [Tab2, Select]))),
?match(S3, sort_res(Trans(W3, [Tab2, Match]))),
?match(S3Bag, sort_res(Trans(W3, [Tab3, Select]))),
?match(S3Bag, sort_res(Trans(W3, [Tab3, SelLoop]))),
?match(S3Bag, sort_res(Trans(W3, [Tab3, Match]))),
W4 = fun(Tab,Search) ->
mnesia:delete(Tab, -1, write),
mnesia:delete(Tab, 4 , write),
mnesia:delete(Tab, 17, write),
mnesia:delete_object(Tab, {RecName, -1, x}, write),
mnesia:delete_object(Tab, {RecName, 4, x}, write),
mnesia:delete_object(Tab, {RecName, 42, x}, write),
mnesia:delete_object(Tab, R2, write),
mnesia:write(Tab, R5, write),
Search(Tab)
end,
S4Bag = lists:sort([R5 | S3Bag]),
S4 = lists:sort([R5 | S3]),
?match(S4, sort_res(Trans(W4, [Tab1, Select]))),
?match(S4, sort_res(Trans(W4, [Tab1, SelLoop]))),
?match(S4, sort_res(Trans(W4, [Tab1, Match]))),
?match(S4, sort_res(Trans(W4, [Tab2, Select]))),
?match(S4, sort_res(Trans(W4, [Tab2, SelLoop]))),
?match(S4, sort_res(Trans(W4, [Tab2, Match]))),
?match(S4Bag, sort_res(Trans(W4, [Tab3, Select]))),
?match(S4Bag, sort_res(Trans(W4, [Tab3, SelLoop]))),
?match(S4Bag, sort_res(Trans(W4, [Tab3, Match]))),
[mnesia:dirty_delete_object(Tab, R) || R <- [{RecName, 3, 3}, R5], Tab <- Tabs],
%% hmmm anything more??
?verify_mnesia([Node1], []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
evil_delete_db_node(doc) ->
["Delete db_node with a repicated table with foreign key"];
evil_delete_db_node(suite) -> [];
evil_delete_db_node(Config) when is_list(Config) ->
Nodes = lists:sort(?acquire_nodes(2, Config)),
Local = node(),
Remote = hd(Nodes -- [Local]),
Type = case mnesia_test_lib:diskless(Config) of
true -> n_ram_copies;
false -> n_disc_copies
end,
Tab = frag_master,
?match({atomic, ok}, mnesia:create_table(Tab, [{frag_properties, [{Type, 2}, {node_pool, Nodes}]}])),
ExtraTab = frag_foreigner,
?match({atomic, ok}, mnesia:create_table(ExtraTab, [{frag_properties, [{foreign_key, {Tab, key}}, {node_pool, Nodes}]}])),
GetPool = fun(T) ->
case lists:keysearch(node_pool, 1, mnesia:table_info (T, frag_properties)) of
{value, {node_pool, N}} -> lists:sort(N);
false -> []
end
end,
?match(Nodes, GetPool(Tab)),
?match(Nodes, GetPool(ExtraTab)),
?match(stopped, rpc:call(Remote, mnesia, stop, [])),
?match({atomic, ok}, mnesia:del_table_copy(schema, Remote)),
?match([Local], GetPool(Tab)),
?match([Local], GetPool(ExtraTab)),
?verify_mnesia([Local], []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Misc convenient helpers
frag_write(Tab, Rec) ->
Fun = fun() -> mnesia:write(Tab, Rec, write) end,
mnesia:activity(sync_dirty, Fun, mnesia_frag).
frag_dist(Tab) ->
Fun = fun() -> mnesia:table_info(Tab, frag_dist) end,
mnesia:activity(sync_dirty, Fun, mnesia_frag).
frag_names(Tab) ->
Fun = fun() -> mnesia:table_info(Tab, frag_names) end,
mnesia:activity(sync_dirty, Fun, mnesia_frag).
frag_rec_dist(Tab) ->
Fun = fun() -> mnesia:table_info(Tab, frag_size) end,
[Size || {_, Size} <- mnesia:activity(sync_dirty, Fun, mnesia_frag)].
sort_res(List) when is_list(List) ->
lists:sort(List);
sort_res(Else) ->
Else.