1: %% 2: %% %CopyrightBegin% 3: %% 4: %% Copyright Ericsson AB 1997-2013. All Rights Reserved. 5: %% 6: %% The contents of this file are subject to the Erlang Public License, 7: %% Version 1.1, (the "License"); you may not use this file except in 8: %% compliance with the License. You should have received a copy of the 9: %% Erlang Public License along with this software. If not, it can be 10: %% retrieved online at http://www.erlang.org/. 11: %% 12: %% Software distributed under the License is distributed on an "AS IS" 13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 14: %% the License for the specific language governing rights and limitations 15: %% under the License. 16: %% 17: %% %CopyrightEnd% 18: %% 19: 20: %% 21: -module(mnesia_isolation_test). 22: -author('hakan@erix.ericsson.se'). 23: 24: -compile([export_all]). 25: -include("mnesia_test_lib.hrl"). 26: 27: init_per_testcase(Func, Conf) -> 28: mnesia_test_lib:init_per_testcase(Func, Conf). 29: 30: end_per_testcase(Func, Conf) -> 31: mnesia_test_lib:end_per_testcase(Func, Conf). 32: 33: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 34: all() -> 35: [{group, locking}, {group, visibility}]. 36: 37: groups() -> 38: [{locking, [], 39: [no_conflict, simple_queue_conflict, 40: advanced_queue_conflict, simple_deadlock_conflict, 41: advanced_deadlock_conflict, lock_burst, 42: {group, sticky_locks}, {group, unbound_locking}, 43: {group, admin_conflict}, nasty]}, 44: {sticky_locks, [], [basic_sticky_functionality]}, 45: {unbound_locking, [], [unbound1, unbound2]}, 46: {admin_conflict, [], 47: [create_table, delete_table, move_table_copy, 48: add_table_index, del_table_index, transform_table, 49: snmp_open_table, snmp_close_table, 50: change_table_copy_type, change_table_access, 51: add_table_copy, del_table_copy, dump_tables, 52: {group, extra_admin_tests}]}, 53: {extra_admin_tests, [], 54: [del_table_copy_1, del_table_copy_2, del_table_copy_3, 55: add_table_copy_1, add_table_copy_2, add_table_copy_3, 56: add_table_copy_4, move_table_copy_1, move_table_copy_2, 57: move_table_copy_3, move_table_copy_4]}, 58: {visibility, [], 59: [dirty_updates_visible_direct, 60: dirty_reads_regardless_of_trans, 61: trans_update_invisibible_outside_trans, 62: trans_update_visible_inside_trans, write_shadows, 63: delete_shadows, write_delete_shadows_bag, 64: write_delete_shadows_bag2, {group, iteration}, 65: shadow_search, snmp_shadows]}, 66: {removed_resources, [], [rr_kill_copy]}, 67: {iteration, [], [foldl, first_next]}]. 68: 69: init_per_group(_GroupName, Config) -> 70: Config. 71: 72: end_per_group(_GroupName, Config) -> 73: Config. 74: 75: 76: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 77: 78: 79: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 80: 81: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 82: no_conflict(suite) -> []; 83: no_conflict(Config) when is_list(Config) -> 84: [Node1] = ?acquire_nodes(1, Config), 85: Tab = no_conflict, 86: create_conflict_table(Tab, [Node1]), 87: Fun = fun(OtherOid, Lock1, Lock2) -> 88: %% Start two transactions 89: {success, [B, A]} = ?start_activities([Node1, Node1]), 90: ?start_transactions([B, A]), 91: 92: A ! fun() -> Lock1(one_oid(Tab)), ok end, 93: ?match_receive({A, ok}), 94: B ! fun() -> Lock2(OtherOid), ok end, 95: ?match_receive({B, ok}), 96: A ! fun() -> mnesia:abort(ok) end, 97: ?match_receive({A, {aborted, ok}}), 98: B ! fun() -> mnesia:abort(ok) end, 99: ?match_receive({B, {aborted, ok}}) 100: end, 101: NoLocks = lock_funs(no_lock, any_granularity), 102: SharedLocks = lock_funs(shared_lock, any_granularity), 103: AnyLocks = lock_funs(any_lock, any_granularity), 104: OneOneFun = fun(Lock1, Lock2) -> Fun(one_oid(Tab), Lock1, Lock2) end, 105: fun_loop(OneOneFun, NoLocks, AnyLocks), 106: fun_loop(OneOneFun, AnyLocks, NoLocks), 107: fun_loop(OneOneFun, SharedLocks, SharedLocks), 108: 109: %% Lock different objects 110: OneOtherFun = fun(Lock1, Lock2) -> Fun(other_oid(Tab), Lock1, Lock2) end, 111: OneSharedLocks = lock_funs(shared_lock, one), 112: OneExclusiveLocks = lock_funs(exclusive_lock, one), 113: fun_loop(OneOtherFun, OneSharedLocks, OneExclusiveLocks), 114: fun_loop(OneOtherFun, OneExclusiveLocks, OneSharedLocks), 115: fun_loop(OneOtherFun, OneExclusiveLocks, OneExclusiveLocks), 116: ok. 117: 118: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 119: simple_queue_conflict(suite) -> []; 120: simple_queue_conflict(Config) when is_list(Config) -> 121: [Node1] = ?acquire_nodes(1, Config), 122: Tab = simple_queue_conflict, 123: create_conflict_table(Tab, [Node1]), 124: Fun = fun(OneLock, OtherLock) -> 125: %% Start two transactions 126: {success, [B, A]} = ?start_activities([Node1, Node1]), 127: ?start_transactions([B, A]), 128: 129: A ! fun() -> OneLock(one_oid(Tab)), ok end, 130: ?match_receive({A, ok}), 131: B ! fun() -> OtherLock(one_oid(Tab)), ok end, 132: wait_for_lock(B, [Node1], 20), % Max 10 sec 133: A ! end_trans, 134: ?match_multi_receive([{A, {atomic, end_trans}}, {B, ok}]), 135: B ! fun() -> mnesia:abort(ok) end, 136: ?match_receive({B, {aborted, ok}}) 137: end, 138: OneSharedLocks = lock_funs(shared_lock, one), 139: AllSharedLocks = lock_funs(shared_lock, all), 140: OneExclusiveLocks = lock_funs(exclusive_lock, one), 141: AllExclusiveLocks = lock_funs(exclusive_lock, all), 142: fun_loop(Fun, OneExclusiveLocks, OneExclusiveLocks), 143: fun_loop(Fun, AllExclusiveLocks, AllExclusiveLocks), 144: fun_loop(Fun, OneExclusiveLocks, AllExclusiveLocks), 145: fun_loop(Fun, AllExclusiveLocks, OneExclusiveLocks), 146: fun_loop(Fun, OneSharedLocks, AllExclusiveLocks), 147: fun_loop(Fun, AllSharedLocks, OneExclusiveLocks), 148: ok. 149: 150: wait_for_lock(Pid, _Nodes, 0) -> 151: Queue = mnesia:system_info(lock_queue), 152: ?error("Timeout while waiting for lock on Pid ~p in queue ~p~n", [Pid, Queue]); 153: wait_for_lock(Pid, Nodes, N) -> 154: rpc:multicall(Nodes, sys, get_status, [mnesia_locker]), 155: List = [rpc:call(Node, mnesia, system_info, [lock_queue]) || Node <- Nodes], 156: Q = lists:append(List), 157: check_q(Pid, Q, Nodes, N). 158: 159: check_q(Pid, [{_Oid, _Op, Pid, _Tid, _WFT} | _Tail], _N, _Count) -> ok; 160: check_q(Pid, [_ | Tail], N, Count) -> check_q(Pid, Tail, N, Count); 161: check_q(Pid, [], N, Count) -> 162: timer:sleep(500), 163: wait_for_lock(Pid, N, Count - 1). 164: 165: 166: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 167: advanced_queue_conflict(suite) -> []; 168: advanced_queue_conflict(Config) when is_list(Config) -> 169: [Node1] = ?acquire_nodes(1, Config), 170: Tab = advanced_queue_conflict, 171: create_conflict_table(Tab, [Node1]), 172: OneRec = {Tab, 3, 3}, 173: OneOid = {Tab, 3}, 174: OtherRec = {Tab, 4, 4}, 175: OtherOid = {Tab, 4}, 176: 177: %% Start four transactions 178: {success, [D, C, B, A]} = ?start_activities(lists:duplicate(4, Node1)), 179: ?start_transactions([D, C, B, A]), 180: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 181: ?match([], mnesia:system_info(held_locks)), 182: ?match([], mnesia:system_info(lock_queue)), 183: 184: %% Acquire some locks 185: A ! fun() -> mnesia:write(OneRec) end, 186: ?match_receive({A, ok}), 187: A ! fun() -> mnesia:read(OneOid) end, 188: ?match_receive({A, [OneRec]}), 189: 190: B ! fun() -> mnesia:write(OtherRec) end, 191: ?match_receive({B, ok}), 192: B ! fun() -> mnesia:read(OneOid) end, 193: ?match_receive(timeout), 194: 195: C ! fun() -> mnesia:read(OtherOid) end, 196: ?match_receive(timeout), 197: D ! fun() -> mnesia:wread(OtherOid) end, 198: ?match_receive(timeout), 199: 200: %% and release them in a certain order 201: A ! end_trans, 202: ?match_multi_receive([{A, {atomic, end_trans}}, {B, [OneRec]}]), 203: B ! end_trans, 204: ?match_multi_receive([{B, {atomic, end_trans}}, {C, [OtherRec]}]), 205: C ! end_trans, 206: ?match_multi_receive([{C, {atomic, end_trans}}, {D, [OtherRec]}]), 207: D ! end_trans, 208: ?match_receive({D, {atomic, end_trans}}), 209: 210: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 211: ?match([], mnesia:system_info(held_locks)), 212: ?match([], mnesia:system_info(lock_queue)), 213: ok. 214: 215: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 216: simple_deadlock_conflict(suite) -> []; 217: simple_deadlock_conflict(Config) when is_list(Config) -> 218: [Node1] = ?acquire_nodes(1, Config), 219: Tab = simple_deadlock_conflict, 220: create_conflict_table(Tab, [Node1]), 221: Rec = {Tab, 4, 4}, 222: Oid = {Tab, 4}, 223: 224: %% Start two transactions 225: {success, [B, A]} = ?start_activities(lists:duplicate(2, Node1)), 226: mnesia_test_lib:start_transactions([B, A], 0), % A is newest 227: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 228: ?match([], mnesia:system_info(held_locks)), 229: ?match([], mnesia:system_info(lock_queue)), 230: 231: B ! fun() -> mnesia:write(Rec) end, 232: ?match_receive({B, ok}), 233: A ! fun() -> mnesia:read(Oid) end, 234: ?match_receive({A, {aborted, nomore}}), 235: B ! end_trans, 236: ?match_receive({B, {atomic, end_trans}}), 237: 238: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 239: ?match([], mnesia:system_info(held_locks)), 240: ?match([], mnesia:system_info(lock_queue)), 241: ok. 242: 243: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 244: advanced_deadlock_conflict(suite) -> []; 245: advanced_deadlock_conflict(Config) when is_list(Config) -> 246: [Node1, Node2] = ?acquire_nodes(2, Config), 247: Tab = advanced_deadlock_conflict, 248: create_conflict_table(Tab, [Node2]), 249: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 250: Rec = {Tab, 4, 4}, 251: Oid = {Tab, 4}, 252: 253: %% Start two transactions 254: {success, [B, A]} = ?start_activities([Node1, Node2]), 255: mnesia_test_lib:start_sync_transactions([B, A], 0), % A is newest 256: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 257: ?match([], mnesia:system_info(held_locks)), 258: ?match([], mnesia:system_info(lock_queue)), 259: 260: B ! fun() -> mnesia:write(Rec) end, 261: ?match_receive({B, ok}), 262: A ! fun() -> mnesia:read(Oid) end, 263: ?match_receive({A, {aborted, nomore}}), 264: B ! end_trans, 265: ?match_receive({B, {atomic, end_trans}}), 266: 267: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 268: ?match([], mnesia:system_info(held_locks)), 269: ?match([], mnesia:system_info(lock_queue)), 270: ok. 271: 272: one_oid(Tab) -> {Tab, 1}. 273: other_oid(Tab) -> {Tab, 2}. 274: 275: create_conflict_table(Tab, Nodes) -> 276: ?match({atomic, ok}, mnesia:create_table([{name, Tab}, 277: {ram_copies, Nodes}, 278: {attributes, [key, val]}, 279: {index, [val]} 280: ])), 281: ?match([], mnesia_test_lib:sync_tables(Nodes, [Tab])), 282: init_conflict_table(Tab). 283: 284: init_conflict_table(Tab) -> 285: Recs = mnesia:dirty_match_object({Tab, '_', '_'}), 286: lists:foreach(fun(R) -> mnesia:dirty_delete_object(R) end, Recs), 287: Keys = [one_oid(Tab), other_oid(Tab)], 288: [mnesia:dirty_write({T, K, K}) || {T, K} <- Keys]. 289: 290: %% Apply Fun for each X and Y 291: fun_loop(Fun, Xs, Ys) -> 292: lists:foreach(fun(X) -> lists:foreach(fun(Y) -> do_fun(Fun, X, Y) end, Ys) end, Xs). 293: 294: do_fun(Fun, X, Y) -> 295: Pid = spawn_link(?MODULE, do_fun, [self(), Fun, X, Y]), 296: receive 297: {done_fun, Pid} -> done_fun 298: end. 299: 300: do_fun(Monitor, Fun, X, Y) -> 301: ?log("{do_fun ~p~n", [[Fun, X, Y]]), 302: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 303: ?match([], mnesia:system_info(held_locks)), 304: ?match([], mnesia:system_info(lock_queue)), 305: Fun(X, Y), 306: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 307: ?match([], mnesia:system_info(held_locks)), 308: ?match([], mnesia:system_info(lock_queue)), 309: unlink(Monitor), 310: Monitor ! {done_fun, self()}, 311: exit(done_fun). 312: 313: %% Returns a list of fun's 314: lock_funs(no_lock, one) -> 315: [ 316: fun(Oid) -> mnesia:dirty_read(Oid) end, 317: fun({Tab, Key}) -> mnesia:dirty_write({Tab, Key, Key}) end, 318: fun({Tab, Key}) -> mnesia:dirty_write({Tab, Key, Key}), 319: mnesia:dirty_update_counter({Tab, Key}, 0) end, 320: fun(Oid) -> mnesia:dirty_delete(Oid) end, 321: fun({Tab, Key}) -> mnesia:dirty_delete_object({Tab, Key, Key}) end, 322: fun({Tab, Key}) -> mnesia:dirty_match_object({Tab, Key, Key}) end, 323: fun({Tab, Key}) -> mnesia:dirty_index_match_object({Tab, Key, Key}, val) end, 324: fun({Tab, Key}) -> mnesia:dirty_index_read(Tab, Key, val) end, 325: fun({Tab, Key}) -> mnesia:dirty_index_match_object({Tab, '_', Key}, val) end 326: ]; 327: lock_funs(no_lock, all) -> 328: [ 329: fun({Tab, _}) -> mnesia:dirty_match_object({Tab, '_', '_'}) end, 330: fun({Tab, _}) -> slot_iter(Tab) end, 331: fun({Tab, _}) -> key_iter(Tab) end 332: ]; 333: lock_funs(shared_lock, one) -> 334: 335: [ 336: fun(Oid) -> mnesia:read(Oid) end, 337: fun({Tab, Key}) -> 338: init_conflict_table(Tab), 339: mnesia:dirty_delete(other_oid(Tab)), 340: mnesia:match_object({Tab, Key, Key}) end 341: ]; 342: lock_funs(shared_lock, all) -> 343: [ 344: fun({Tab, _}) -> mnesia:read_lock_table(Tab) end, 345: fun({Tab, Key}) -> mnesia:match_object({Tab, '_', Key}) end, 346: fun({Tab, _}) -> mnesia:match_object({Tab, '_', '_'}) end, 347: fun({Tab, _}) -> mnesia:all_keys(Tab) end, 348: fun({Tab, Key}) -> mnesia:index_match_object({Tab, '_', Key}, val) end, 349: fun({Tab, Key}) -> mnesia:index_read(Tab, Key, val) end 350: ]; 351: lock_funs(exclusive_lock, one) -> 352: [ 353: fun(Oid) -> mnesia:wread(Oid) end, 354: fun({Tab, Key}) -> mnesia:write({Tab, Key, Key}) end, 355: fun(Oid) -> mnesia:delete(Oid) end, 356: fun({Tab, Key}) -> mnesia:delete_object({Tab, Key, Key}) end, 357: fun({Tab, Key}) -> mnesia:s_write({Tab, Key, Key}) end, 358: fun(Oid) -> mnesia:s_delete(Oid) end, 359: fun({Tab, Key}) -> mnesia:s_delete_object({Tab, Key, Key}) end 360: ]; 361: lock_funs(exclusive_lock, all) -> 362: [ 363: fun({Tab, _}) -> mnesia:write_lock_table(Tab) end 364: ]; 365: lock_funs(Compatibility, any_granularity) -> 366: lists:append([lock_funs(Compatibility, Granularity) || 367: Granularity <- [one, all]]); 368: lock_funs(any_lock, Granularity) -> 369: lists:append([lock_funs(Compatibility, Granularity) || 370: Compatibility <- [no_lock, shared_lock, exclusive_lock]]). 371: 372: slot_iter(Tab) -> 373: slot_iter(Tab, mnesia:dirty_slot(Tab, 0), 1). 374: slot_iter(_Tab, '$end_of_table', _) -> 375: []; 376: slot_iter(Tab, Recs, Slot) -> 377: Recs ++ slot_iter(Tab, mnesia:dirty_slot(Tab, Slot), Slot+1). 378: 379: key_iter(Tab) -> 380: key_iter(Tab, mnesia:dirty_first(Tab)). 381: key_iter(_Tab, '$end_of_table') -> 382: []; 383: key_iter(Tab, Key) -> 384: [Key | key_iter(Tab, mnesia:dirty_next(Tab, Key))]. 385: 386: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 387: lock_burst(suite) -> []; 388: lock_burst(Config) when is_list(Config) -> 389: [Node1] = ?acquire_nodes(1, Config), 390: Tab = burst, 391: ?match({atomic, ok}, mnesia:create_table(Tab, 392: [{attributes, [a, b]}, 393: {ram_copies, [Node1]}])), 394: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 395: ?match([], mnesia:system_info(held_locks)), 396: ?match([], mnesia:system_info(lock_queue)), 397: ?match(ok, burst_em(Tab, 1000)), 398: ?match([{burst,1,1000}], mnesia:dirty_read(Tab,1)), 399: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 400: ?match([], mnesia:system_info(held_locks)), 401: ?match([], mnesia:system_info(lock_queue)), 402: ok. 403: 404: burst_em(Tab, N) -> 405: spawn_link(?MODULE, burst_counter, [self(), Tab, N]), 406: receive 407: burst_counter_done -> ok 408: end. 409: 410: burst_counter(Monitor, Tab, N) when N > 0 -> 411: ?match(ok, burst_gen(Tab, N, self())), 412: Monitor ! burst_receiver(N). 413: 414: burst_receiver(0) -> 415: burst_counter_done; 416: burst_receiver(N) -> 417: receive 418: burst_incr_done -> 419: burst_receiver(N-1) 420: end. 421: 422: burst_gen(_, 0, _) -> 423: ok; 424: burst_gen(Tab, N, Father) when is_integer(N), N > 0 -> 425: spawn_link(?MODULE, burst_incr, [Tab, Father]), 426: burst_gen(Tab, N-1, Father). 427: 428: burst_incr(Tab, Father) -> 429: Fun = fun() -> 430: Val = 431: case mnesia:read({Tab, 1}) of 432: [{Tab, 1, V}] -> V; 433: [] -> 0 434: end, 435: mnesia:write({Tab, 1, Val+1}) 436: end, 437: ?match({atomic, ok}, mnesia:transaction(Fun)), 438: Father ! burst_incr_done. 439: 440: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 441: 442: basic_sticky_functionality(suite) -> []; 443: basic_sticky_functionality(Config) when is_list(Config) -> 444: [N1, N2] = Nodes = ?acquire_nodes(2, Config), 445: Tab = basic_table, 446: Storage = mnesia_test_lib:storage_type(disc_copies, Config), 447: ?match({atomic, ok}, mnesia:create_table(Tab, [{Storage, Nodes}])), 448: ?match({atomic, ok}, mnesia:create_table(sync, [{ram_copies, Nodes}])), 449: Trans1 = fun() -> 450: ?match(ok, mnesia:s_write({Tab, 1, 2})), 451: ?match([{Tab, 1, 2}], mnesia:read({Tab, 1})), 452: ?match(timeout, receive M -> M after 500 -> timeout end), 453: ?match(ok, mnesia:s_write({Tab, 2, 2})), 454: ?match(ok, mnesia:write({Tab, 42, 4711})) 455: end, 456: Trans2 = fun() -> 457: ?match([{Tab, 1, 2}], mnesia:read({Tab, 1})), 458: ?match(timeout, receive M -> M after 500 -> timeout end), 459: ?match(ok, mnesia:write({Tab, 1, 4711})), 460: ?match(ok, mnesia:s_write({Tab, 2, 4})), 461: ?match(ok, mnesia:delete({Tab, 42})) 462: end, 463: rpc:call(N1, mnesia, transaction, [Trans1]), 464: ?match([{Tab,N1}], rpc:call(N1, ?MODULE, get_sticky, [])), 465: ?match([{Tab,N1}], rpc:call(N2, ?MODULE, get_sticky, [])), 466: 467: rpc:call(N2, mnesia, transaction, [Trans2]), 468: ?match([], rpc:call(N1, ?MODULE, get_sticky, [])), 469: ?match([], rpc:call(N2, ?MODULE, get_sticky, [])), 470: 471: Slock = fun() -> mnesia:read({sync,sync}),get_sticky() end, 472: ?match({atomic, [{Tab,1, 4711}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)), 473: ?match({atomic, [{Tab,2, 4}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 2}) end)), 474: ?match({atomic, [{Tab,N1}]}, rpc:call(N1, mnesia, transaction, 475: [fun() -> mnesia:s_write({Tab, 1, 3}),Slock() end])), 476: ?match([{Tab,N1}], rpc:call(N2, ?MODULE, get_sticky, [])), 477: 478: ?match({atomic,[]}, rpc:call(N2, mnesia, transaction, 479: [fun() -> mnesia:s_write({Tab, 1, 4}),Slock() end])), 480: 481: ?match([], rpc:call(N1, ?MODULE, get_sticky, [])), 482: ?match([], rpc:call(N2, ?MODULE, get_sticky, [])), 483: 484: ?match({atomic,[{Tab,N2}]}, rpc:call(N2, mnesia, transaction, 485: [fun() -> mnesia:s_write({Tab, 1, 4}),Slock() end])), 486: 487: ?match({atomic,[]}, rpc:call(N1, mnesia, transaction, 488: [fun() -> mnesia:s_write({Tab, 1, 5}),Slock() end])), 489: ?match({atomic,[{Tab,N1}]}, rpc:call(N1, mnesia, transaction, 490: [fun() -> mnesia:s_write({Tab, 1, 5}),Slock() end])), 491: ?match({atomic,[]}, rpc:call(N2, mnesia, transaction, 492: [fun() -> mnesia:s_write({Tab, 1, 6}),Slock() end])), 493: ?match({atomic,[{Tab,N2}]}, rpc:call(N2, mnesia, transaction, 494: [fun() -> mnesia:s_write({Tab, 1, 7}),Slock() end])), 495: 496: ?match([{Tab,N2}], get_sticky()), 497: ?match({atomic, [{Tab,1, 7}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)), 498: ?match([{Tab,N2}], get_sticky()), 499: ?match({atomic, [{Tab,2, 4}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 2}) end)), 500: ?match([{Tab,N2}], get_sticky()), 501: ?match({atomic,[{Tab,N2}]}, rpc:call(N2, mnesia, transaction, 502: [fun() -> mnesia:s_write({Tab, 1, 6}),Slock() end])), 503: ?match([{Tab,N2}], get_sticky()), 504: ?match({atomic, [{Tab,1, 6}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)), 505: ?match([{Tab,N2}], get_sticky()), 506: ?match({atomic, [{Tab,2, 4}]}, mnesia:transaction(fun() -> mnesia:read({Tab, 2}) end)), 507: ?match([{Tab,N2}], get_sticky()), 508: ?verify_mnesia(Nodes, []). 509: 510: get_sticky() -> 511: mnesia_locker ! {get_table, self(), mnesia_sticky_locks}, 512: receive {mnesia_sticky_locks, Locks} -> Locks end. 513: 514: get_held() -> 515: mnesia_locker ! {get_table, self(), mnesia_sticky_locks}, 516: receive {mnesia_sticky_locks, Locks} -> Locks end. 517: 518: 519: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 520: 521: 522: unbound1(suite) -> []; 523: unbound1(Config) when is_list(Config) -> 524: [Node1] = ?acquire_nodes(1, Config), 525: 526: ?match({atomic, ok}, mnesia:create_table(ul, [])), 527: 528: Tester = self(), 529: Write = fun() -> 530: mnesia:write({ul, {key, {17,42}}, val}), 531: ?log("~p Got write lock waiting...~n", [self()]), 532: Tester ! continue, 533: receive 534: continue -> 535: ok 536: end, 537: ?log("..continuing~n", []), 538: ok 539: end, 540: 541: {success, [A]} = ?start_activities([Node1]), 542: ?start_transactions([A]), 543: A ! Write, 544: 545: receive continue -> ok end, 546: 547: Match = fun() -> 548: case catch mnesia:match_object({ul, {key, {'_', '$0'}}, '_'}) of 549: {'EXIT', What} -> %% Cyclic first time 550: ?log("Cyclic Restarting~n", []), 551: A ! continue, 552: A ! end_trans, 553: exit(What); 554: Res -> 555: ?log("Got match log ~p...~n", [Res]), 556: Res 557: end 558: end, 559: ?match({atomic, [{ul,{key,{17,42}},val}]}, mnesia:transaction(Match)), 560: 561: ?match_receive({A, ok}), 562: ?match_receive({A, {atomic, end_trans}}), 563: ok. 564: 565: unbound2(suite) -> []; 566: unbound2(Config) when is_list(Config) -> 567: [Node1] = ?acquire_nodes(1, Config), 568: 569: ?match({atomic, ok}, mnesia:create_table(ul, [])), 570: 571: {success, [B, A]} = ?start_activities([Node1, Node1]), 572: 573: Me = self(), 574: 575: Write = fun() -> 576: mnesia:write({ul, {key, {17,42}}, val}), 577: ?log("~p Got write lock waiting... Tid ~p ~n", 578: [self(), get(mnesia_activity_state)]), 579: Me ! ok_lock, 580: receive 581: continue -> 582: ok 583: end, 584: ?log("..continuing~n", []), 585: ok 586: end, 587: 588: Match = fun() -> 589: receive 590: continueB -> 591: ?log("~p, moving on TID ~p~n", 592: [self(), get(mnesia_activity_state)]), 593: Me ! {self(), continuing} 594: end, 595: case catch mnesia:match_object({ul, {key, {'_', '$0'}}, 596: '_'}) of 597: {'EXIT', What} -> %% Cyclic first time 598: ?log("Cyclic Restarting ~p ~n", [What]), 599: {should_not_happen,What}; 600: Res -> 601: ?log("Got match log ~p...~n", [Res]), 602: Res 603: end 604: end, 605: 606: B ! fun() -> mnesia:transaction(Match) end, 607: timer:sleep(100), %% Let B be started first.. 608: A ! fun() -> mnesia:transaction(Write) end, 609: 610: receive ok_lock -> ok end, 611: 612: B ! continueB, 613: ?match_receive({B, continuing}), 614: 615: %% B should now be in lock queue. 616: A ! continue, 617: ?match_multi_receive([{A, {atomic, ok}}, 618: {B, {atomic, [{ul,{key,{17,42}},val}]}}]), 619: ok. 620: 621: receiver() -> 622: receive 623: {_Pid, begin_trans} -> 624: receiver(); 625: Else -> 626: Else 627: after 628: 10000 -> 629: timeout 630: end. 631: 632: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 633: 634: create_table(suite) -> []; 635: create_table(Config) when is_list(Config) -> 636: [ThisNode, Node2] = ?acquire_nodes(2, Config), 637: Tab = c_t_tab, 638: Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}], 639: ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 640: insert(Tab, 50), 641: {success, [A]} = ?start_activities([ThisNode]), 642: mnesia_test_lib:start_sync_transactions([A], 0), 643: 644: A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end, 645: ?match_receive({A, ok}), %% A is executed 646: 647: DiskMaybe = mnesia_test_lib:storage_type(disc_copies, Config), 648: 649: Pid = spawn_link(?MODULE, op, [self(), mnesia, create_table, 650: [test_tab1, [{DiskMaybe, [ThisNode]}]]]), 651: ?match_multi_receive([{Pid, {atomic, ok}}, 652: {'EXIT', Pid, normal}]), %% No Locks! op should be exec. 653: 654: Pid2 = spawn_link(?MODULE, op, [self(), mnesia, create_table, 655: [test_tab2, [{ram_copies, [Node2]}]]]), 656: 657: ?match_multi_receive([{Pid2, {atomic, ok}}, 658: {'EXIT', Pid2, normal}]), %% No Locks! op should be exec. 659: 660: A ! end_trans, 661: ?match_receive({A,{atomic,end_trans}}), 662: 663: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 664: ?match([], mnesia:system_info(held_locks)), 665: ?match([], mnesia:system_info(lock_queue)), 666: ok. 667: 668: delete_table(suite) -> []; 669: delete_table(Config) when is_list(Config) -> 670: [ThisNode, Node2] = ?acquire_nodes(2, Config), 671: Tab = d_t_tab, 672: Def = [{ram_copies, [ThisNode, Node2]}, {attributes, [key, attr1, attr2]}], 673: ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 674: insert(Tab, 50), 675: {success, [A]} = ?start_activities([ThisNode]), 676: mnesia_test_lib:start_sync_transactions([A], 0), 677: 678: A ! fun() -> mnesia:read({Tab, 1}) end, 679: ?match_receive({A, [{Tab, 1, 1, 0}]}), %% A is executed 680: 681: Pid = spawn_link(?MODULE, op, [self(), mnesia, delete_table, 682: [Tab]]), 683: 684: ?match_receive(timeout), %% op waits for locks occupied by A 685: 686: A ! end_trans, %% Kill A, locks should be released 687: ?match_receive({A,{atomic,end_trans}}), 688: 689: receive 690: Msg -> ?match({Pid, {atomic, ok}}, Msg) 691: after 692: timer:seconds(20) -> ?error("Operation timed out", []) 693: end, 694: 695: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 696: ?match([], mnesia:system_info(held_locks)), 697: ?match([], mnesia:system_info(lock_queue)), 698: ok. 699: 700: move_table_copy(suite) -> []; 701: move_table_copy(Config) when is_list(Config) -> 702: [ThisNode, Node2] = ?acquire_nodes(2, Config), 703: Tab = m_t_c_tab, 704: Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}], 705: ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 706: insert(Tab, 50), 707: {success, [A]} = ?start_activities([ThisNode]), 708: mnesia_test_lib:start_sync_transactions([A], 0), 709: 710: A ! fun() -> mnesia:write({Tab, 1, 2, 3}) end, 711: ?match_receive({A, ok}), %% A is executed 712: 713: Pid = spawn_link(?MODULE, op, [self(), mnesia, move_table_copy, 714: [Tab, ThisNode, Node2]]), 715: 716: ?match_receive(timeout), %% op waits for locks occupied by A 717: 718: A ! end_trans, %% Kill A, locks should be released 719: ?match_receive({A,{atomic,end_trans}}), 720: 721: receive 722: Msg -> ?match({Pid, {atomic, ok}}, Msg) 723: after 724: timer:seconds(20) -> ?error("Operation timed out", []) 725: end, 726: 727: timer:sleep(500), %% Don't know how to sync this !!! 728: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 729: sys:get_status(whereis(mnesia_tm)), % Explicit sync, release locks is async 730: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 731: ?match([], mnesia:system_info(held_locks)), 732: ?match([], mnesia:system_info(lock_queue)), 733: ok. 734: 735: add_table_index(suite) -> []; 736: add_table_index(Config) when is_list(Config) -> 737: [ThisNode, _Node2] = ?acquire_nodes(2, Config ++ [{tc_timeout, 60000}]), 738: Tab = a_t_i_tab, 739: Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}], 740: ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 741: insert(Tab, 50), 742: {success, [A]} = ?start_activities([ThisNode]), 743: mnesia_test_lib:start_sync_transactions([A], 0), 744: 745: A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end, 746: ?match_receive({A, ok}), %% A is executed 747: 748: Pid = spawn_link(?MODULE, op, [self(), mnesia, 749: add_table_index, [Tab, attr1]]), 750: 751: ?match_receive(timeout), %% op waits for locks occupied by A 752: 753: A ! end_trans, %% Kill A, locks should be released 754: ?match_receive({A,{atomic,end_trans}}), 755: 756: receive 757: Msg -> ?match({Pid, {atomic, ok}}, Msg) 758: after 759: timer:seconds(20) -> ?error("Operation timed out", []) 760: end, 761: 762: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 763: ?match([], mnesia:system_info(held_locks)), 764: ?match([], mnesia:system_info(lock_queue)), 765: ok. 766: 767: del_table_index(suite) -> []; 768: del_table_index(Config) when is_list(Config) -> 769: [ThisNode, _Node2] = ?acquire_nodes(2, Config), 770: Tab = d_t_i_tab, 771: Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}], 772: ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 773: insert(Tab, 50), 774: ?match({atomic, ok}, mnesia:add_table_index(Tab, attr1)), 775: 776: {success, [A]} = ?start_activities([ThisNode]), 777: mnesia_test_lib:start_sync_transactions([A], 0), 778: 779: A ! fun() -> mnesia:write({Tab, 51, 51, attr2}) end, 780: ?match_receive({A, ok}), %% A is executed 781: 782: Pid = spawn_link(?MODULE, op, [self(), mnesia, del_table_index, 783: [Tab, attr1]]), 784: 785: ?match_receive(timeout), %% op waits for locks occupied by A 786: 787: A ! end_trans, %% Kill A, locks should be released 788: ?match_receive({A,{atomic,end_trans}}), 789: %% Locks released! op should be exec. 790: receive 791: Msg -> ?match({Pid, {atomic, ok}}, Msg) 792: after 793: timer:seconds(20) -> ?error("Operation timed out", []) 794: end, 795: 796: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 797: ?match([], mnesia:system_info(held_locks)), 798: ?match([], mnesia:system_info(lock_queue)), 799: ok. 800: 801: transform_table(suite) -> []; 802: transform_table(Config) when is_list(Config) -> 803: [ThisNode, Node2] = ?acquire_nodes(2, Config), 804: Tab = t_t_tab, 805: Def = [{ram_copies, [ThisNode, Node2]}, {attributes, [key, attr1, attr2]}], 806: ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 807: insert(Tab, 50), 808: {success, [A]} = ?start_activities([ThisNode]), 809: mnesia_test_lib:start_sync_transactions([A], 0), 810: 811: A ! fun() -> mnesia:read({Tab, 1}) end, 812: ?match_receive({A, [{Tab, 1, 1, 0}]}), %% A is executed 813: 814: Transform = fun({Table, Key, Attr1, Attr2}) -> % Need todo a transform 815: {Table, Key, {Attr1, Attr2}} end, 816: Pid = spawn_link(?MODULE, op, [self(), mnesia, transform_table, 817: [Tab, Transform, [key, attr1]]]), 818: ?match_receive(timeout), %% op waits for locks occupied by A 819: 820: A ! end_trans, %% Kill A, locks should be released 821: ?match_receive({A,{atomic,end_trans}}), 822: 823: receive 824: Msg -> ?match({Pid, {atomic, ok}}, Msg) 825: after 826: timer:seconds(20) -> ?error("Operation timed out", []) 827: end, 828: 829: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 830: ?match([], mnesia:system_info(held_locks)), 831: ?match([], mnesia:system_info(lock_queue)), 832: ok. 833: 834: snmp_open_table(suite) -> []; 835: snmp_open_table(Config) when is_list(Config) -> 836: [ThisNode, _Node2] = ?acquire_nodes(2, Config), 837: Tab = s_o_t_tab, 838: Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}], 839: ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 840: insert(Tab, 50), 841: {success, [A]} = ?start_activities([ThisNode]), 842: mnesia_test_lib:start_sync_transactions([A], 0), 843: 844: A ! fun() -> mnesia:write({Tab, 1, 1, 100}) end, 845: ?match_receive({A, ok}), %% A is executed 846: 847: Pid = spawn_link(?MODULE, op, [self(), mnesia, snmp_open_table, 848: [Tab, [{key, integer}]]]), 849: 850: ?match_receive(timeout), %% op waits for locks occupied by A 851: 852: A ! end_trans, %% Kill A, locks should be released 853: ?match_receive({A,{atomic,end_trans}}), 854: 855: %% Locks released! op should be exec. Can take a while (thats the timeout) 856: receive 857: Msg -> ?match({Pid, {atomic, ok}}, Msg) 858: after 859: timer:seconds(20) -> ?error("Operation timed out", []) 860: end, 861: 862: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 863: ?match([], mnesia:system_info(held_locks)), 864: ?match([], mnesia:system_info(lock_queue)), 865: ok. 866: 867: snmp_close_table(suite) -> []; 868: snmp_close_table(Config) when is_list(Config) -> 869: [ThisNode, _Node2] = ?acquire_nodes(2, Config), 870: Tab = s_c_t_tab, 871: Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}], 872: ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 873: ?match({atomic, ok}, mnesia:snmp_open_table(Tab, [{key, integer}])), 874: insert(Tab, 50), 875: {success, [A]} = ?start_activities([ThisNode]), 876: mnesia_test_lib:start_sync_transactions([A], 0), 877: 878: A ! fun() -> mnesia:write({Tab, 1, 1, 100}) end, 879: ?match_receive({A, ok}), %% A is executed 880: 881: Pid = spawn_link(?MODULE, op, [self(), mnesia, snmp_close_table, [Tab]]), 882: ?match_receive(timeout), %% op waits for locks occupied by A 883: 884: A ! end_trans, %% Kill A, locks should be released 885: ?match_receive({A,{atomic,end_trans}}), 886: 887: %% Locks released! op should be exec. Can take a while (thats the timeout) 888: receive 889: Msg -> ?match({Pid, {atomic, ok}}, Msg) 890: after 891: timer:seconds(20) -> ?error("Operation timed out", []) 892: end, 893: 894: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 895: ?match([], mnesia:system_info(held_locks)), 896: ?match([], mnesia:system_info(lock_queue)), 897: ok. 898: 899: change_table_copy_type(suite) -> []; 900: change_table_copy_type(Config) when is_list(Config) -> 901: [ThisNode, _Node2] = ?acquire_nodes(2, Config), 902: Tab = c_t_c_t_tab, 903: Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}], 904: ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 905: insert(Tab, 50), 906: {success, [A]} = ?start_activities([ThisNode]), 907: mnesia_test_lib:start_sync_transactions([A], 0), 908: A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end, 909: ?match_receive({A, ok}), %% A is executed 910: 911: Pid = spawn_link(?MODULE, op, [self(), mnesia, change_table_copy_type, 912: [Tab, ThisNode, disc_copies]]), 913: 914: ?match_receive(timeout), %% op waits for locks occupied by A 915: 916: A ! end_trans, %% Kill A, locks should be released 917: ?match_receive({A,{atomic,end_trans}}), 918: 919: receive 920: Msg -> ?match({Pid, {atomic, ok}}, Msg) 921: after 922: timer:seconds(20) -> ?error("Operation timed out", []) 923: end, 924: 925: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 926: ?match([], mnesia:system_info(held_locks)), 927: ?match([], mnesia:system_info(lock_queue)), 928: ok. 929: 930: change_table_access(suite) -> []; 931: change_table_access(Config) when is_list(Config) -> 932: [ThisNode, _Node2] = ?acquire_nodes(2, Config), 933: Tab = c_t_a_tab, 934: Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}], 935: ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 936: insert(Tab, 50), 937: {success, [A]} = ?start_activities([ThisNode]), 938: mnesia_test_lib:start_sync_transactions([A], 0), 939: 940: A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end, 941: ?match_receive({A, ok}), %% A is executed 942: 943: Pid = spawn_link(?MODULE, op, [self(), mnesia, change_table_access_mode, 944: [Tab, read_only]]), 945: 946: 947: ?match_receive(timeout), %% op waits for locks occupied by A 948: 949: A ! end_trans, %% Kill A, locks should be released 950: ?match_receive({A,{atomic,end_trans}}), 951: 952: receive 953: Msg -> ?match({Pid, {atomic, ok}}, Msg) 954: after 955: timer:seconds(20) -> ?error("Operation timed out", []) 956: end, 957: 958: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 959: ?match([], mnesia:system_info(held_locks)), 960: ?match([], mnesia:system_info(lock_queue)), 961: ok. 962: 963: add_table_copy(suite) -> []; 964: add_table_copy(Config) when is_list(Config) -> 965: [ThisNode, Node2] = ?acquire_nodes(2, Config), 966: Tab = a_t_c_tab, 967: Def = [{ram_copies, [ThisNode]}, {attributes, [key, attr1, attr2]}], 968: ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 969: insert(Tab, 50), 970: {success, [A]} = ?start_activities([ThisNode]), 971: mnesia_test_lib:start_sync_transactions([A], 0), 972: 973: A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end, 974: ?match_receive({A, ok}), %% A is executed 975: 976: Pid = spawn_link(?MODULE, op, [self(), mnesia, add_table_copy, 977: [Tab, Node2, ram_copies]]), 978: 979: ?match_receive(timeout), %% op waits for locks occupied by A 980: 981: A ! end_trans, %% Kill A, locks should be released 982: ?match_receive({A,{atomic,end_trans}}), 983: 984: receive 985: Msg -> ?match({Pid, {atomic, ok}}, Msg) 986: after 987: timer:seconds(20) -> ?error("Operation timed out", []) 988: end, 989: 990: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 991: ?match([], mnesia:system_info(held_locks)), 992: ?match([], mnesia:system_info(lock_queue)), 993: ok. 994: 995: del_table_copy(suite) -> []; 996: del_table_copy(Config) when is_list(Config) -> 997: [ThisNode, Node2] = ?acquire_nodes(2, Config), 998: Tab = d_t_c_tab, 999: Def = [{ram_copies, [ThisNode, Node2]}, {attributes, [key, attr1, attr2]}], 1000: ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 1001: insert(Tab, 50), 1002: {success, [A]} = ?start_activities([ThisNode]), 1003: mnesia_test_lib:start_sync_transactions([A], 0), 1004: A ! fun() -> mnesia:write({Tab, 1, 2, 5}) end, 1005: ?match_receive({A, ok}), %% A is executed 1006: 1007: Pid = spawn_link(?MODULE, op, [self(), mnesia, del_table_copy, 1008: [Tab, ThisNode]]), 1009: 1010: ?match_receive(timeout), %% op waits for locks occupied by A 1011: A ! end_trans, %% Kill A, locks should be released 1012: ?match_receive({A, {atomic,end_trans}}), 1013: 1014: ?match_receive({Pid, {atomic, ok}}), 1015: ?match_receive({'EXIT', Pid, normal}), 1016: 1017: timer:sleep(500), %% Don't know how to sync this !!! 1018: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 1019: sys:get_status(whereis(mnesia_tm)), % Explicit sync, release locks is async 1020: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 1021: ?match([], mnesia:system_info(held_locks)), 1022: ?match([], mnesia:system_info(lock_queue)), 1023: ok. 1024: 1025: dump_tables(suite) -> []; 1026: dump_tables(Config) when is_list(Config) -> 1027: [ThisNode, Node2] = ?acquire_nodes(2, Config), 1028: Tab = dump_t_tab, 1029: Def = [{ram_copies, [ThisNode, Node2]}, {attributes, [key, attr1, attr2]}], 1030: ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 1031: insert(Tab, 50), 1032: {success, [A]} = ?start_activities([ThisNode]), 1033: mnesia_test_lib:start_sync_transactions([A], 0), 1034: A ! fun() -> mnesia:write({Tab, 1, 1, updated}) end, 1035: ?match_receive({A, ok}), %% A is executed 1036: 1037: Pid = spawn_link(?MODULE, op, [self(), mnesia, dump_tables, 1038: [[Tab]]]), 1039: 1040: ?match_receive(timeout), %% op waits for locks occupied by A 1041: 1042: A ! end_trans, %% Kill A, locks should be released 1043: ?match_receive({A,{atomic,end_trans}}), 1044: 1045: receive 1046: Msg -> ?match({Pid, {atomic, ok}}, Msg) 1047: after 1048: timer:seconds(20) -> ?error("Operation timed out", []) 1049: end, 1050: 1051: sys:get_status(whereis(mnesia_locker)), % Explicit sync, release locks is async 1052: ?match([], mnesia:system_info(held_locks)), 1053: ?match([], mnesia:system_info(lock_queue)), 1054: ok. 1055: 1056: op(Father, Mod, Fun, Args) -> 1057: Res = apply(Mod, Fun, Args), 1058: Father ! {self(), Res}. 1059: 1060: insert(_Tab, 0) -> ok; 1061: insert(Tab, N) when N > 0 -> 1062: ok = mnesia:sync_dirty(fun() -> mnesia:write({Tab, N, N, 0}) end), 1063: insert(Tab, N-1). 1064: 1065: 1066: update_own(Tab, Key, Acc) -> 1067: Update = 1068: fun() -> 1069: Res = mnesia:read({Tab, Key}), 1070: case Res of 1071: [{Tab, Key, Extra, Acc}] -> 1072: mnesia:write({Tab,Key,Extra, Acc+1}); 1073: Val -> 1074: {read, Val, {acc, Acc}} 1075: end 1076: end, 1077: receive 1078: {Pid, quit} -> Pid ! {self(), Acc} 1079: after 1080: 0 -> 1081: case mnesia:transaction(Update) of 1082: {atomic, ok} -> 1083: update_own(Tab, Key, Acc+1); 1084: Else -> 1085: ?error("Trans failed on ~p with ~p~n" 1086: "Info w2read ~p w2write ~p w2commit ~p storage ~p ~n", 1087: [node(), 1088: Else, 1089: mnesia:table_info(Tab, where_to_read), 1090: mnesia:table_info(Tab, where_to_write), 1091: mnesia:table_info(Tab, where_to_commit), 1092: mnesia:table_info(Tab, storage_type)]) 1093: end 1094: end. 1095: 1096: update_shared(Tab, Me, Acc) -> 1097: Update = 1098: fun() -> 1099: W2R = mnesia:table_info(Tab, where_to_read), 1100: Res = mnesia:read({Tab, 0}), 1101: case Res of 1102: [{Tab, Key, Extra, Val}] when element(Me, Extra) == Acc -> 1103: Extra1 = setelement(Me, Extra, Acc+1), 1104: Term = {Tab, Key, Extra1, Val+1}, 1105: ok = mnesia:write(Term), 1106: % ?log("At ~p: ~p w2r ~p w2w ~p ~n", 1107: % [node(), Term, 1108: % mnesia:table_info(Tab, where_to_read), 1109: W2W = mnesia:table_info(Tab, where_to_write), 1110: W2C = mnesia:table_info(Tab, where_to_commit), 1111: %% mnesia:table_info(Tab, storage_type) 1112: % ]), 1113: {_Mod, Tid, Ts} = get(mnesia_activity_state), 1114: io:format("~p ~p~n", [Tid, ets:tab2list(element(2,Ts))]), 1115: {ok,Term,{W2R,W2W,W2C}}; 1116: Val -> 1117: Info = [{acc, Acc}, {me, Me}, 1118: {tid, element(2, mnesia:get_activity_id())}, 1119: {locks, mnesia:system_info(held_locks)}], 1120: {read, Val, Info} 1121: end 1122: end, 1123: receive 1124: {Pid, quit} -> Pid ! {self(), Acc} 1125: after 1126: 0 -> 1127: case mnesia:transaction(Update) of 1128: {atomic, {ok,Term,W2}} -> 1129: io:format("~p:~p:(~p,~p) ~w@~w~n", [erlang:now(),node(),Me,Acc,Term,W2]), 1130: update_shared(Tab, Me, Acc+1); 1131: Else -> 1132: ?error("Trans failed on ~p with ~p~n" 1133: "Info w2read ~p w2write ~p w2commit ~p storage ~p ~n", 1134: [node(), 1135: Else, 1136: mnesia:table_info(Tab, where_to_read), 1137: mnesia:table_info(Tab, where_to_write), 1138: mnesia:table_info(Tab, where_to_commit), 1139: mnesia:table_info(Tab, storage_type) 1140: ]) 1141: end 1142: end. 1143: 1144: init_admin(Def, N1, N2, N3) -> 1145: Tab = schema_ops, 1146: ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 1147: insert(Tab, 1002), 1148: 1149: Pid1 = spawn_link(N1, ?MODULE, update_own, [Tab, 1, 0]), 1150: Pid2 = spawn_link(N2, ?MODULE, update_own, [Tab, 2, 0]), 1151: Pid3 = spawn_link(N3, ?MODULE, update_own, [Tab, 3, 0]), 1152: 1153: ?match({atomic, ok}, 1154: mnesia:transaction(fun() -> mnesia:write({Tab, 0, {0,0,0}, 0}) end)), 1155: 1156: Pid4 = spawn_link(N1, ?MODULE, update_shared, [Tab, 1, 0]), 1157: Pid5 = spawn_link(N2, ?MODULE, update_shared, [Tab, 2, 0]), 1158: Pid6 = spawn_link(N3, ?MODULE, update_shared, [Tab, 3, 0]), 1159: 1160: {Pid1, Pid2, Pid3, Pid4, Pid5, Pid6}. 1161: 1162: verify_results({P1, P2, P3, P4, P5, P6}) -> 1163: Tab = schema_ops, N1 = node(P1), N2 = node(P2), N3 = node(P3), 1164: 1165: try 1166: P1 ! {self(), quit}, 1167: R1 = receive {P1, Res1} -> Res1 after 9000 -> throw({timeout,P1}) end, 1168: P2 ! {self(), quit}, 1169: R2 = receive {P2, Res2} -> Res2 after 9000 -> throw({timeout,P2}) end, 1170: P3 ! {self(), quit}, 1171: R3 = receive {P3, Res3} -> Res3 after 9000 -> throw({timeout,P3}) end, 1172: 1173: P4 ! {self(), quit}, 1174: R4 = receive {P4, Res4} -> Res4 after 9000 -> throw({timeout,P4}) end, 1175: P5 ! {self(), quit}, 1176: R5 = receive {P5, Res5} -> Res5 after 9000 -> throw({timeout,P5}) end, 1177: P6 ! {self(), quit}, 1178: R6 = receive {P6, Res6} -> Res6 after 9000 -> throw({timeout,P6}) end, 1179: 1180: ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write_lock_table(Tab) end)), 1181: ?log("Should be ~p~n", [R1]), 1182: ?match([{_, _, _, R1}], rpc:call(N1, mnesia, dirty_read, [{Tab, 1}])), 1183: ?match([{_, _, _, R1}], rpc:call(N2, mnesia, dirty_read, [{Tab, 1}])), 1184: ?match([{_, _, _, R1}], rpc:call(N3, mnesia, dirty_read, [{Tab, 1}])), 1185: ?log("Should be ~p~n", [R2]), 1186: ?match([{_, _, _, R2}], rpc:call(N1, mnesia, dirty_read, [{Tab, 2}])), 1187: ?match([{_, _, _, R2}], rpc:call(N2, mnesia, dirty_read, [{Tab, 2}])), 1188: ?match([{_, _, _, R2}], rpc:call(N3, mnesia, dirty_read, [{Tab, 2}])), 1189: ?log("Should be ~p~n", [R3]), 1190: ?match([{_, _, _, R3}], rpc:call(N1, mnesia, dirty_read, [{Tab, 3}])), 1191: ?match([{_, _, _, R3}], rpc:call(N2, mnesia, dirty_read, [{Tab, 3}])), 1192: ?match([{_, _, _, R3}], rpc:call(N3, mnesia, dirty_read, [{Tab, 3}])), 1193: 1194: Res = R4+R5+R6, 1195: ?log("Should be {~p+~p+~p}= ~p~n", [R4, R5, R6, Res]), 1196: ?match([{_, _, {R4,R5,R6}, Res}], rpc:call(N1, mnesia, dirty_read, [{Tab, 0}])), 1197: ?match([{_, _, {R4,R5,R6}, Res}], rpc:call(N2, mnesia, dirty_read, [{Tab, 0}])), 1198: ?match([{_, _, {R4,R5,R6}, Res}], rpc:call(N3, mnesia, dirty_read, [{Tab, 0}])) 1199: catch throw:{timeout, Pid} -> 1200: mnesia_lib:dist_coredump(), 1201: ?error("Timeout ~p ~n", [Pid]) 1202: end. 1203: 1204: 1205: get_info(Tab) -> 1206: Info = mnesia:table_info(Tab, all), 1207: mnesia_lib:verbose("~p~n", [Info]). 1208: 1209: del_table_copy_1(suite) -> []; 1210: del_table_copy_1(Config) when is_list(Config) -> 1211: [_Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config), 1212: del_table(Node2, Node2, Nodes). %Called on same Node as deleted 1213: del_table_copy_2(suite) -> []; 1214: del_table_copy_2(Config) when is_list(Config) -> 1215: [Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config), 1216: del_table(Node1, Node2, Nodes). %Called from other Node 1217: del_table_copy_3(suite) -> []; 1218: del_table_copy_3(Config) when is_list(Config) -> 1219: [_Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config), 1220: del_table(Node3, Node2, Nodes). %Called from Node w.o. table 1221: 1222: %%% The actual test 1223: del_table(CallFrom, DelNode, [Node1, Node2, Node3]) -> 1224: Def = [{ram_copies, [Node1]}, {disc_copies, [Node2]}, 1225: {attributes, [key, attr1, attr2]}], 1226: Tab = schema_ops, 1227: Pids = init_admin(Def, Node1, Node2, Node3), 1228: 1229: ?log("Call from ~p delete table from ~p ~n", [CallFrom, DelNode]), 1230: rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]), 1231: 1232: ?match({atomic, ok}, 1233: rpc:call(CallFrom, mnesia, del_table_copy, [Tab, DelNode])), 1234: 1235: verify_results(Pids), 1236: rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]), 1237: ?verify_mnesia([Node1, Node2, Node3], []). 1238: 1239: add_table_copy_1(suite) -> []; 1240: add_table_copy_1(Config) when is_list(Config) -> 1241: [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config), 1242: Def = [{disc_only_copies, [Node1, Node2]}, 1243: {attributes, [key, attr1, attr2]}], 1244: add_table(Node1, Node3, Nodes, Def). 1245: add_table_copy_2(suite) -> []; 1246: add_table_copy_2(Config) when is_list(Config) -> 1247: [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config), 1248: Def = [{disc_only_copies, [Node1, Node2]}, 1249: {attributes, [key, attr1, attr2]}], 1250: add_table(Node2, Node3, Nodes, Def). 1251: add_table_copy_3(suite) -> []; 1252: add_table_copy_3(Config) when is_list(Config) -> 1253: [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config), 1254: Def = [{disc_only_copies, [Node1, Node2]}, 1255: {attributes, [key, attr1, attr2]}], 1256: add_table(Node3, Node3, Nodes, Def). 1257: add_table_copy_4(suite) -> []; 1258: add_table_copy_4(Config) when is_list(Config) -> 1259: [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config), 1260: Def = [{disc_only_copies, [Node1]}, 1261: {attributes, [key, attr1, attr2]}], 1262: add_table(Node2, Node3, Nodes, Def). 1263: %%% The actual test 1264: add_table(CallFrom, AddNode, [Node1, Node2, Node3], Def) -> 1265: Pids = init_admin(Def, Node1, Node2, Node3), 1266: Tab = schema_ops, 1267: ?log("Call from ~p add table to ~p ~n", [CallFrom, AddNode]), 1268: rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]), 1269: ?match({atomic, ok}, rpc:call(CallFrom, mnesia, add_table_copy, 1270: [Tab, AddNode, ram_copies])), 1271: verify_results(Pids), 1272: rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]), 1273: ?verify_mnesia([Node1, Node2, Node3], []). 1274: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1275: move_table_copy_1(suite) -> []; 1276: move_table_copy_1(Config) when is_list(Config) -> 1277: [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config), 1278: Def = [{disc_copies, [Node1, Node2]}, 1279: {attributes, [key, attr1, attr2]}], 1280: move_table(Node1, Node1, Node3, Nodes, Def). 1281: move_table_copy_2(suite) -> []; 1282: move_table_copy_2(Config) when is_list(Config) -> 1283: [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config), 1284: Def = [{disc_copies, [Node1, Node2]}, 1285: {attributes, [key, attr1, attr2]}], 1286: move_table(Node2, Node1, Node3, Nodes, Def). 1287: move_table_copy_3(suite) -> []; 1288: move_table_copy_3(Config) when is_list(Config) -> 1289: [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config), 1290: Def = [{disc_copies, [Node1, Node2]}, 1291: {attributes, [key, attr1, attr2]}], 1292: move_table(Node3, Node1, Node3, Nodes, Def). 1293: move_table_copy_4(suite) -> []; 1294: move_table_copy_4(Config) when is_list(Config) -> 1295: [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config), 1296: Def = [{disc_copies, [Node1]}, 1297: {attributes, [key, attr1, attr2]}], 1298: move_table(Node2, Node1, Node3, Nodes, Def). 1299: %%% The actual test 1300: move_table(CallFrom, FromNode, ToNode, [Node1, Node2, Node3], Def) -> 1301: Pids = init_admin(Def, Node1, Node2, Node3), 1302: Tab = schema_ops, 1303: ?log("Call from ~p move table from ~p to ~p ~n", [CallFrom, FromNode, ToNode]), 1304: rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]), 1305: ?match({atomic, ok}, rpc:call(CallFrom, mnesia, move_table_copy, 1306: [Tab, FromNode, ToNode])), 1307: verify_results(Pids), 1308: rpc:multicall([Node1, Node2, Node3], ?MODULE, get_info, [Tab]), 1309: ?verify_mnesia([Node1, Node2, Node3], []). 1310: 1311: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1312: 1313: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1314: dirty_updates_visible_direct(doc) -> 1315: ["One process can immediately see dirty updates of another"]; 1316: dirty_updates_visible_direct(suite) -> []; 1317: dirty_updates_visible_direct(Config) when is_list(Config) -> 1318: dirty_visibility(outside_trans, Config). 1319: 1320: dirty_reads_regardless_of_trans(doc) -> 1321: ["Dirty reads are not affected by transaction context"]; 1322: dirty_reads_regardless_of_trans(suite) -> []; 1323: dirty_reads_regardless_of_trans(Config) when is_list(Config) -> 1324: dirty_visibility(inside_trans, Config). 1325: 1326: dirty_visibility(Mode, Config) -> 1327: [Node1] = ?acquire_nodes(1, Config), 1328: Tab = list_to_atom(lists:concat([dirty_visibility, '_', Mode])), 1329: 1330: ?match({atomic, ok}, mnesia:create_table([{name, Tab}, {ram_copies, [Node1]}])), 1331: ValPos = 3, 1332: 1333: ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)), 1334: 1335: %% Start two processes 1336: {success, [A]} = ?start_activities([Node1]), 1337: 1338: case Mode of 1339: inside_trans -> 1340: ?start_transactions([A]), 1341: A ! fun() -> 1342: mnesia:write({Tab, a, 11}), 1343: mnesia:write({Tab, b, 22}), 1344: mnesia:write({Tab, c, 1}), 1345: mnesia:write({Tab, d, 2}), 1346: mnesia:write({Tab, e, 3}), 1347: lists:sort(mnesia:all_keys(Tab)) 1348: end, 1349: ?match_receive({A, [a, b, c, d, e]}); 1350: outside_trans -> 1351: ignore 1352: end, 1353: 1354: RecA = {Tab, a, 1}, 1355: PatA = {Tab, '$1', 1}, 1356: RecB = {Tab, b, 3}, 1357: PatB = {Tab, '$1', 3}, 1358: RecB2 = {Tab, b, 2}, 1359: PatB2 = {Tab, '$1', 2}, 1360: ?match([], mnesia:dirty_read({Tab, a})), 1361: ?match([], mnesia:dirty_read({Tab, b})), 1362: ?match([], mnesia:dirty_match_object(PatA)), 1363: ?match([], mnesia:dirty_match_object(PatB)), 1364: ?match([], mnesia:dirty_match_object(PatB2)), 1365: ?match([], mnesia:dirty_index_read(Tab, 1, ValPos)), 1366: ?match([], mnesia:dirty_index_read(Tab, 3, ValPos)), 1367: ?match([], mnesia:dirty_index_match_object(PatA, ValPos)), 1368: ?match([], mnesia:dirty_index_match_object(PatB, ValPos)), 1369: ?match([], mnesia:dirty_index_match_object(PatB2, ValPos)), 1370: ?match('$end_of_table', mnesia:dirty_first(Tab)), 1371: 1372: %% dirty_write 1373: A ! fun() -> mnesia:dirty_write(RecA) end, 1374: ?match_receive({A, ok}), 1375: ?match([RecA], mnesia:dirty_read({Tab, a})), 1376: ?match([RecA], mnesia:dirty_match_object(PatA)), 1377: ?match(a, mnesia:dirty_first(Tab)), 1378: ?match([RecA], mnesia:dirty_index_read(Tab, 1, ValPos)), 1379: ?match([RecA], mnesia:dirty_index_match_object(PatA, ValPos)), 1380: ?match('$end_of_table', mnesia:dirty_next(Tab, a)), 1381: 1382: %% dirty_create 1383: A ! fun() -> mnesia:dirty_write(RecB) end, 1384: ?match_receive({A, ok}), 1385: ?match([RecB], mnesia:dirty_read({Tab, b})), 1386: ?match([RecB], mnesia:dirty_match_object(PatB)), 1387: ?match([RecB], mnesia:dirty_index_read(Tab, 3, ValPos)), 1388: ?match([RecB], mnesia:dirty_index_match_object(PatB, ValPos)), 1389: ?match('$end_of_table', 1390: mnesia:dirty_next(Tab, mnesia:dirty_next(Tab, mnesia:dirty_first(Tab)))), 1391: 1392: %% dirty_update_counter 1393: A ! fun() -> mnesia:dirty_update_counter({Tab, b}, -1) end, 1394: ?match_receive({A, _}), 1395: ?match([RecB2], mnesia:dirty_read({Tab, b})), 1396: ?match([], mnesia:dirty_match_object(PatB)), 1397: ?match([RecB2], mnesia:dirty_match_object(PatB2)), 1398: ?match([RecB2], mnesia:dirty_index_read(Tab, 2, ValPos)), 1399: ?match([], mnesia:dirty_index_match_object(PatB, ValPos)), 1400: ?match([RecB2], mnesia:dirty_index_match_object(PatB2, ValPos)), 1401: ?match('$end_of_table', 1402: mnesia:dirty_next(Tab, mnesia:dirty_next(Tab, mnesia:dirty_first(Tab)))), 1403: 1404: %% dirty_delete 1405: A ! fun() -> mnesia:dirty_delete({Tab, b}) end, 1406: ?match_receive({A, ok}), 1407: ?match([], mnesia:dirty_read({Tab, b})), 1408: ?match([], mnesia:dirty_match_object(PatB2)), 1409: ?match([], mnesia:dirty_index_read(Tab, 3, ValPos)), 1410: ?match([], mnesia:dirty_index_match_object(PatB2, ValPos)), 1411: ?match(a, mnesia:dirty_first(Tab)), 1412: ?match('$end_of_table', mnesia:dirty_next(Tab, a)), 1413: 1414: %% dirty_delete_object 1415: ?match([RecA], mnesia:dirty_match_object(PatA)), 1416: A ! fun() -> mnesia:dirty_delete_object(RecA) end, 1417: ?match_receive({A, ok}), 1418: ?match([], mnesia:dirty_read({Tab, a})), 1419: ?match([], mnesia:dirty_match_object(PatA)), 1420: ?match([], mnesia:dirty_index_read(Tab, 1, ValPos)), 1421: ?match([], mnesia:dirty_index_match_object(PatA, ValPos)), 1422: ?match('$end_of_table', mnesia:dirty_first(Tab)), 1423: 1424: case Mode of 1425: inside_trans -> 1426: A ! end_trans, 1427: ?match_receive({A, {atomic, end_trans}}); 1428: outside_trans -> 1429: ignore 1430: end, 1431: ok. 1432: 1433: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1434: trans_update_invisibible_outside_trans(doc) -> 1435: ["Updates in a transaction are invisible outside the transaction"]; 1436: trans_update_invisibible_outside_trans(suite) -> []; 1437: trans_update_invisibible_outside_trans(Config) when is_list(Config) -> 1438: [Node1] = ?acquire_nodes(1, Config), 1439: Tab = trans_update_invisibible_outside_trans, 1440: 1441: ?match({atomic, ok}, mnesia:create_table([{name, Tab}, 1442: {ram_copies, [Node1]}])), 1443: ValPos = 3, 1444: RecA = {Tab, a, 1}, 1445: PatA = {Tab, '$1', 1}, 1446: RecB = {Tab, b, 3}, 1447: PatB = {Tab, '$1', 3}, 1448: ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)), 1449: 1450: Verify = 1451: fun() -> 1452: ?match([], mnesia:dirty_read({Tab, a})), 1453: ?match([], mnesia:dirty_read({Tab, b})), 1454: ?match([], mnesia:dirty_match_object(PatA)), 1455: ?match([], mnesia:dirty_match_object(PatB)), 1456: ?match([], mnesia:dirty_index_read(Tab, 1, ValPos)), 1457: ?match([], mnesia:dirty_index_read(Tab, 3, ValPos)), 1458: ?match([], mnesia:dirty_index_match_object(PatA, ValPos)), 1459: ?match([], mnesia:dirty_index_match_object(PatB, ValPos)), 1460: ?match('$end_of_table', mnesia:dirty_first(Tab)) 1461: end, 1462: 1463: Fun = fun() -> 1464: ?match(ok, mnesia:write(RecA)), 1465: Verify(), 1466: 1467: ?match(ok, mnesia:write(RecB)), 1468: Verify(), 1469: 1470: ?match(ok, mnesia:delete({Tab, b})), 1471: Verify(), 1472: 1473: ?match([RecA], mnesia:match_object(PatA)), 1474: Verify(), 1475: 1476: ?match(ok, mnesia:delete_object(RecA)), 1477: Verify(), 1478: ok 1479: end, 1480: ?match({atomic, ok}, mnesia:transaction(Fun)), 1481: Verify(), 1482: ok. 1483: 1484: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1485: trans_update_visible_inside_trans(doc) -> 1486: ["Updates in a transaction are visible in the same transaction"]; 1487: trans_update_visible_inside_trans(suite) -> []; 1488: trans_update_visible_inside_trans(Config) when is_list(Config) -> 1489: [Node1] = ?acquire_nodes(1, Config), 1490: Tab = trans_update_visible_inside_trans, 1491: 1492: ?match({atomic, ok}, mnesia:create_table([{name, Tab}, 1493: {ram_copies, [Node1]}])), 1494: ValPos = 3, 1495: RecA = {Tab, a, 1}, 1496: PatA = {Tab, '$1', 1}, 1497: RecB = {Tab, b, 3}, 1498: PatB = {Tab, '$1', 3}, 1499: ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)), 1500: 1501: Fun = fun() -> 1502: %% write 1503: ?match(ok, mnesia:write(RecA)), 1504: ?match([RecA], mnesia:read({Tab, a})), 1505: ?match([RecA], mnesia:wread({Tab, a})), 1506: ?match([RecA], mnesia:match_object(PatA)), 1507: ?match([a], mnesia:all_keys(Tab)), 1508: ?match([RecA], mnesia:index_match_object(PatA, ValPos)), 1509: ?match([RecA], mnesia:index_read(Tab, 1, ValPos)), 1510: 1511: %% create 1512: ?match(ok, mnesia:write(RecB)), 1513: ?match([RecB], mnesia:read({Tab, b})), 1514: ?match([RecB], mnesia:wread({Tab, b})), 1515: ?match([RecB], mnesia:match_object(PatB)), 1516: ?match([RecB], mnesia:index_match_object(PatB, ValPos)), 1517: ?match([RecB], mnesia:index_read(Tab, 3, ValPos)), 1518: 1519: %% delete 1520: ?match(ok, mnesia:delete({Tab, b})), 1521: ?match([], mnesia:read({Tab, b})), 1522: ?match([], mnesia:wread({Tab, b})), 1523: ?match([], mnesia:match_object(PatB)), 1524: ?match([a], mnesia:all_keys(Tab)), 1525: ?match([], mnesia:index_match_object(PatB, ValPos)), 1526: ?match([], mnesia:index_read(Tab, 2, ValPos)), 1527: ?match([], mnesia:index_read(Tab, 3, ValPos)), 1528: 1529: %% delete_object 1530: ?match(ok, mnesia:delete_object(RecA)), 1531: ?match([], mnesia:read({Tab, a})), 1532: ?match([], mnesia:wread({Tab, a})), 1533: ?match([], mnesia:match_object(PatA)), 1534: ?match([], mnesia:all_keys(Tab)), 1535: ?match([], mnesia:index_match_object(PatA, ValPos)), 1536: ?match([], mnesia:index_read(Tab, 2, ValPos)), 1537: ?match([], mnesia:index_read(Tab, 3, ValPos)), 1538: ok 1539: end, 1540: ?match({atomic, ok}, mnesia:transaction(Fun)), 1541: ok. 1542: 1543: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1544: write_shadows(doc) -> 1545: ["Tests whether the shadow shows the correct object when", 1546: "writing to the table"]; 1547: write_shadows(suite) -> []; 1548: write_shadows(Config) when is_list(Config) -> 1549: [Node1] = ?acquire_nodes(1, Config), 1550: Tab = write_shadows, 1551: 1552: ?match({atomic, ok}, mnesia:create_table([{name, Tab}, 1553: {ram_copies, [Node1]}, 1554: {type, set}])), 1555: ValPos = 3, 1556: RecA1 = {Tab, a, 1}, 1557: PatA1 = {Tab, '$1', 1}, 1558: RecA2 = {Tab, a, 2}, 1559: PatA2 = {Tab, '$1', 2}, 1560: 1561: 1562: ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)), 1563: 1564: Fun1 = fun() -> 1565: ?match(ok, mnesia:write(RecA1)), 1566: ok 1567: end, 1568: 1569: ?match({atomic, ok}, mnesia:transaction(Fun1)), 1570: 1571: Fun2 = fun() -> 1572: %% write shadow old write - is the confirmed value visable 1573: %% in the shadow ? 1574: ?match([RecA1], mnesia:read({Tab, a})), 1575: ?match([RecA1], mnesia:wread({Tab, a})), 1576: ?match([RecA1], mnesia:match_object(PatA1)), 1577: ?match([a], mnesia:all_keys(Tab)), 1578: ?match([RecA1], mnesia:index_match_object(PatA1, ValPos)), 1579: ?match([RecA1], mnesia:index_read(Tab, 1, ValPos)), 1580: 1581: %% write shadow new write - is a new value visable instead 1582: %% of the old value ? 1583: ?match(ok, mnesia:write(RecA2)), 1584: 1585: ?match([RecA2], mnesia:read({Tab, a})), 1586: ?match([RecA2], mnesia:wread({Tab, a})), 1587: ?match([RecA2], mnesia:match_object(PatA2)), %% delete shadow old but not new write - is the new value visable 1588: 1589: ?match([a], mnesia:all_keys(Tab)), 1590: ?match([RecA2], mnesia:index_match_object(PatA2, ValPos)), 1591: ?match([RecA2], mnesia:index_read(Tab, 2, ValPos)), 1592: ok 1593: 1594: end, 1595: ?match({atomic, ok}, mnesia:transaction(Fun2)), 1596: ok. 1597: 1598: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1599: delete_shadows(doc) -> 1600: ["Test whether the shadow shows the correct object when deleting objects"]; 1601: delete_shadows(suite) -> []; 1602: delete_shadows(Config) when is_list(Config) -> 1603: [Node1] = ?acquire_nodes(1, Config), 1604: Tab = delete_shadows, 1605: 1606: ?match({atomic, ok}, mnesia:create_table([{name, Tab}, 1607: {ram_copies, [Node1]}, 1608: {type, set}])), 1609: ValPos = 3, 1610: OidA = {Tab, a}, 1611: RecA1 = {Tab, a, 1}, 1612: PatA1 = {Tab, '$1', 1}, 1613: RecA2 = {Tab, a, 2}, 1614: PatA2 = {Tab, '$1', 2}, 1615: 1616: 1617: ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)), 1618: 1619: Fun1 = fun() -> 1620: ?match(ok, mnesia:write(RecA1)), 1621: ok 1622: end, 1623: 1624: ?match({atomic, ok}, mnesia:transaction(Fun1)), 1625: 1626: Fun2 = fun() -> 1627: 1628: 1629: %% delete shadow old write - is the confirmed value invisible 1630: %% when deleted in the transaction ? 1631: ?match(ok, mnesia:delete(OidA)), 1632: 1633: ?match([], mnesia:read({Tab, a})), 1634: ?match([], mnesia:wread({Tab, a})), 1635: ?match([], mnesia:match_object(PatA1)), 1636: ?match([], mnesia:all_keys(Tab)), 1637: ?match([], mnesia:index_match_object(PatA1, ValPos)), 1638: ?match([], mnesia:index_read(Tab, 1, ValPos)), 1639: 1640: %% delete shadow old but not new write - is the new value visable 1641: %% when the old one was deleted ? 1642: ?match(ok, mnesia:write(RecA2)), 1643: 1644: ?match([RecA2], mnesia:read({Tab, a})), 1645: ?match([RecA2], mnesia:wread({Tab, a})), 1646: ?match([RecA2], mnesia:match_object(PatA2)), 1647: ?match([a], mnesia:all_keys(Tab)), 1648: ?match([RecA2], mnesia:index_match_object(PatA2, ValPos)), 1649: ?match([RecA2], mnesia:index_read(Tab, 2, ValPos)), 1650: 1651: %% delete shadow old and new write - is the new value invisable 1652: %% when deleted ? 1653: ?match(ok, mnesia:delete(OidA)), 1654: 1655: ?match([], mnesia:read({Tab, a})), 1656: ?match([], mnesia:wread({Tab, a})), 1657: ?match([], mnesia:match_object(PatA2)), 1658: ?match([], mnesia:all_keys(Tab)), 1659: ?match([], mnesia:index_match_object(PatA2, ValPos)), 1660: ?match([], mnesia:index_read(Tab, 2, ValPos)), 1661: ok 1662: 1663: end, 1664: ?match({atomic, ok}, mnesia:transaction(Fun2)), 1665: ok. 1666: 1667: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 1668: write_delete_shadows_bag(doc) -> 1669: ["Test the visibility of written and deleted objects in an bag type table"]; 1670: write_delete_shadows_bag(suite) -> []; 1671: write_delete_shadows_bag(Config) when is_list(Config) -> 1672: [Node1] = ?acquire_nodes(1, Config), 1673: Tab = write_delete_shadows_bag, 1674: 1675: ?match({atomic, ok}, mnesia:create_table([{name, Tab}, 1676: {ram_copies, [Node1]}, 1677: {type, bag}])), 1678: ValPos = 3, 1679: OidA = {Tab, a}, 1680: 1681: RecA1 = {Tab, a, 1}, 1682: PatA1 = {Tab, '$1', 1}, 1683: 1684: RecA2 = {Tab, a, 2}, 1685: PatA2 = {Tab, '$1', 2}, 1686: 1687: RecA3 = {Tab, a, 3}, 1688: PatA3 = {Tab, '$1', 3}, 1689: 1690: PatA = {Tab, a, '_'}, 1691: 1692: 1693: ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)), 1694: 1695: Fun1 = fun() -> 1696: ?match(ok, mnesia:write(RecA1)), 1697: ?match(ok, mnesia:write(RecA2)), 1698: ok 1699: end, 1700: 1701: ?match({atomic, ok}, mnesia:transaction(Fun1)), 1702: 1703: Fun2 = fun() -> 1704: %% delete shadow old write - is the confirmed value invisible 1705: %% when deleted in the transaction ? 1706: ?match(ok, mnesia:delete_object(RecA1)), 1707: 1708: ?match([RecA2], mnesia:read({Tab, a})), 1709: ?match([RecA2], mnesia:wread({Tab, a})), 1710: ?match([RecA2], mnesia:match_object(PatA2)), 1711: ?match([a], mnesia:all_keys(Tab)), 1712: ?match([RecA2], mnesia:index_match_object(PatA2, ValPos)), 1713: ?match([RecA2], mnesia:index_read(Tab, 2, ValPos)), 1714: 1715: ?match(ok, mnesia:delete(OidA)), 1716: 1717: ?match([], mnesia:read({Tab, a})), 1718: ?match([], mnesia:wread({Tab, a})), 1719: ?match([], mnesia:match_object(PatA1)), 1720: ?match([], mnesia:all_keys(Tab)), 1721: ?match([], mnesia:index_match_object(PatA1, ValPos)), 1722: ?match([], mnesia:index_read(Tab, 1, ValPos)), 1723: 1724: %% delete shadow old but not new write - are both new value visable 1725: %% when the old one was deleted ? 1726: ?match(ok, mnesia:write(RecA2)), 1727: ?match(ok, mnesia:write(RecA3)), 1728: 1729: 1730: ?match([RecA2, RecA3], lists:sort(mnesia:read({Tab, a}))), 1731: ?match([RecA2, RecA3], lists:sort(mnesia:wread({Tab, a}))), 1732: ?match([RecA2], mnesia:match_object(PatA2)), 1733: ?match([a], mnesia:all_keys(Tab)), 1734: ?match([RecA2, RecA3], lists:sort(mnesia:match_object(PatA))), 1735: ?match([RecA2], mnesia:index_match_object(PatA2, ValPos)), 1736: ?match([RecA3], mnesia:index_match_object(PatA3, ValPos)), 1737: ?match([RecA2], mnesia:index_read(Tab, 2, ValPos)), 1738: 1739: %% delete shadow old and new write - is the new value invisable 1740: %% when deleted ? 1741: ?match(ok, mnesia:delete(OidA)), 1742: 1743: ?match([], mnesia:read({Tab, a})), 1744: ?match([], mnesia:wread({Tab, a})), 1745: ?match([], mnesia:match_object(PatA2)), 1746: ?match([], mnesia:all_keys(Tab)), 1747: ?match([], mnesia:index_match_object(PatA2, ValPos)), 1748: ?match([], mnesia:index_read(Tab, 2, ValPos)), 1749: ok 1750: end, 1751: ?match({atomic, ok}, mnesia:transaction(Fun2)), 1752: ok. 1753: 1754: write_delete_shadows_bag2(doc) -> 1755: ["Test the visibility of written and deleted objects in an bag type table " 1756: "and verifies the results"]; 1757: write_delete_shadows_bag2(suite) -> []; 1758: write_delete_shadows_bag2(Config) when is_list(Config) -> 1759: 1760: [Node1] = ?acquire_nodes(1, Config), 1761: Tab = w_d_s_b, 1762: 1763: ?match({atomic, ok}, mnesia:create_table([{name, Tab}, 1764: {ram_copies, [Node1]}, 1765: {type, bag}])), 1766: Del = fun() -> 1767: R1 = mnesia:read({Tab, 1}), 1768: mnesia:delete({Tab, 1}), 1769: R2 = mnesia:read({Tab, 1}), 1770: mnesia:write({Tab, 1, 1}), 1771: mnesia:write({Tab, 1, 2}), 1772: R3 = mnesia:read({Tab, 1}), 1773: {R1, R2, R3} 1774: end, 1775: DelObj = fun() -> 1776: R1 = mnesia:read({Tab, 2}), 1777: mnesia:delete_object({Tab, 2, 2}), 1778: R2 = mnesia:read({Tab, 2}), 1779: mnesia:write({Tab, 2, 1}), 1780: mnesia:write({Tab, 2, 2}), 1781: R3 = mnesia:read({Tab, 2}), 1782: {R1, R2, R3} 1783: end, 1784: Both1 = [{Tab, 1, 1}, {Tab, 1, 2}], 1785: Both2 = [{Tab, 2, 1}, {Tab, 2, 2}], 1786: ?match({atomic, {[], [], Both1}}, mnesia:transaction(Del)), 1787: ?match({atomic, {Both1, [], Both1}}, mnesia:transaction(Del)), 1788: ?match({atomic, Both1}, mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)), 1789: ?match({atomic, {[], [], Both2}}, mnesia:transaction(DelObj)), 1790: ?match({atomic, {Both2, [{Tab, 2, 1}], Both2}}, mnesia:transaction(DelObj)), 1791: ?match({atomic, Both2}, mnesia:transaction(fun() -> mnesia:read({Tab, 2}) end)), 1792: ?verify_mnesia([Node1], []). 1793: 1794: shadow_search(doc) -> 1795: ["Verifies that ordered_set tables are ordered, and the order is kept" 1796: "even when table is shadowed by transaction updates"]; 1797: shadow_search(suite) -> []; 1798: shadow_search(Config) when is_list(Config) -> 1799: [Node1] = ?acquire_nodes(1, Config), 1800: Tab1 = ss_oset, 1801: Tab2 = ss_set, 1802: Tab3 = ss_bag, 1803: Tabs = [Tab1,Tab2,Tab3], 1804: RecName = ss, 1805: ?match({atomic, ok}, mnesia:create_table([{name, Tab1}, 1806: {ram_copies, [Node1]}, 1807: {record_name, RecName}, 1808: {type, ordered_set}])), 1809: ?match({atomic, ok}, mnesia:create_table([{name, Tab2}, 1810: {record_name, RecName}, 1811: {ram_copies, [Node1]}, 1812: {type, set}])), 1813: ?match({atomic, ok}, mnesia:create_table([{name, Tab3}, 1814: {record_name, RecName}, 1815: {ram_copies, [Node1]}, 1816: {type, bag}])), 1817: Recs = [{RecName, K, K} || K <- [1,3,5]], 1818: [mnesia:dirty_write(Tab1, R) || R <- Recs], 1819: [mnesia:dirty_write(Tab2, R) || R <- Recs], 1820: [mnesia:dirty_write(Tab3, R) || R <- Recs], 1821: 1822: Match = fun(Tab) -> mnesia:match_object(Tab, {'_','_','_'}, write) end, 1823: Select = fun(Tab) -> mnesia:select(Tab, [{'_', [], ['$_']}]) end, 1824: % Trans = fun(Fun,Args) -> mnesia:transaction(Fun,Args) end, 1825: LoopHelp = fun('$end_of_table',_) -> []; 1826: ({Res,Cont},Fun) -> 1827: Sel = mnesia:select(Cont), 1828: Res ++ Fun(Sel, Fun) 1829: end, 1830: SelLoop = fun(Table) -> 1831: Sel = mnesia:select(Table, [{'_', [], ['$_']}], 1, read), 1832: LoopHelp(Sel,LoopHelp) 1833: end, 1834: 1835: R1 = {RecName, 2, 2}, R2 = {RecName, 4, 4}, 1836: R3 = {RecName, 2, 3}, R4 = {RecName, 3, 1}, 1837: R5 = {RecName, 104, 104}, 1838: W1 = fun(Tab,Search) -> mnesia:write(Tab,R1,write), 1839: mnesia:write(Tab,R2,write), 1840: Search(Tab) 1841: end, 1842: S1 = lists:sort([R1,R2|Recs]), 1843: ?match({atomic,S1}, mnesia:transaction(W1, [Tab1,Select])), 1844: ?match({atomic,S1}, mnesia:transaction(W1, [Tab1,Match])), 1845: ?match({atomic,S1}, mnesia:transaction(W1, [Tab1,SelLoop])), 1846: ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab2,Select]))), 1847: ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab2,SelLoop]))), 1848: ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab2,Match]))), 1849: ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab3,Select]))), 1850: ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab3,SelLoop]))), 1851: ?match({atomic,S1}, sort_res(mnesia:transaction(W1, [Tab3,Match]))), 1852: [mnesia:dirty_delete_object(Tab,R) || R <- [R1,R2], Tab <- Tabs], 1853: 1854: W2 = fun(Tab,Search) -> 1855: mnesia:write(Tab,R3,write), 1856: mnesia:write(Tab,R1,write), 1857: Search(Tab) 1858: end, 1859: S2 = lists:sort([R1|Recs]), 1860: S2Bag = lists:sort([R1,R3|Recs]), 1861: ?match({atomic,S2}, mnesia:transaction(W2, [Tab1,Select])), 1862: ?match({atomic,S2}, mnesia:transaction(W2, [Tab1,SelLoop])), 1863: ?match({atomic,S2}, mnesia:transaction(W2, [Tab1,Match])), 1864: ?match({atomic,S2}, sort_res(mnesia:transaction(W2, [Tab2,Select]))), 1865: ?match({atomic,S2}, sort_res(mnesia:transaction(W2, [Tab2,SelLoop]))), 1866: ?match({atomic,S2}, sort_res(mnesia:transaction(W2, [Tab2,Match]))), 1867: ?match({atomic,S2Bag}, sort_res(mnesia:transaction(W2, [Tab3,Select]))), 1868: ?match({atomic,S2Bag}, sort_res(mnesia:transaction(W2, [Tab3,SelLoop]))), 1869: ?match({atomic,S2Bag}, sort_res(mnesia:transaction(W2, [Tab3,Match]))), 1870: %% [mnesia:dirty_delete_object(Tab,R) || R <- [R1,R3], Tab <- Tabs], 1871: 1872: W3 = fun(Tab,Search) -> 1873: mnesia:write(Tab,R4,write), 1874: mnesia:delete(Tab,element(2,R1),write), 1875: Search(Tab) 1876: end, 1877: S3Bag = lists:sort([R4|lists:delete(R1,Recs)]), 1878: S3 = lists:delete({RecName,3,3},S3Bag), 1879: ?match({atomic,S3}, mnesia:transaction(W3, [Tab1,Select])), 1880: ?match({atomic,S3}, mnesia:transaction(W3, [Tab1,SelLoop])), 1881: ?match({atomic,S3}, mnesia:transaction(W3, [Tab1,Match])), 1882: ?match({atomic,S3}, sort_res(mnesia:transaction(W3, [Tab2,SelLoop]))), 1883: ?match({atomic,S3}, sort_res(mnesia:transaction(W3, [Tab2,Select]))), 1884: ?match({atomic,S3}, sort_res(mnesia:transaction(W3, [Tab2,Match]))), 1885: ?match({atomic,S3Bag}, sort_res(mnesia:transaction(W3, [Tab3,Select]))), 1886: ?match({atomic,S3Bag}, sort_res(mnesia:transaction(W3, [Tab3,SelLoop]))), 1887: ?match({atomic,S3Bag}, sort_res(mnesia:transaction(W3, [Tab3,Match]))), 1888: 1889: W4 = fun(Tab,Search) -> 1890: mnesia:delete(Tab,-1,write), 1891: mnesia:delete(Tab,4 ,write), 1892: mnesia:delete(Tab,17,write), 1893: mnesia:delete_object(Tab,{RecName, -1, x},write), 1894: mnesia:delete_object(Tab,{RecName, 4, x},write), 1895: mnesia:delete_object(Tab,{RecName, 42, x},write), 1896: mnesia:delete_object(Tab,R2,write), 1897: mnesia:write(Tab, R5, write), 1898: Search(Tab) 1899: end, 1900: S4Bag = lists:sort([R5|S3Bag]), 1901: S4 = lists:sort([R5|S3]), 1902: ?match({atomic,S4}, mnesia:transaction(W4, [Tab1,Select])), 1903: ?match({atomic,S4}, mnesia:transaction(W4, [Tab1,SelLoop])), 1904: ?match({atomic,S4}, mnesia:transaction(W4, [Tab1,Match])), 1905: ?match({atomic,S4}, sort_res(mnesia:transaction(W4, [Tab2,Select]))), 1906: ?match({atomic,S4}, sort_res(mnesia:transaction(W4, [Tab2,SelLoop]))), 1907: ?match({atomic,S4}, sort_res(mnesia:transaction(W4, [Tab2,Match]))), 1908: ?match({atomic,S4Bag}, sort_res(mnesia:transaction(W4, [Tab3,Select]))), 1909: ?match({atomic,S4Bag}, sort_res(mnesia:transaction(W4, [Tab3,SelLoop]))), 1910: ?match({atomic,S4Bag}, sort_res(mnesia:transaction(W4, [Tab3,Match]))), 1911: [mnesia:dirty_delete_object(Tab,R) || R <- [{RecName,3,3},R5], Tab <- Tabs], 1912: 1913: %% hmmm anything more?? 1914: 1915: ?verify_mnesia([Node1], []). 1916: 1917: 1918: rr_kill_copy(suite) -> []; 1919: rr_kill_copy(Config) when is_list(Config) -> 1920: Ns = ?acquire_nodes(3,Config ++ [{tc_timeout, 60000}]), 1921: DeleteMe = fun(_Tab,Where2read) -> 1922: ?match([], mnesia_test_lib:kill_mnesia([Where2read])) 1923: end, 1924: Del = removed_resources(Ns, DeleteMe), 1925: ?verify_mnesia(Ns -- [Del], []). 1926: 1927: removed_resources([_N1,N2,N3], DeleteRes) -> 1928: Tab = del_res, 1929: ?match({atomic, ok}, mnesia:create_table(Tab,[{ram_copies, [N2,N3]}])), 1930: 1931: Init = fun() -> [mnesia:write({Tab,Key,Key}) || Key <- lists:seq(0,99)] end, 1932: ?match([], [Bad || Bad <- mnesia:sync_dirty(Init), Bad /= ok]), 1933: 1934: Where2Read = mnesia:table_info(Tab, where_to_read), 1935: [Keep] = [N2,N3] -- [Where2Read], 1936: Tester = self(), 1937: 1938: Conflict = fun() -> 1939: %% Read a value.. 1940: [{Tab,1,Val}] = mnesia:read({Tab,1}), 1941: case get(restart) of 1942: undefined -> 1943: Tester ! {pid_1, self()}, 1944: %% Wait for sync, the read value have been 1945: %% updated and this function should be restarted. 1946: receive {Tester,sync} -> ok end, 1947: put(restart, restarted); 1948: restarted -> 1949: ok 1950: end, 1951: mnesia:write({Tab,1,Val+10}) 1952: end, 1953: Lucky = fun() -> 1954: [{Tab,1,Val}] = mnesia:read({Tab,1}), 1955: mnesia:write({Tab,1,Val+100}) 1956: end, 1957: 1958: CPid = spawn_link(fun() -> Tester ! {self(), mnesia:transaction(Conflict)} end), 1959: 1960: %% sync first transaction 1961: receive {pid_1, CPid} -> synced end, 1962: 1963: DeleteRes(Tab, Where2Read), 1964: 1965: ?match(Keep, mnesia:table_info(Tab, where_to_read)), 1966: 1967: %% Run the other/Lucky transaction, this should work since 1968: %% it won't grab a lock on the conflicting transactions Where2Read node. 1969: 1970: LPid = spawn_link(Keep, fun() -> Tester ! {self(),mnesia:transaction(Lucky)} end), 1971: ?match_receive({LPid,{atomic,ok}}), 1972: 1973: %% Continue Transaction no 1 1974: CPid ! {self(), sync}, 1975: 1976: ?match(ok, receive {CPid,{atomic,ok}} -> ok after 2000 -> process_info(self()) end), 1977: 1978: ?match({atomic,[{del_res,1,111}]}, mnesia:transaction(fun() -> mnesia:read({Tab,1}) end)), 1979: Where2Read. 1980: 1981: nasty(suite) -> []; 1982: 1983: nasty(doc) -> 1984: ["Tries to fullfill a rather nasty locking scenario, where we have had " 1985: "bugs, the testcase tries a combination of locks in locker queue"]; 1986: 1987: %% This testcase no longer works as it was intended to show errors when 1988: %% tablelocks was allowed to be placed in the queue though locks existed 1989: %% in the queue with less Tid's. This is no longer allowed and the testcase 1990: %% has been update. 1991: 1992: nasty(Config) -> 1993: ?acquire_nodes(1, Config), 1994: Tab = nasty, 1995: ?match({atomic, ok}, mnesia:create_table(Tab, [])), 1996: Coord = self(), 1997: Write = fun(Key) -> 1998: mnesia:write({Tab, Key, write}), 1999: Coord ! {write, Key, self(), mnesia:get_activity_id()}, 2000: receive 2001: continue -> 2002: ok 2003: end, 2004: Coord ! {done, {write, Key}, self()} 2005: end, 2006: 2007: Update = fun(Key) -> 2008: Coord ! {update, Key, self(), mnesia:get_activity_id()}, 2009: receive 2010: continue -> 2011: ok 2012: end, 2013: mnesia:read({Tab, Key}), 2014: mnesia:write({Tab, Key, update}), 2015: receive 2016: continue -> 2017: ok 2018: end, 2019: 2020: Coord ! {done, {update, Key}, self()} 2021: end, 2022: 2023: TabLock = fun() -> 2024: Coord ! {tablock, Tab, self(), mnesia:get_activity_id()}, 2025: receive 2026: continue -> 2027: ok 2028: end, 2029: mnesia:lock({table, Tab}, write), 2030: Coord ! {done, {tablock, Tab}, self()} 2031: end, 2032: 2033: Up = spawn_link(mnesia, transaction, [Update, [0]]), 2034: ?match_receive({update, 0, Up, _Tid}), 2035: TL = spawn_link(mnesia, transaction, [TabLock]), 2036: ?match_receive({tablock, Tab, _Tl, _Tid}), 2037: W0 = spawn_link(mnesia, transaction, [Write, [0]]), 2038: ?match_receive({write, 0, W0, _Tid}), 2039: W1 = spawn_link(mnesia, transaction, [Write, [1]]), 2040: ?match_receive({write, 1, W1, _Tid}), 2041: 2042: %% Nothing should be in msg queue! 2043: ?match(timeout, receive A -> A after 1000 -> timeout end), 2044: Up ! continue, %% Should be queued 2045: ?match(timeout, receive A -> A after 1000 -> timeout end), 2046: TL ! continue, %% Should be restarted 2047: % ?match({tablock, _, _, _}, receive A -> A after 1000 -> timeout end), 2048: ?match(timeout, receive A -> A after 1000 -> timeout end), 2049: 2050: LQ1 = mnesia_locker:get_lock_queue(), 2051: ?match({2, _}, {length(LQ1), LQ1}), 2052: W0 ! continue, % Up should be in queue 2053: ?match_receive({done, {write, 0}, W0}), 2054: ?match_receive({'EXIT', W0, normal}), 2055: 2056: TL ! continue, % Should stay in queue W1 2057: ?match(timeout, receive A -> A after 1000 -> timeout end), 2058: Up ! continue, % Should stay in queue (TL got higher tid) 2059: ?match(timeout, receive A -> A after 1000 -> timeout end), 2060: 2061: LQ2 = mnesia_locker:get_lock_queue(), 2062: ?match({2, _}, {length(LQ2), LQ2}), 2063: 2064: W1 ! continue, 2065: ?match_receive({done, {write, 1}, W1}), 2066: get_exit(W1), 2067: get_exit(TL), 2068: ?match_receive({done, {tablock,Tab}, TL}), 2069: get_exit(Up), 2070: ?match_receive({done, {update, 0}, Up}), 2071: 2072: ok. 2073: 2074: get_exit(Pid) -> 2075: receive 2076: {'EXIT', Pid, normal} -> 2077: ok 2078: after 10000 -> 2079: ?error("Timeout EXIT ~p~n", [Pid]) 2080: end. 2081: 2082: 2083: foldl(doc) -> 2084: [""]; 2085: foldl(suite) -> 2086: []; 2087: foldl(Config) when is_list(Config) -> 2088: Nodes = [_,N2] = ?acquire_nodes(2, Config), 2089: Tab1 = foldl_local, 2090: Tab2 = foldl_remote, 2091: Tab3 = foldl_ordered, 2092: Tab11 = foldr_local, 2093: Tab21 = foldr_remote, 2094: Tab31 = foldr_ordered, 2095: ?match({atomic, ok}, mnesia:create_table(Tab1, [{ram_copies, Nodes}])), 2096: ?match({atomic, ok}, mnesia:create_table(Tab2, [{ram_copies, [N2]}, {type, bag}])), 2097: ?match({atomic, ok}, mnesia:create_table(Tab3, [{ram_copies, Nodes}, 2098: {type, ordered_set}])), 2099: ?match({atomic, ok}, mnesia:create_table(Tab11, [{ram_copies, Nodes}])), 2100: ?match({atomic, ok}, mnesia:create_table(Tab21, [{ram_copies, [N2]}, {type, bag}])), 2101: ?match({atomic, ok}, mnesia:create_table(Tab31, [{ram_copies, Nodes}, 2102: {type, ordered_set}])), 2103: 2104: 2105: Tab1Els = [{Tab1, N, N} || N <- lists:seq(1, 10)], 2106: Tab2Els = [{Tab2, 1, 2} | [{Tab2, N, N} || N <- lists:seq(1, 10)]], 2107: Tab3Els = [{Tab3, N, N} || N <- lists:seq(1, 10)], 2108: Tab11Els = [{Tab11, N, N} || N <- lists:seq(1, 10)], 2109: Tab21Els = [{Tab21, 1, 2} | [{Tab21, N, N} || N <- lists:seq(1, 10)]], 2110: Tab31Els = [{Tab31, N, N} || N <- lists:seq(1, 10)], 2111: 2112: [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab1Els], 2113: [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab2Els], 2114: [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab3Els], 2115: [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab11Els], 2116: [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab21Els], 2117: [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab31Els], 2118: 2119: Get = fun(E, A) -> [E | A] end, 2120: 2121: %% Before 2122: AddB = fun(Tab, Func) -> 2123: mnesia:write({Tab, 0, 0}), 2124: mnesia:write({Tab, 1, 0}), 2125: mnesia:write({Tab, 11, 0}), 2126: mnesia:Func(Get, [], Tab) 2127: end, 2128: AddT1 = [{Tab1, 0, 0}, {Tab1, 1, 0}] ++ tl(Tab1Els) ++ [{Tab1, 11, 0}], 2129: AddT2 = lists:sort([{Tab2, 0, 0}, {Tab2, 1, 0}] ++ Tab2Els ++ [{Tab2, 11, 0}]), 2130: AddT3 = [{Tab3, 0, 0}, {Tab3, 1, 0}] ++ tl(Tab3Els) ++ [{Tab3, 11, 0}], 2131: AddT11 = [{Tab11, 0, 0}, {Tab11, 1, 0}] ++ tl(Tab11Els) ++ [{Tab11, 11, 0}], 2132: AddT21 = lists:sort([{Tab21, 0, 0}, {Tab21, 1, 0}] ++ Tab21Els ++ [{Tab21, 11, 0}]), 2133: AddT31 = [{Tab31, 0, 0}, {Tab31, 1, 0}] ++ tl(Tab31Els) ++ [{Tab31, 11, 0}], 2134: 2135: ?match({atomic, AddT1}, sort_res(mnesia:transaction(AddB, [Tab1, foldl]))), 2136: ?match({atomic, AddT2}, sort_res(mnesia:transaction(AddB, [Tab2, foldl]))), 2137: ?match({atomic, AddT3}, rev_res(mnesia:transaction(AddB, [Tab3, foldl]))), 2138: ?match({atomic, AddT11}, sort_res(mnesia:transaction(AddB, [Tab11, foldr]))), 2139: ?match({atomic, AddT21}, sort_res(mnesia:transaction(AddB, [Tab21, foldr]))), 2140: ?match({atomic, AddT31}, mnesia:transaction(AddB, [Tab31, foldr])), 2141: 2142: ?match({atomic, ok}, mnesia:create_table(copy, [{ram_copies, [N2]}, 2143: {record_name, Tab1}])), 2144: CopyRec = fun(NewRec, Acc) -> 2145: %% OTP-5495 2146: W = fun() -> mnesia:write(copy, NewRec, write), [NewRec| Acc] end, 2147: {atomic,Res} = sort_res(mnesia:transaction(W)), 2148: Res 2149: end, 2150: Copy = fun() -> 2151: AddT1 = mnesia:foldl(CopyRec, [], Tab1), 2152: AddT1 = sort_res(mnesia:foldl(Get, [], copy)) 2153: end, 2154: ?match({atomic, AddT1}, sort_res(mnesia:transaction(Copy))), 2155: 2156: Del = fun(E, A) -> mnesia:delete_object(E), [E|A] end, 2157: DelD = fun(Tab) -> 2158: mnesia:write({Tab, 12, 12}), 2159: mnesia:delete({Tab, 0}), 2160: mnesia:foldr(Del, [], Tab), 2161: mnesia:foldl(Get, [], Tab) 2162: end, 2163: ?match({atomic, []}, sort_res(mnesia:transaction(DelD, [Tab1]))), 2164: ?match({atomic, []}, sort_res(mnesia:transaction(DelD, [Tab2]))), 2165: ?match({atomic, []}, rev_res(mnesia:transaction(DelD, [Tab3]))), 2166: 2167: ListWrite = fun(Tab) -> %% OTP-3893 2168: mnesia:write({Tab, [12], 12}), 2169: mnesia:foldr(Get, [], Tab) 2170: end, 2171: ?match({atomic, [{Tab1, [12], 12}]}, sort_res(mnesia:transaction(ListWrite, [Tab1]))), 2172: ?match({atomic, [{Tab2, [12], 12}]}, sort_res(mnesia:transaction(ListWrite, [Tab2]))), 2173: ?match({atomic, [{Tab3, [12], 12}]}, rev_res(mnesia:transaction(ListWrite, [Tab3]))), 2174: 2175: ?verify_mnesia(Nodes, []). 2176: 2177: sort_res({atomic, List}) when is_list(List) -> 2178: {atomic, lists:sort(List)}; 2179: sort_res(Else) when is_list(Else) -> 2180: lists:sort(Else); 2181: sort_res(Else) -> 2182: Else. 2183: 2184: rev_res({atomic, List}) -> 2185: {atomic, lists:reverse(List)}; 2186: rev_res(Else) -> 2187: Else. 2188: 2189: 2190: first_next(doc) -> [""]; 2191: first_next(suite) -> []; 2192: first_next(Config) when is_list(Config) -> 2193: Nodes = [_,N2] = ?acquire_nodes(2, Config), 2194: Tab1 = local, 2195: Tab2 = remote, 2196: Tab3 = ordered, 2197: Tab4 = bag, 2198: Tabs = [Tab1,Tab2,Tab3,Tab4], 2199: 2200: ?match({atomic, ok}, mnesia:create_table(Tab1, [{ram_copies, Nodes}])), 2201: ?match({atomic, ok}, mnesia:create_table(Tab2, [{ram_copies, [N2]}])), 2202: ?match({atomic, ok}, mnesia:create_table(Tab3, [{ram_copies, Nodes}, 2203: {type, ordered_set}])), 2204: ?match({atomic, ok}, mnesia:create_table(Tab4, [{ram_copies, Nodes}, 2205: {type, bag}])), 2206: 2207: %% Some Helpers 2208: Trans = fun(Fun) -> mnesia:transaction(Fun) end, 2209: Continue = fun(first) -> next; 2210: (last) -> prev 2211: end, 2212: LoopHelp = fun('$end_of_table',_,_,_Fun) -> []; 2213: (Key,Tab,Op,Fun) -> 2214: Next = mnesia:Op(Tab,Key), 2215: [Next |Fun(Next,Tab,Op,Fun)] 2216: end, 2217: Loop = fun(Tab,Start) -> 2218: First = mnesia:Start(Tab), 2219: Res = [First|LoopHelp(First,Tab,Continue(Start),LoopHelp)], 2220: case mnesia:table_info(Tab, type) of 2221: ordered_set when Start == first -> Res; 2222: ordered_set -> 2223: {L1,L2} = lists:split(length(Res)-1,Res), 2224: lists:reverse(L1) ++ L2; 2225: _ -> lists:sort(Res) 2226: end 2227: end, 2228: 2229: %% Verify empty tables 2230: [?match({atomic, ['$end_of_table']}, 2231: Trans(fun() -> Loop(Tab,first) end)) 2232: || Tab <- Tabs], 2233: [?match({atomic, ['$end_of_table']}, 2234: Trans(fun() -> Loop(Tab,last) end)) 2235: || Tab <- Tabs], 2236: %% Verify that trans write is visible inside trans 2237: [?match({atomic, [0,10,'$end_of_table']}, 2238: Trans(fun() -> 2239: mnesia:write({Tab,0,0}), 2240: mnesia:write({Tab,10,10}), 2241: Loop(Tab,first) end)) 2242: || Tab <- Tabs], 2243: [?match({atomic, ['$end_of_table']}, 2244: Trans(fun() -> 2245: mnesia:delete({Tab,0}), 2246: mnesia:delete({Tab,10}), 2247: Loop(Tab,first) end)) 2248: || Tab <- Tabs], 2249: 2250: [?match({atomic, [0,10,'$end_of_table']}, 2251: Trans(fun() -> 2252: mnesia:write({Tab,0,0}), 2253: mnesia:write({Tab,10,10}), 2254: Loop(Tab,last) end)) 2255: || Tab <- Tabs], 2256: [?match({atomic, ['$end_of_table']}, 2257: Trans(fun() -> 2258: mnesia:delete({Tab,0}), 2259: mnesia:delete({Tab,10}), 2260: Loop(Tab,last) end)) 2261: || Tab <- Tabs], 2262: 2263: Tab1Els = [{Tab1, N, N} || N <- lists:seq(1, 5)], 2264: Tab2Els = [{Tab2, N, N} || N <- lists:seq(1, 5)], 2265: Tab3Els = [{Tab3, N, N} || N <- lists:seq(1, 5)], 2266: Tab4Els = [{Tab4, 1, 2} | [{Tab4, N, N} || N <- lists:seq(1, 5)]], 2267: 2268: [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab1Els], 2269: [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab2Els], 2270: [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab3Els], 2271: [mnesia:sync_dirty(fun() -> mnesia:write(E) end) || E <- Tab4Els], 2272: Keys = lists:sort(mnesia:dirty_all_keys(Tab1)), 2273: R1 = Keys++ ['$end_of_table'], 2274: [?match({atomic, R1}, Trans(fun() -> Loop(Tab,first) end)) 2275: || Tab <- Tabs], 2276: 2277: [?match({atomic, R1}, Trans(fun() -> Loop(Tab,last) end)) 2278: || Tab <- Tabs], 2279: R2 = R1 -- [3], 2280: 2281: [?match({atomic, R2}, Trans(fun() -> mnesia:delete({Tab,3}),Loop(Tab,first) end)) 2282: || Tab <- Tabs], 2283: [?match({atomic, R1}, Trans(fun() -> mnesia:write({Tab,3,3}),Loop(Tab,first) end)) 2284: || Tab <- Tabs], 2285: [?match({atomic, R2}, Trans(fun() -> mnesia:delete({Tab,3}),Loop(Tab,last) end)) 2286: || Tab <- Tabs], 2287: [?match({atomic, R1}, Trans(fun() -> mnesia:write({Tab,3,3}),Loop(Tab,last) end)) 2288: || Tab <- Tabs], 2289: [?match({atomic, R1}, Trans(fun() -> mnesia:write({Tab,4,19}),Loop(Tab,first) end)) 2290: || Tab <- Tabs], 2291: [?match({atomic, R1}, Trans(fun() -> mnesia:write({Tab,4,4}),Loop(Tab,last) end)) 2292: || Tab <- Tabs], 2293: 2294: ?verify_mnesia(Nodes, []). 2295: 2296: 2297: snmp_shadows(doc) -> [""]; 2298: snmp_shadows(suite) -> []; 2299: snmp_shadows(Config) when is_list(Config) -> 2300: Nodes = ?acquire_nodes(1, Config), 2301: Tab = snmp_shadows, 2302: io:format("With fixstring~n", []), 2303: ?match({atomic, ok}, mnesia:create_table(Tab,[{snmp,[{key,{fix_string,integer}}]}])), 2304: snmp_shadows_test(Tab), 2305: ?match({atomic, ok}, mnesia:delete_table(Tab)), 2306: io:format("Without fixstring~n", []), 2307: ?match({atomic, ok}, mnesia:create_table(Tab,[{snmp,[{key,{string,integer}}]}])), 2308: snmp_shadows_test(Tab), 2309: ?verify_mnesia(Nodes, []). 2310: 2311: snmp_shadows_test(Tab) -> 2312: [mnesia:dirty_write({Tab, {"string", N}, {N, init}}) || N <- lists:seq(2,8,2)], 2313: 2314: CheckOrder = fun(A={_,_,{_,_,State}}, Prev) -> 2315: ?match({true, A, Prev}, {Prev < A, A, Prev}), 2316: {State,A} 2317: end, 2318: R1 = mnesia:sync_dirty(fun() -> loop_snmp(Tab, []) end), 2319: lists:mapfoldl(CheckOrder, {[],foo,foo}, R1), 2320: R2 = mnesia:transaction(fun() -> loop_snmp(Tab, []) end), 2321: ?match({atomic, R1}, R2), 2322: 2323: Shadow = fun() -> 2324: ok = mnesia:write({Tab, {"string",1}, {1,update}}), 2325: ok = mnesia:write({Tab, {"string",4}, {4,update}}), 2326: ok = mnesia:write({Tab, {"string",6}, {6,update}}), 2327: ok = mnesia:delete({Tab, {"string",6}}), 2328: ok = mnesia:write({Tab, {"string",9}, {9,update}}), 2329: ok = mnesia:write({Tab, {"string",3}, {3,update}}), 2330: ok = mnesia:write({Tab, {"string",5}, {5,update}}), 2331: [Row5] = mnesia:read({Tab, {"string",5}}), 2332: ok = mnesia:delete_object(Row5), 2333: loop_snmp(Tab, []) 2334: end, 2335: R3 = mnesia:sync_dirty(Shadow), 2336: {L3,_} = lists:mapfoldl(CheckOrder, {[],foo,foo}, R3), 2337: ?match([{1,update},{2,init},{3,update},{4,update},{8,init},{9,update}], L3), 2338: ?match({atomic, ok}, mnesia:clear_table(Tab)), 2339: 2340: [mnesia:dirty_write({Tab, {"string", N}, {N, init}}) || N <- lists:seq(2,8,2)], 2341: {atomic, R3} = mnesia:transaction(Shadow), 2342: {L4,_} = lists:mapfoldl(CheckOrder, {[],foo,foo}, R3), 2343: ?match([{1,update},{2,init},{3,update},{4,update},{8,init},{9,update}], L4), 2344: ok. 2345: 2346: loop_snmp(Tab,Prev) -> 2347: case mnesia:snmp_get_next_index(Tab,Prev) of 2348: {ok, SKey} -> 2349: {{ok,Row},_} = {mnesia:snmp_get_row(Tab, SKey),{?LINE,Prev,SKey}}, 2350: {{ok,MKey},_} = {mnesia:snmp_get_mnesia_key(Tab,SKey),{?LINE,Prev,SKey}}, 2351: ?match({[Row],Row,SKey,MKey}, {mnesia:read({Tab,MKey}),Row,SKey,MKey}), 2352: [{SKey, MKey, Row} | loop_snmp(Tab, SKey)]; 2353: endOfTable -> 2354: [] 2355: end.