File 0410-Do-NOT-disc_load-from-ram_copies-when-master_node-is.patch of Package erlang

From 9818fcc0b899c7d19f017dc3059a5c5f4b0b450c Mon Sep 17 00:00:00 2001
From: Dan Gudmundsson <dgud@erlang.org>
Date: Wed, 8 Aug 2018 12:14:40 +0200
Subject: [PATCH] Do NOT disc_load from ram_copies when master_node is set

Setting master_nodes to a node with ram_copies replica and
that node had not loaded the table, could cause it load an
empty table, even though (non master) nodes had disc_replicas.

This meant that tables where unexpected empty after multiple failures
happened. When this happen do not load the table and wait for user
to force_load it on some node, preferably with a disk copy.
---
 lib/mnesia/src/mnesia_controller.erl       |   3 +-
 lib/mnesia/test/mnesia_durability_test.erl | 107 ++++++++++++++++++++++++++++-
 2 files changed, 107 insertions(+), 3 deletions(-)

diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl
index 77013489b3..7a592f25f9 100644
--- a/lib/mnesia/src/mnesia_controller.erl
+++ b/lib/mnesia/src/mnesia_controller.erl
@@ -1456,7 +1456,8 @@ orphan_tables([Tab | Tabs], Node, Ns, Local, Remote) ->
 				    L = [Tab | Local],
 				    orphan_tables(Tabs, Node, Ns, L, Remote);
 				Masters ->
-				    R = [{Tab, Masters} | Remote],
+                                    %% Do not disc_load table from RamCopyHolders
+				    R = [{Tab, Masters -- RamCopyHolders} | Remote],
 				    orphan_tables(Tabs, Node, Ns, Local, R)
 			    end;
 			_ ->
diff --git a/lib/mnesia/test/mnesia_durability_test.erl b/lib/mnesia/test/mnesia_durability_test.erl
index 2fac5cac82..c186f7330b 100644
--- a/lib/mnesia/test/mnesia_durability_test.erl
+++ b/lib/mnesia/test/mnesia_durability_test.erl
@@ -91,7 +92,8 @@ groups() ->
      {load_tables_with_master_tables, [],
       [master_nodes, starting_master_nodes,
        master_on_non_local_tables,
-       remote_force_load_with_local_master_node]},
+       remote_force_load_with_local_master_node,
+       master_node_with_ram_copy_2, master_node_with_ram_copy_3]},
      {durability_of_dump_tables, [],
       [dump_ram_copies, dump_disc_copies, dump_disc_only]}].
 
@@ -1165,6 +1167,107 @@ remote_force_load_with_local_master_node(Config) when is_list(Config) ->
 
     ?verify_mnesia(Nodes, []).
 
+master_node_with_ram_copy_2(Config) when is_list(Config) ->
+    [A, B] = Nodes = ?acquire_nodes(2, Config),
+    Tab = ?FUNCTION_NAME,
+    ?match({atomic,ok}, mnesia:create_table(Tab, [{disc_copies, [A]}, {ram_copies, [B]}])),
+    ?match({atomic,ok}, mnesia:sync_transaction(?SDwrite({Tab, 1, init}))),
+
+    %% Test that we don't load from ram_copies
+    ?match(stopped, rpc:call(A, mnesia, stop, [])),
+    ?match(stopped, rpc:call(B, mnesia, stop, [])),
+    ?match(ok, rpc:call(B, mnesia, start, [])),
+    ?match({timeout, [Tab]}, rpc:call(B, mnesia, wait_for_tables, [[Tab], 1000])),
+    ?match(ok, rpc:call(A, mnesia, start, [])),
+    ?match(ok, rpc:call(B, mnesia, wait_for_tables, [[Tab], 3000])),
+    ?match([{Tab, 1, init}], rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+    ?match([{Tab, 1, init}], rpc:call(B, mnesia, dirty_read, [{Tab, 1}])),
+
+    %% Test that master_nodes set to ram_copy node require force_load
+    ?match(ok, rpc:call(A, mnesia, set_master_nodes, [[B]])),
+    ?match(stopped, rpc:call(A, mnesia, stop, [])),
+    ?match(stopped, rpc:call(B, mnesia, stop, [])),
+    ?match(ok, rpc:call(B, mnesia, start, [])),
+    ?match({timeout, [Tab]}, rpc:call(B, mnesia, wait_for_tables, [[Tab], 1000])),
+    ?match(ok, rpc:call(A, mnesia, start, [])),
+    ?match({timeout, [Tab]}, rpc:call(B, mnesia, wait_for_tables, [[Tab], 1000])),
+
+    ?match(yes, rpc:call(A, mnesia, force_load_table, [Tab])),
+    ?match(ok, rpc:call(A, mnesia, wait_for_tables, [[Tab], 1000])),
+    ?match(ok, rpc:call(B, mnesia, wait_for_tables, [[Tab], 1000])),
+    ?match([{Tab, 1, init}], rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+    ?match([{Tab, 1, init}], rpc:call(B, mnesia, dirty_read, [{Tab, 1}])),
+
+    ?verify_mnesia(Nodes, []).
+
+
+master_node_with_ram_copy_3(Config) when is_list(Config) ->
+    [A, B, C] = Nodes = ?acquire_nodes(3, Config),
+    Tab = ?FUNCTION_NAME,
+    ?match({atomic,ok}, mnesia:create_table(Tab, [{disc_copies, [A,C]}, {ram_copies, [B]}])),
+    ?match({atomic,ok}, mnesia:sync_transaction(?SDwrite({Tab, 1, init}))),
+
+    %% Test that we don't load from ram_copies
+    ?match(stopped, rpc:call(A, mnesia, stop, [])),
+    ?match(stopped, rpc:call(C, mnesia, stop, [])),
+    ?match(stopped, rpc:call(B, mnesia, stop, [])),
+    ?match(ok, rpc:call(B, mnesia, start, [])),
+    ?match({timeout, [Tab]}, rpc:call(B, mnesia, wait_for_tables, [[Tab], 1000])),
+    ?match(ok, rpc:call(A, mnesia, start, [])),
+    ?match(ok, rpc:call(C, mnesia, start, [])),
+    ?match(ok, rpc:call(B, mnesia, wait_for_tables, [[Tab], 3000])),
+    ?match(ok, rpc:call(A, mnesia, wait_for_tables, [[Tab], 3000])),
+    ?match([{Tab, 1, init}], rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+    ?match([{Tab, 1, init}], rpc:call(B, mnesia, dirty_read, [{Tab, 1}])),
+    ?match([{Tab, 1, init}], rpc:call(C, mnesia, dirty_read, [{Tab, 1}])),
+
+    %% Test that master_nodes set to ram_copy node will wait until loaded
+    ?match(ok, rpc:call(A, mnesia, set_master_nodes, [[B]])),
+    ?match(stopped, rpc:call(A, mnesia, stop, [])),
+    ?match({atomic,ok}, rpc:call(B, mnesia, sync_transaction, [?SDwrite({Tab, 1, update})])),
+    ?match(stopped, rpc:call(C, mnesia, stop, [])),
+    ?match({atomic,ok}, rpc:call(B, mnesia, sync_transaction, [?SDwrite({Tab, 1, ram_copies})])),
+    ?match(stopped, rpc:call(B, mnesia, stop, [])),
+    ?match(ok, rpc:call(B, mnesia, start, [])),
+    ?match({timeout, [Tab]}, rpc:call(B, mnesia, wait_for_tables, [[Tab], 500])),
+    ?match(ok, rpc:call(A, mnesia, start, [])),
+    ?match({timeout, [Tab]}, rpc:call(A, mnesia, wait_for_tables, [[Tab], 500])),
+    ?match(ok, rpc:call(C, mnesia, start, [])),
+    ?match(ok, rpc:call(B, mnesia, wait_for_tables, [[Tab], 3000])),
+    ?match(ok, rpc:call(A, mnesia, wait_for_tables, [[Tab], 3000])),
+    ?match([{Tab, 1, update}], rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+    ?match([{Tab, 1, update}], rpc:call(B, mnesia, dirty_read, [{Tab, 1}])),
+    ?match([{Tab, 1, update}], rpc:call(C, mnesia, dirty_read, [{Tab, 1}])),
+
+    %% Test that master_nodes set to ram_copy node requires force load
+    ?match({atomic,ok}, mnesia:sync_transaction(?SDwrite({Tab, 1, init}))),
+    ?match(ok, rpc:call(A, mnesia, set_master_nodes, [[B]])),
+    ?match(ok, rpc:call(C, mnesia, set_master_nodes, [[B]])),
+
+    ?match(stopped, rpc:call(A, mnesia, stop, [])),
+    ?match({atomic,ok}, rpc:call(B, mnesia, sync_transaction, [?SDwrite({Tab, 1, update})])),
+    ?match(stopped, rpc:call(C, mnesia, stop, [])),
+    ?match({atomic,ok}, rpc:call(B, mnesia, sync_transaction, [?SDwrite({Tab, 1, ram_copies})])),
+    ?match(stopped, rpc:call(B, mnesia, stop, [])),
+    ?match(ok, rpc:call(B, mnesia, start, [])),
+    ?match({timeout, [Tab]}, rpc:call(B, mnesia, wait_for_tables, [[Tab], 500])),
+    ?match(ok, rpc:call(A, mnesia, start, [])),
+    ?match({timeout, [Tab]}, rpc:call(A, mnesia, wait_for_tables, [[Tab], 500])),
+    ?match(ok, rpc:call(C, mnesia, start, [])),
+    ?match({timeout, [Tab]}, rpc:call(A, mnesia, wait_for_tables, [[Tab], 500])),
+    ?match({timeout, [Tab]}, rpc:call(B, mnesia, wait_for_tables, [[Tab], 500])),
+    ?match({timeout, [Tab]}, rpc:call(B, mnesia, wait_for_tables, [[Tab], 500])),
+    ?match(yes, rpc:call(C, mnesia, force_load_table, [Tab])),
+
+    ?match(ok, rpc:call(A, mnesia, wait_for_tables, [[Tab], 3000])),
+    ?match(ok, rpc:call(B, mnesia, wait_for_tables, [[Tab], 3000])),
+    ?match(ok, rpc:call(C, mnesia, wait_for_tables, [[Tab], 3000])),
+    ?match([{Tab, 1, update}], rpc:call(A, mnesia, dirty_read, [{Tab, 1}])),
+    ?match([{Tab, 1, update}], rpc:call(B, mnesia, dirty_read, [{Tab, 1}])),
+    ?match([{Tab, 1, update}], rpc:call(C, mnesia, dirty_read, [{Tab, 1}])),
+
+    ?verify_mnesia(Nodes, []).
+
 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
 
@@ -1415,7 +1518,7 @@ do_disc_durability(Config,CopyType) ->
 		  [{Tab_bag, 22, a_2222}], [{Tab_bag, 33, a_3333}],
 		  [{Tab_set, counter, 10}]]),
 
-    timer:sleep(1000), %% Debugging strange msgs..
+    timer:sleep(500), %% Debugging strange msgs..
     ?log("Flushed ~p ~n", [mnesia_test_lib:flush()]),
     ?verify_mnesia(Nodes, []).
 
-- 
2.16.4

openSUSE Build Service is sponsored by