diff options
Diffstat (limited to 'lib/mnesia')
65 files changed, 2810 insertions, 2253 deletions
diff --git a/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc index 0714c7b645..473b35b806 100644 --- a/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc +++ b/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -235,9 +235,7 @@ <seealso marker="Mnesia_chap3#start_mnesia">Starting Mnesia</seealso>. </item> </list> - <p>Continuing the dialogue with the Erlang shell will produce the following - the following: - </p> + <p>Continuing the dialogue with the Erlang shell will produce the following:</p> <pre><![CDATA[ 3> company:init(). {atomic,ok} @@ -418,7 +416,7 @@ In_proj</tcaption> interchangeably throughout this book. </p> <p>A Mnesia table is populated by Mnesia records. For example, - the tuple <c>{boss, klacke, bjarne}</c> is an record. The + the tuple <c>{boss, klacke, bjarne}</c> is a record. The second element in this tuple is the key. In order to uniquely identify a table row both the key and the table name is needed. The term <em>object identifier</em>, @@ -553,7 +551,7 @@ In_proj</tcaption> stored in the database: </p> <pre> -\011 mnesia:select(employee, [{#employee{sex = female, name = '$1', _ = '_'},[], ['$1']}]). +mnesia:select(employee, [{#employee{sex = female, name = '$1', _ = '_'},[], ['$1']}]). </pre> <p>Select must always run within an activity such as a transaction. To be able to call from the shell we might @@ -587,8 +585,8 @@ In_proj</tcaption> </p> <pre> Q = qlc:q([E#employee.name || E <![CDATA[<-]]> mnesia:table(employee), -\011 E#employee.sex == female]), -\011 qlc:e(Q), + E#employee.sex == female]), + qlc:e(Q), </pre> <p>Accessing mnesia tables from a QLC list comprehension must always be done within a transaction. Consider the following diff --git a/lib/mnesia/doc/src/Mnesia_chap3.xml b/lib/mnesia/doc/src/Mnesia_chap3.xml index 9a382bcb5a..5733aedbfd 100644 --- a/lib/mnesia/doc/src/Mnesia_chap3.xml +++ b/lib/mnesia/doc/src/Mnesia_chap3.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -132,7 +132,7 @@ function changes the format on all records in table <c>Tab</c>. It applies the argument <c>Fun</c> to all records in the table. <c>Fun</c> shall be a function which - takes an record of the old type, and returns the record of the new + takes a record of the old type, and returns the record of the new type. The table key may not be changed.</p> <code type="none"> -record(old, {key, val}). @@ -418,8 +418,8 @@ skeppet %<input>erl -sname b -mnesia dir '"/ldisc/scratch/Mnesia.company"'</inpu type <c>set</c> and <c>bag</c>: </p> <pre> f() -> F = fun() -> -\011 mnesia:write({foo, 1, 2}), mnesia:write({foo, 1, 3}), -\011 mnesia:read({foo, 1}) end, mnesia:transaction(F). </pre> + mnesia:write({foo, 1, 2}), mnesia:write({foo, 1, 3}), + mnesia:read({foo, 1}) end, mnesia:transaction(F). </pre> <p>This transaction will return the list <c>[{foo,1,3}]</c> if the <c>foo</c> table is of type <c>set</c>. However, list <c>[{foo,1,2}, {foo,1,3}]</c> will return if the table is diff --git a/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc index 7d89c1b0dd..7e57c7ac02 100644 --- a/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc +++ b/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -514,13 +514,13 @@ The behavior is undefined if any process perform a write of the table itself. This is an implementation detail, but remember the dirty functions are low level functions. </item> - <item><c>mnesia:dirty_last(Tab)</c> This function works exactly as + <item><c>mnesia:dirty_last(Tab)</c> This function works exactly like <c>mnesia:dirty_first/1</c> but returns the last object in Erlang term order for the <c>ordered_set</c> table type. For all other table types, <c>mnesia:dirty_first/1</c> and <c>mnesia:dirty_last/1</c> are synonyms. </item> - <item><c>mnesia:dirty_prev(Tab, Key)</c> This function works exactly as + <item><c>mnesia:dirty_prev(Tab, Key)</c> This function works exactly like <c>mnesia:dirty_next/2</c> but returns the previous object in Erlang term order for the ordered_set table type. For all other table types, <c>mnesia:dirty_next/2</c> and diff --git a/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc index 3ec0aa37f5..30a8991465 100644 --- a/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc +++ b/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2010</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -13,12 +13,12 @@ compliance with the License. You should have received a copy of the Erlang Public License along with this software. If not, it can be retrieved online at http://www.erlang.org/. - + Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - + </legalnotice> <title>Miscellaneous Mnesia Features</title> @@ -335,7 +335,7 @@ ok explicitly be set at table creation. The default is <c>0</c>, but if <c>n_disc_copies</c> and <c>n_disc_only_copies</c> also are <c>0</c>, - <c>n_ram_copies</c>\011will default be set to <c>1</c>. + <c>n_ram_copies</c> will default be set to <c>1</c>. </p> </item> <tag><c>{n_disc_copies, Int}</c></tag> @@ -408,7 +408,7 @@ ok (a@sam)4> SecProps = [{foreign_key, {prim_dict, sec_val}}]. [{foreign_key,{prim_dict,sec_val}}] (a@sam)5> mnesia:create_table(sec_dict, -\011 [{frag_properties, SecProps}, + [{frag_properties, SecProps}, (a@sam)5> {attributes, [sec_key, sec_val]}]). {atomic,ok} (a@sam)6> Write = fun(Rec) -> mnesia:write(Rec) end. @@ -418,23 +418,23 @@ ok (a@sam)8> SecKey = 42. 42 (a@sam)9> mnesia:activity(sync_dirty, Write, -\011\011 [{prim_dict, PrimKey, -11}], mnesia_frag). + [{prim_dict, PrimKey, -11}], mnesia_frag). ok (a@sam)10> mnesia:activity(sync_dirty, Write, -\011\011 [{sec_dict, SecKey, PrimKey}], mnesia_frag). + [{sec_dict, SecKey, PrimKey}], mnesia_frag). ok (a@sam)11> mnesia:change_table_frag(prim_dict, {add_frag, [node()]}). {atomic,ok} (a@sam)12> SecRead = fun(PrimKey, SecKey) -> -\011\011 mnesia:read({sec_dict, PrimKey}, SecKey, read) end. + mnesia:read({sec_dict, PrimKey}, SecKey, read) end. #Fun<erl_eval> (a@sam)13> mnesia:activity(transaction, SecRead, -\011\011 [PrimKey, SecKey], mnesia_frag). + [PrimKey, SecKey], mnesia_frag). [{sec_dict,42,11}] (a@sam)14> Info = fun(Tab, Item) -> mnesia:table_info(Tab, Item) end. #Fun<erl_eval> (a@sam)15> mnesia:activity(sync_dirty, Info, -\011\011 [prim_dict, frag_size], mnesia_frag). + [prim_dict, frag_size], mnesia_frag). [{prim_dict,0}, {prim_dict_frag2,0}, {prim_dict_frag3,0}, @@ -444,7 +444,7 @@ ok {prim_dict_frag7,0}, {prim_dict_frag8,0}] (a@sam)16> mnesia:activity(sync_dirty, Info, -\011\011 [sec_dict, frag_size], mnesia_frag). + [sec_dict, frag_size], mnesia_frag). [{sec_dict,0}, {sec_dict_frag2,0}, {sec_dict_frag3,0}, @@ -885,16 +885,16 @@ ok <item>Removes the subscription on events of type <c>Event-Category</c></item> </taglist> - <p><c>Event-Category</c> may either be the atom <c>system</c>, or + <p><c>Event-Category</c> may either be the atom <c>system</c>, the atom <c>activity</c>, or one of the tuples <c>{table, Tab, simple}</c>, <c>{table, Tab, detailed}</c>. The old event-category <c>{table, Tab}</c> is the same event-category as <c>{table, Tab, simple}</c>. The subscribe functions activate a subscription of events. The events are delivered as messages to the process evaluating the <c>mnesia:subscribe/1</c> function. The syntax of - system events is <c>{mnesia_system_event, Event}</c> and - <c>{mnesia_table_event, Event}</c> for table events. What system - events and table events means is described below. - </p> + system events is <c>{mnesia_system_event, Event}</c>, + <c>{mnesia_activity_event, Event}</c> for activity events, and + <c>{mnesia_table_event, Event}</c> for table events. What the various + event types mean is described below.</p> <p>All system events are subscribed by Mnesia's gen_event handler. The default gen_event handler is <c>mnesia_event</c>. But it may be changed by using the application @@ -1039,8 +1039,26 @@ ok </section> <section> + <title>Activity Events</title> + <p>Currently, there is only one type of activity event:</p> + <taglist> + <tag><c>{complete, ActivityID}</c></tag> + <item> + <p>This event occurs when a transaction that caused a modification to the database + has completed. It is useful for determining when a set of table events + (see below) caused by a given activity have all been sent. Once the this event + has been received, it is guaranteed that no further table events with the same + ActivityID will be received. Note that this event may still be received even + if no table events with a corresponding ActivityID were received, depending on + the tables to which the receiving process is subscribed.</p> + <p>Dirty operations always only contain one update and thus no activity event is sent.</p> + </item> + </taglist> + </section> + + <section> <title>Table Events</title> - <p>Another category of events are table events, which are + <p>The final category of events are table events, which are events related to table updates. There are two types of table events simple and detailed. </p> diff --git a/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc index 7078499fbf..ae41a216f4 100644 --- a/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc +++ b/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -473,6 +473,13 @@ dets:close(N). </pre> <c>mnesia:table_info(Tab, master_nodes)</c> may be used to obtain information about the potential master nodes. </p> + <p>Determining which data to keep after communication failure is outside + the scope of Mnesia. One approach would be to determine which "island" + contains a majority of the nodes. Using the <c>{majority,true}</c> option + for critical tables can be a way of ensuring that nodes that are not part + of a "majority island" are not able to update those tables. Note that this + constitutes a reduction in service on the minority nodes. This would be + a tradeoff in favour of higher consistency guarantees.</p> <p>The function <c>mnesia:force_load_table(Tab)</c> may be used to force load the table regardless of which table load mechanism is activated. diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml index d76471d922..19ec70118f 100644 --- a/lib/mnesia/doc/src/mnesia.xml +++ b/lib/mnesia/doc/src/mnesia.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -161,6 +161,14 @@ If a new item is inserted with the same key as </p> </item> <item> + <p><c>majority</c> This attribute can be either <c>true</c> or + <c>false</c> (default is <c>false</c>). When <c>true</c>, a majority + of the table replicas must be available for an update to succeed. + Majority checking can be enabled on tables with mission-critical data, + where it is vital to avoid inconsistencies due to network splits. + </p> + </item> + <item> <p><c>snmp</c> Each (set based) Mnesia table can be automatically turned into an SNMP ordered table as well. This property specifies the types of the SNMP keys. @@ -238,7 +246,7 @@ If a new item is inserted with the same key as </p> </item> <item> - <p><c>{max,MaxTabs}</c><c>MaxTabs</c> is a list of + <p><c>{max,MaxTabs}</c>. <c>MaxTabs</c> is a list of tables that should be included in the checkpoint. The default is []. For these tables, the redundancy will be maximized and checkpoint information will be retained together @@ -266,7 +274,7 @@ If a new item is inserted with the same key as </p> </item> <item> - <p><c>{ram_overrides_dump,Bool} </c> Only applicable + <p><c>{ram_overrides_dump,Bool}</c>. Only applicable for <c>ram_copies</c>. <c>Bool</c> allows you to choose to backup the table state as it is in RAM, or as it is on disc. <c>true</c> means that the latest committed @@ -650,6 +658,17 @@ mnesia:change_table_copy_type(person, node(), disc_copies) </desc> </func> <func> + <name>change_table_majority(Tab, Majority) -> {aborted, R} | {atomic, ok}</name> + <fsummary>Change the majority check setting for the table.</fsummary> + <desc> + <p><c>Majority</c> must be a boolean; the default is <c>false</c>. + When <c>true</c>, a majority of the table's replicas must be available + for an update to succeed. When used on fragmented tables, <c>Tab</c> + must be the name base table. Directly changing the majority setting on + individual fragments is not allowed.</p> + </desc> + </func> + <func> <name>clear_table(Tab) -> {aborted, R} | {atomic, ok}</name> <fsummary>Deletes all entries in a table.</fsummary> <desc> @@ -753,6 +772,14 @@ mnesia:change_table_copy_type(person, node(), disc_copies) priority will be loaded first at startup. </p> </item> + <item> + <p><c>{majority, Flag}</c>, where <c>Flag</c> must be a boolean. + If <c>true</c>, any (non-dirty) update to the table will abort unless + a majority of the table's replicas are available for the commit. + When used on a fragmented table, all fragments will be given + the same majority setting. + </p> + </item> <item> <p><c>{ram_copies, Nodelist}</c>, where <c>Nodelist</c> is a list of the nodes where this table @@ -799,7 +826,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies) </item> <item> <p><c>{local_content, Bool}</c>, where <c>Bool</c> must be - either <c>true</c> or <c>false</c>. The default value is <c>false</c>.\011 </p> + either <c>true</c> or <c>false</c>. The default value is <c>false</c>.</p> </item> </list> <p>For example, the following call creates the <c>person</c> table @@ -1022,7 +1049,7 @@ mnesia:create_table(person, <name>dirty_last(Tab) -> Key | exit({aborted, Reason}) </name> <fsummary>Return the key for the last record in a table.</fsummary> <desc> - <p>This function works exactly + <p>This function works exactly like <c>mnesia:dirty_first/1</c> but returns the last object in Erlang term order for the <c>ordered_set</c> table type. For all other table types, <c>mnesia:dirty_first/1</c> and @@ -1063,11 +1090,11 @@ mnesia:create_table(person, <name>dirty_prev(Tab, Key) -> Key | exit({aborted, Reason}) </name> <fsummary>Return the previous key in a table. </fsummary> <desc> - <p>This function works exactly + <p>This function works exactly like <c>mnesia:dirty_next/2</c> but returns the previous object in Erlang term order for the ordered_set table type. For all other table types, <c>mnesia:dirty_next/2</c> and - <c>mnesia:dirty_prev/2</c> are synonyms.\011 </p> + <c>mnesia:dirty_prev/2</c> are synonyms.</p> </desc> </func> <func> @@ -1135,7 +1162,7 @@ mnesia:create_table(person, </list> <p>If two processes perform <c>mnesia:dirty_update_counter/3</c> simultaneously, both updates will take effect without the - risk of loosing one of the updates. The new value + risk of losing one of the updates. The new value <c>NewVal</c> of the counter is returned.</p> <p>If <c>Key</c> don't exits, a new record is created with the value <c>Incr</c> if it is larger than 0, otherwise it is set to 0.</p> @@ -1334,7 +1361,7 @@ mnesia:create_table(person, <name>foldr(Function, Acc, Table) -> NewAcc | transaction abort </name> <fsummary>Call Function for each record in Table </fsummary> <desc> - <p>This function works exactly as + <p>This function works exactly like <c>foldl/3</c> but iterates the table in the opposite order for the <c>ordered_set</c> table type. For all other table types, <c>foldr/3</c> and @@ -1512,14 +1539,14 @@ mnesia:create_table(person, <fsummary>Check if code is running in a transaction.</fsummary> <desc> <p>When this function is executed inside a transaction context - it returns <c>true</c>, otherwise <c>false</c>.</p> + it returns <c>true</c>, otherwise <c>false</c>.</p> </desc> </func> <func> <name>last(Tab) -> Key | transaction abort </name> <fsummary>Return the key for the last record in a table.</fsummary> <desc> - <p>This function works exactly + <p>This function works exactly like <c>mnesia:first/1</c> but returns the last object in Erlang term order for the <c>ordered_set</c> table type. For all other table types, <c>mnesia:first/1</c> and @@ -1698,11 +1725,11 @@ mnesia:create_table(person, <name>prev(Tab, Key) -> Key | transaction abort </name> <fsummary>Return the previous key in a table. </fsummary> <desc> - <p>This function works exactly + <p>This function works exactly like <c>mnesia:next/2</c> but returns the previous object in Erlang term order for the ordered_set table type. For all other table types, <c>mnesia:next/2</c> and - <c>mnesia:prev/2</c> are synonyms.\011 </p> + <c>mnesia:prev/2</c> are synonyms.</p> </desc> </func> <func> @@ -1737,7 +1764,10 @@ mnesia:create_table(person, <c>write</c> and <c>sticky_write</c> are supported. </p> <p>If the user wants to update the record it is more efficient to - use <c>write/sticky_write</c> as the LockKind. + use <c>write/sticky_write</c> as the LockKind. If majority checking + is active on the table, it will be checked as soon as a write lock is + attempted. This can be used to quickly abort if the majority condition + isn't met. </p> </desc> </func> @@ -1891,10 +1921,10 @@ mnesia:create_table(person, <p>For example to find the names of all male persons with an age over 30 in table Tab do:</p> <code type="none"> -\011 MatchHead = #person{name='$1', sex=male, age='$2', _='_'}, -\011 Guard = {'>', '$2', 30}, -\011 Result = '$1', -\011 mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]), +MatchHead = #person{name='$1', sex=male, age='$2', _='_'}, +Guard = {'>', '$2', 30}, +Result = '$1', +mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]), </code> </desc> </func> @@ -2835,7 +2865,7 @@ raise(Name, Amount) -> </func> <func> <name>write(Tab, Record, LockKind) -> transaction abort | ok </name> - <fsummary>Write an record into the database.</fsummary> + <fsummary>Write a record into the database.</fsummary> <desc> <p>Writes the record <c>Record</c> to the table <c>Tab</c>. </p> diff --git a/lib/mnesia/doc/src/mnesia_frag_hash.xml b/lib/mnesia/doc/src/mnesia_frag_hash.xml index ca03327994..665796f20d 100644 --- a/lib/mnesia/doc/src/mnesia_frag_hash.xml +++ b/lib/mnesia/doc/src/mnesia_frag_hash.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2002</year> - <year>2007</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -64,7 +64,7 @@ <p>Note that the <c>add_frag/2</c> function will be invoked one time each for the rest of the fragments (all but number 1) as a part of the table creation procedure.</p> - <p><c>State</c> is the initial value of the <c>hash_state</c><c>frag_property</c>. The <c>NewState</c> will be stored as + <p><c>State</c> is the initial value of the <c>hash_state</c> <c>frag_property</c>. The <c>NewState</c> will be stored as <c>hash_state</c> among the other <c>frag_properties</c>. </p> </desc> diff --git a/lib/mnesia/doc/src/mnesia_registry.xml b/lib/mnesia/doc/src/mnesia_registry.xml index 966134d508..e08f3a42fc 100644 --- a/lib/mnesia/doc/src/mnesia_registry.xml +++ b/lib/mnesia/doc/src/mnesia_registry.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1998</year> - <year>2007</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml index 66242398d9..8ef573a948 100644 --- a/lib/mnesia/doc/src/notes.xml +++ b/lib/mnesia/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1996</year><year>2010</year> + <year>1996</year><year>2011</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -37,6 +37,205 @@ bugfixes for every release of Mnesia. Each release of Mnesia thus constitutes one section in this document. The title of each section is the version number of Mnesia.</p> + + <section><title>Mnesia 4.5</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix protocol issues. Mnesia-4.4.19 could not communicate + with to older nodes.</p> + <p> + Own Id: OTP-9473</p> + </item> + <item> + <p> XML files have been corrected. </p> + <p> + Own Id: OTP-9550 Aux Id: OTP-9541 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Dump the log even if no transactions have been invoked on + local node, otherwise the log will grow forever with + decisions from the other nodes who have tables on disk. + Thanks Marek Majkowski.</p> + <p> + Own Id: OTP-9551</p> + </item> + <item> + <p> + Use dedicated api for clear_table, i.e. instead of + match_delete use delete_all_objects. Thanks KukHyun Lee.</p> + <p> + Own Id: OTP-9558</p> + </item> + </list> + </section> + +</section> + +<section><title>Mnesia 4.4.19</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Mnesia could crash if mnesia:add_table_index/2 was + invoked before the table was loaded on all nodes.</p> + <p> + Own Id: OTP-9285 Aux Id: seq11844 </p> + </item> + <item> + <p> + Add {majority, boolean()} per-table option.</p> + <p> + With {majority, true} set for a table, write transactions + will abort if they cannot commit to a majority of the + nodes that have a copy of the table. Currently, the + implementation hooks into the prepare_commit, and forces + an asymmetric transaction if the commit set affects any + table with the majority flag set. In the commit itself, + the transaction will abort if it cannot satisfy the + majority requirement for all tables involved in the + transaction.(Thanks to Ulf Wiger)</p> + <p> + Own Id: OTP-9304</p> + </item> + </list> + </section> + +</section> + +<section><title>Mnesia 4.4.18</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Call chmod without the "-f" flag</p> + <p> + "-f" is a non-standard chmod option which at least SGI + IRIX and HP UX do not support. As the only effect of the + "-f" flag is to suppress warning messages, it can be + safely omitted. (Thanks to Holger Wei�)</p> + <p> + Own Id: OTP-9170</p> + </item> + <item> + <p> + Mnesia sometimes failed to update meta-information in + large systems, which could cause table content to be + inconsistent between nodes.</p> + <p> + Own Id: OTP-9186 Aux Id: seq11728 </p> + </item> + </list> + </section> + +</section> + +<section><title>Mnesia 4.4.17</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Calling mnesia:first/1 on empty fragmented table works. + Thanks Magnus Henoch.</p> + <p> + Own Id: OTP-9108</p> + </item> + <item> + <p> + If Mnesia detects that the network is not fully connected + during start, Mnesia will not start until all nodes are + reachable.</p> + <p> + Own Id: OTP-9115 Aux Id: seq-11728 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Fix issues reported by dialyzer.</p> + <p> + Own Id: OTP-9107</p> + </item> + </list> + </section> + +</section> + +<section><title>Mnesia 4.4.16</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Sometimes a 'log_header' record was added to tables when + invoking mnesia:restore/2 with the option + 'recreate_tables'. Thanks Vance Shipley.</p> + <p> + Own Id: OTP-8960</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Compiler warnings were eliminated.</p> + <p> + Own Id: OTP-8855</p> + </item> + </list> + </section> + +</section> + +<section><title>Mnesia 4.4.15</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Eliminated warnings for auto-imported BIF clashes.</p> + <p> + Own Id: OTP-8840</p> + </item> + </list> + </section> + +</section> + +<section><title>Mnesia 4.4.14</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Added mnesia:subscribe(activity) contributed by Bernard + Duggan.</p> + <p> + Own Id: OTP-8519</p> + </item> + </list> + </section> + + </section> <section><title>Mnesia 4.4.13</title> diff --git a/lib/mnesia/doc/src/part_notes_history.xml b/lib/mnesia/doc/src/part_notes_history.xml index 177738623c..e4621dbbf7 100644 --- a/lib/mnesia/doc/src/part_notes_history.xml +++ b/lib/mnesia/doc/src/part_notes_history.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2004</year> - <year>2007</year> + <year>2011</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> diff --git a/lib/mnesia/examples/mnesia_meter.erl b/lib/mnesia/examples/mnesia_meter.erl index ea74d8691b..68094c4431 100644 --- a/lib/mnesia/examples/mnesia_meter.erl +++ b/lib/mnesia/examples/mnesia_meter.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% @@ -407,7 +407,7 @@ run(Nodes, Config, FunOverhead) -> stop(Nodes), Res. -run_meter(M, Nodes, FunOverhead) when record(M, meter) -> +run_meter(M, Nodes, FunOverhead) when is_record(M, meter) -> io:format(".", []), case catch init_records(M#meter.init, ?TIMES) of {atomic, ok} -> diff --git a/lib/mnesia/src/Makefile b/lib/mnesia/src/Makefile index e032f563fa..1c8ec54605 100644 --- a/lib/mnesia/src/Makefile +++ b/lib/mnesia/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2009. All Rights Reserved. +# Copyright Ericsson AB 1996-2011. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -113,6 +113,8 @@ clean: docs: +$(TARGET_FILES): $(HRL_FILES) + # ---------------------------------------------------- # Special Build Targets # ---------------------------------------------------- diff --git a/lib/mnesia/src/mnesia.appup.src b/lib/mnesia/src/mnesia.appup.src index b3b9297db2..fe4e5e2e7a 100644 --- a/lib/mnesia/src/mnesia.appup.src +++ b/lib/mnesia/src/mnesia.appup.src @@ -1,69 +1,15 @@ %% -*- erlang -*- {"%VSN%", [ - {"4.4.12", - [ - {update, mnesia, soft, soft_purge, soft_purge, []}, - {update, mnesia_loader, soft, soft_purge, soft_purge, []}, - {update, mnesia_monitor, soft, soft_purge, soft_purge, []}, - {update, mnesia_tm, soft, soft_purge, soft_purge, []} - ] - }, - {"4.4.11", - [ - {update, mnesia, soft, soft_purge, soft_purge, []}, - {update, mnesia_loader, soft, soft_purge, soft_purge, []}, - {update, mnesia_monitor, soft, soft_purge, soft_purge, []}, - {update, mnesia_tm, soft, soft_purge, soft_purge, []}, - {update, mnesia_locker, soft, soft_purge, soft_purge, []}, - {update, mnesia_controller, soft, soft_purge, soft_purge, []} - ] - }, - {"4.4.10", - [ - {update, mnesia, soft, soft_purge, soft_purge, []}, - {update, mnesia_loader, soft, soft_purge, soft_purge, []}, - {update, mnesia_monitor, soft, soft_purge, soft_purge, []}, - {update, mnesia_tm, soft, soft_purge, soft_purge, []}, - {update, mnesia_locker, soft, soft_purge, soft_purge, []}, - {update, mnesia_controller, soft, soft_purge, soft_purge, []} - ] - }, - {"4.4.9", [{restart_application, mnesia}]}, - {"4.4.8", [{restart_application, mnesia}]}, - {"4.4.7", [{restart_application, mnesia}]} + {"4.4.19", [{restart_application, mnesia}]}, + {"4.4.18", [{restart_application, mnesia}]}, + {"4.4.17", [{restart_application, mnesia}]}, + {"4.4.16", [{restart_application, mnesia}]} ], [ - {"4.4.12", - [ - {update, mnesia, soft, soft_purge, soft_purge, []}, - {update, mnesia_loader, soft, soft_purge, soft_purge, []}, - {update, mnesia_monitor, soft, soft_purge, soft_purge, []}, - {update, mnesia_tm, soft, soft_purge, soft_purge, []} - ] - }, - {"4.4.11", - [ - {update, mnesia, soft, soft_purge, soft_purge, []}, - {update, mnesia_loader, soft, soft_purge, soft_purge, []}, - {update, mnesia_monitor, soft, soft_purge, soft_purge, []}, - {update, mnesia_tm, soft, soft_purge, soft_purge, []}, - {update, mnesia_locker, soft, soft_purge, soft_purge, []}, - {update, mnesia_controller, soft, soft_purge, soft_purge, []} - ] - }, - {"4.4.10", - [ - {update, mnesia, soft, soft_purge, soft_purge, []}, - {update, mnesia_loader, soft, soft_purge, soft_purge, []}, - {update, mnesia_monitor, soft, soft_purge, soft_purge, []}, - {update, mnesia_tm, soft, soft_purge, soft_purge, []}, - {update, mnesia_locker, soft, soft_purge, soft_purge, []}, - {update, mnesia_controller, soft, soft_purge, soft_purge, []} - ] - }, - {"4.4.9", [{restart_application, mnesia}]}, - {"4.4.8", [{restart_application, mnesia}]}, - {"4.4.7", [{restart_application, mnesia}]} + {"4.4.19", [{restart_application, mnesia}]}, + {"4.4.18", [{restart_application, mnesia}]}, + {"4.4.17", [{restart_application, mnesia}]}, + {"4.4.16", [{restart_application, mnesia}]} ] }. diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl index fb29007780..980a9c6213 100644 --- a/lib/mnesia/src/mnesia.erl +++ b/lib/mnesia/src/mnesia.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -39,6 +39,7 @@ %% Access within an activity - Lock acquisition lock/2, lock/4, + lock_table/2, read_lock_table/1, write_lock_table/1, @@ -92,7 +93,7 @@ add_table_copy/3, del_table_copy/2, move_table_copy/3, add_table_index/2, del_table_index/2, transform_table/3, transform_table/4, - change_table_copy_type/3, + change_table_copy_type/3, change_table_majority/2, read_table_property/2, write_table_property/2, delete_table_property/2, change_table_frag/2, clear_table/1, clear_table/4, @@ -302,7 +303,7 @@ ms() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Activity mgt --spec(abort/1 :: (_) -> no_return()). +-spec abort(_) -> no_return(). abort(Reason) -> exit({aborted, Reason}). @@ -415,6 +416,9 @@ lock(LockItem, LockKind) -> abort(no_transaction) end. +lock_table(Tab, LockKind) -> + lock({table, Tab}, LockKind). + lock(Tid, Ts, LockItem, LockKind) -> case element(1, Tid) of tid -> @@ -467,6 +471,8 @@ lock_table(Tid, Ts, Tab, LockKind) when is_atom(Tab) -> mnesia_locker:rlock_table(Tid, Store, Tab); write -> mnesia_locker:wlock_table(Tid, Store, Tab); + load -> + mnesia_locker:load_lock_table(Tid, Store, Tab); sticky_write -> mnesia_locker:sticky_wlock_table(Tid, Store, Tab); none -> @@ -1835,6 +1841,7 @@ do_dirty_rpc(Tab, Node, M, F, Args) -> %% Info %% Info about one table +-spec table_info(atom(), any()) -> any(). table_info(Tab, Item) -> case get(mnesia_activity_state) of undefined -> @@ -1868,7 +1875,7 @@ any_table_info(Tab, Item) when is_atom(Tab) -> type -> case ?catch_val({Tab, setorbag}) of {'EXIT', _} -> - bad_info_reply(Tab, Item); + abort({no_exists, Tab, Item}); Val -> Val end; @@ -1886,7 +1893,7 @@ any_table_info(Tab, Item) when is_atom(Tab) -> _ -> case ?catch_val({Tab, Item}) of {'EXIT', _} -> - bad_info_reply(Tab, Item); + abort({no_exists, Tab, Item}); Val -> Val end @@ -2454,6 +2461,9 @@ change_table_access_mode(T, Access) -> change_table_load_order(T, O) -> mnesia_schema:change_table_load_order(T, O). +change_table_majority(T, M) -> + mnesia_schema:change_table_majority(T, M). + set_master_nodes(Nodes) when is_list(Nodes) -> UseDir = system_info(use_dir), IsRunning = system_info(is_running), diff --git a/lib/mnesia/src/mnesia.hrl b/lib/mnesia/src/mnesia.hrl index d488d9364a..2375b72d59 100644 --- a/lib/mnesia/src/mnesia.hrl +++ b/lib/mnesia/src/mnesia.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -62,6 +62,7 @@ disc_only_copies = [], % [Node] load_order = 0, % Integer access_mode = read_write, % read_write | read_only + majority = false, % true | false index = [], % [Integer] snmp = [], % Snmp Ustruct local_content = false, % true | false diff --git a/lib/mnesia/src/mnesia_bup.erl b/lib/mnesia/src/mnesia_bup.erl index 37a8258d74..47dcdad7ac 100644 --- a/lib/mnesia/src/mnesia_bup.erl +++ b/lib/mnesia/src/mnesia_bup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -65,6 +65,8 @@ default_op = keep_tables }). +-type fallback_args() :: #fallback_args{}. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Backup iterator @@ -108,6 +110,7 @@ iter(R, Header, Schema, Fun, Acc, BupItems) -> Acc2 = Fun(BupItems, Header, Schema, Acc), iter(R, Header, Schema, Fun, Acc2, []). +-spec safe_apply(#restore{}, atom(), list()) -> tuple(). safe_apply(R, write, [_, Items]) when Items =:= [] -> R; safe_apply(R, What, Args) -> @@ -570,6 +573,7 @@ fallback_bup() -> mnesia_lib:dir(fallback_name()). fallback_tmp_name() -> "FALLBACK.TMP". %% fallback_full_tmp_name() -> mnesia_lib:dir(fallback_tmp_name()). +-spec fallback_receiver(pid(), fallback_args()) -> no_return(). fallback_receiver(Master, FA) -> process_flag(trap_exit, true), @@ -981,6 +985,7 @@ do_uninstall_fallback(FA) -> {error, Reason} end. +-spec uninstall_fallback_master(pid(), fallback_args()) -> no_return(). uninstall_fallback_master(ClientPid, FA) -> process_flag(trap_exit, true), diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl index 9bc480e619..1d3bd55b48 100644 --- a/lib/mnesia/src/mnesia_controller.erl +++ b/lib/mnesia/src/mnesia_controller.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% @@ -52,11 +52,13 @@ async_dump_log/1, sync_dump_log/1, connect_nodes/1, + connect_nodes/2, wait_for_schema_commit_lock/0, release_schema_commit_lock/0, create_table/1, get_disc_copy/1, - get_cstructs/0, + get_remote_cstructs/0, % new function + get_cstructs/0, % old function sync_and_block_table_whereabouts/4, sync_del_table_copy_whereabouts/2, block_table/1, @@ -71,6 +73,7 @@ add_active_replica/4, update/1, change_table_access_mode/1, + change_table_majority/1, del_active_replica/2, wait_for_tables/2, get_network_copy/2, @@ -94,9 +97,11 @@ load_and_reply/2, send_and_reply/2, wait_for_tables_init/2, - connect_nodes2/2 + connect_nodes2/3 ]). +-compile({no_auto_import,[error/2]}). + -import(mnesia_lib, [set/2, add/2]). -import(mnesia_lib, [fatal/2, error/2, verbose/2, dbg_out/2]). @@ -274,9 +279,51 @@ rec_tabs([], _, _, Init) -> unlink(Init), ok. -get_cstructs() -> +%% New function that does exactly what get_cstructs() used to do. +%% When this function is called, we know that the calling node knows +%% how to convert cstructs on the receiving end (should they differ). +get_remote_cstructs() -> call(get_cstructs). +%% Old function kept for backwards compatibility; converts cstructs before sending. +get_cstructs() -> + {cstructs, Cstructs, Running} = call(get_cstructs), + Node = node(group_leader()), + {cstructs, normalize_cstructs(Cstructs, Node), Running}. + +normalize_cstructs(Cstructs, Node) -> + %% backward-compatibility hack; normalize before returning + case rpc:call(Node, mnesia_lib, val, [{schema,cstruct}]) of + {badrpc, _} -> + %% assume it's not a schema merge + Cstructs; + #cstruct{} -> + %% same format + Cstructs; + Cstruct -> + %% some other format + RemoteFields = [F || {F,_} <- rpc:call(Node, mnesia_schema, cs2list, [Cstruct])], + [convert_cs(Cs, RemoteFields) || Cs <- Cstructs] + end. + +convert_cs(Cs, Fields) -> + MyFields = record_info(fields, cstruct), + convert(tl(tuple_to_list(Cs)), MyFields, Fields, []). + +convert([H|T], [F|FsL], [F|FsR], Acc) -> + convert(T, FsL, FsR, [H|Acc]); +convert([H|T], [Fl|FsL] = L, [Fr|FsR] = R, Acc) -> + case {lists:member(Fl, FsR), lists:member(Fr, FsL)} of + {true, false} -> + convert(T, L, FsR, [H|Acc]); + {false, true} -> + %% Field Fl doesn't exist on receiver side; skip. + convert(T, FsL, R, Acc) + end; +convert([], _, _, Acc) -> + list_to_tuple([cstruct|lists:reverse(Acc)]). + + update(Fun) -> call({update,Fun}). @@ -420,12 +467,15 @@ try_schedule_late_disc_load(Tabs, Reason, MsgTag) -> [[Tabs, Reason, MsgTag], AbortReason]) end. -connect_nodes(Ns) -> +connect_nodes(Ns) -> + connect_nodes(Ns, fun default_merge/1). + +connect_nodes(Ns, UserFun) -> case mnesia:system_info(is_running) of no -> {error, {node_not_running, node()}}; yes -> - Pid = spawn_link(?MODULE,connect_nodes2,[self(),Ns]), + Pid = spawn_link(?MODULE,connect_nodes2,[self(),Ns, UserFun]), receive {?MODULE, Pid, Res, New} -> case Res of @@ -443,7 +493,7 @@ connect_nodes(Ns) -> end end. -connect_nodes2(Father, Ns) -> +connect_nodes2(Father, Ns, UserFun) -> Current = val({current, db_nodes}), abcast([node()|Ns], {merging_schema, node()}), {NewC, OldC} = mnesia_recover:connect_nodes(Ns), @@ -451,7 +501,7 @@ connect_nodes2(Father, Ns) -> New1 = mnesia_lib:intersect(Ns, Connected), New = New1 -- Current, process_flag(trap_exit, true), - Res = try_merge_schema(New), + Res = try_merge_schema(New, [], UserFun), Msg = {schema_is_merged, [], late_merge, []}, multicall([node()|Ns], Msg), After = val({current, db_nodes}), @@ -465,7 +515,7 @@ connect_nodes2(Father, Ns) -> merge_schema() -> AllNodes = mnesia_lib:all_nodes(), - case try_merge_schema(AllNodes) of + case try_merge_schema(AllNodes, [node()], fun default_merge/1) of ok -> schema_is_merged(); {aborted, {throw, Str}} when is_list(Str) -> @@ -474,11 +524,20 @@ merge_schema() -> fatal("Failed to merge schema: ~p~n", [Else]) end. -try_merge_schema(Nodes) -> - case mnesia_schema:merge_schema() of +default_merge(F) -> + F([]). + +try_merge_schema(Nodes, Told0, UserFun) -> + case mnesia_schema:merge_schema(UserFun) of {atomic, not_merged} -> %% No more nodes that we need to merge the schema with - ok; + %% Ensure we have told everybody that we are running + case val({current,db_nodes}) -- mnesia_lib:uniq(Told0) of + [] -> ok; + Tell -> + im_running(Tell, [node()]), + ok + end; {atomic, {merged, OldFriends, NewFriends}} -> %% Check if new nodes has been added to the schema Diff = mnesia_lib:all_nodes() -- [node() | Nodes], @@ -487,12 +546,18 @@ try_merge_schema(Nodes) -> %% Tell everybody to adopt orphan tables im_running(OldFriends, NewFriends), im_running(NewFriends, OldFriends), - - try_merge_schema(Nodes); + Told = case lists:member(node(), NewFriends) of + true -> Told0 ++ OldFriends; + false -> Told0 ++ NewFriends + end, + try_merge_schema(Nodes, Told, UserFun); {atomic, {"Cannot get cstructs", Node, Reason}} -> dbg_out("Cannot get cstructs, Node ~p ~p~n", [Node, Reason]), - timer:sleep(1000), % Avoid a endless loop look alike - try_merge_schema(Nodes); + timer:sleep(300), % Avoid a endless loop look alike + try_merge_schema(Nodes, Told0, UserFun); + {aborted, {shutdown, _}} -> %% One of the nodes is going down + timer:sleep(300), % Avoid a endless loop look alike + try_merge_schema(Nodes, Told0, UserFun); Other -> Other end. @@ -669,7 +734,8 @@ handle_call({update_where_to_write, [add, Tab, AddNode], _From}, _Dummy, State) case lists:member(AddNode, Current) and (State#state.schema_is_merged == true) of true -> - mnesia_lib:add_lsort({Tab, where_to_write}, AddNode); + mnesia_lib:add_lsort({Tab, where_to_write}, AddNode), + update_where_to_wlock(Tab); false -> ignore end, @@ -906,6 +972,7 @@ handle_cast(unblock_controller, State) -> handle_cast({mnesia_down, Node}, State) -> maybe_log_mnesia_down(Node), mnesia_lib:del({current, db_nodes}, Node), + mnesia_lib:unset({node_up, Node}), mnesia_checkpoint:tm_mnesia_down(Node), Alltabs = val({schema, tables}), reconfigure_tables(Node, Alltabs), @@ -968,11 +1035,12 @@ handle_cast(Msg, State) when State#state.schema_is_merged /= true -> %% This must be done after schema_is_merged otherwise adopt_orphan %% might trigger a table load from wrong nodes as a result of that we don't %% know which tables we can load safly first. -handle_cast({im_running, _Node, NewFriends}, State) -> +handle_cast({im_running, Node, NewFriends}, State) -> LocalTabs = mnesia_lib:local_active_tables() -- [schema], RemoveLocalOnly = fun(Tab) -> not val({Tab, local_content}) end, Tabs = lists:filter(RemoveLocalOnly, LocalTabs), - Ns = mnesia_lib:intersect(NewFriends, val({current, db_nodes})), + Nodes = mnesia_lib:union([Node],val({current, db_nodes})), + Ns = mnesia_lib:intersect(NewFriends, Nodes), abcast(Ns, {adopt_orphans, node(), Tabs}), noreply(State); @@ -1033,30 +1101,33 @@ handle_cast({master_nodes_updated, Tab, Masters}, State) -> end; handle_cast({adopt_orphans, Node, Tabs}, State) -> - State2 = node_has_tabs(Tabs, Node, State), - %% Register the other node as up and running - mnesia_recover:log_mnesia_up(Node), - verbose("Logging mnesia_up ~w~n",[Node]), - mnesia_lib:report_system_event({mnesia_up, Node}), - - %% Load orphan tables - LocalTabs = val({schema, local_tables}) -- [schema], - Nodes = val({current, db_nodes}), - {LocalOrphans, RemoteMasters} = - orphan_tables(LocalTabs, Node, Nodes, [], []), - Reason = {adopt_orphan, node()}, - mnesia_late_loader:async_late_disc_load(node(), LocalOrphans, Reason), - - Fun = - fun(N) -> - RemoteOrphans = - [Tab || {Tab, Ns} <- RemoteMasters, - lists:member(N, Ns)], - mnesia_late_loader:maybe_async_late_disc_load(N, RemoteOrphans, Reason) - end, - lists:foreach(Fun, Nodes), + case ?catch_val({node_up,Node}) of + true -> ignore; + _ -> + %% Register the other node as up and running + set({node_up, Node}, true), + mnesia_recover:log_mnesia_up(Node), + verbose("Logging mnesia_up ~w~n",[Node]), + mnesia_lib:report_system_event({mnesia_up, Node}), + %% Load orphan tables + LocalTabs = val({schema, local_tables}) -- [schema], + Nodes = val({current, db_nodes}), + {LocalOrphans, RemoteMasters} = + orphan_tables(LocalTabs, Node, Nodes, [], []), + Reason = {adopt_orphan, node()}, + mnesia_late_loader:async_late_disc_load(node(), LocalOrphans, Reason), + + Fun = + fun(N) -> + RemoteOrphans = + [Tab || {Tab, Ns} <- RemoteMasters, + lists:member(N, Ns)], + mnesia_late_loader:maybe_async_late_disc_load(N, RemoteOrphans, Reason) + end, + lists:foreach(Fun, Nodes) + end, noreply(State2); handle_cast(Msg, State) -> @@ -1664,6 +1735,8 @@ add_active_replica(Tab, Node, Storage, AccessMode) -> set(Var, mark_blocked_tab(Blocked, Del)), mnesia_lib:del({Tab, where_to_write}, Node) end, + + update_where_to_wlock(Tab), add({Tab, active_replicas}, Node). del_active_replica(Tab, Node) -> @@ -1673,7 +1746,8 @@ del_active_replica(Tab, Node) -> New = lists:sort(Del), set(Var, mark_blocked_tab(Blocked, New)), % where_to_commit mnesia_lib:del({Tab, active_replicas}, Node), - mnesia_lib:del({Tab, where_to_write}, Node). + mnesia_lib:del({Tab, where_to_write}, Node), + update_where_to_wlock(Tab). change_table_access_mode(Cs) -> W = fun() -> @@ -1682,7 +1756,22 @@ change_table_access_mode(Cs) -> val({Tab, active_replicas})) end, update(W). - + +change_table_majority(Cs) -> + W = fun() -> + Tab = Cs#cstruct.name, + set({Tab, majority}, Cs#cstruct.majority), + update_where_to_wlock(Tab) + end, + update(W). + +update_where_to_wlock(Tab) -> + WNodes = val({Tab, where_to_write}), + Majority = case catch val({Tab, majority}) of + true -> true; + _ -> false + end, + set({Tab, where_to_wlock}, {WNodes, Majority}). %% node To now has tab loaded, but this must be undone %% This code is rpc:call'ed from the tab_copier process @@ -1842,17 +1931,20 @@ reply(ReplyTo, Reply) -> add_worker(Worker = #dump_log{}, State) -> InitBy = Worker#dump_log.initiated_by, Queue = State#state.dumper_queue, - case lists:keymember(InitBy, #dump_log.initiated_by, Queue) of - true when Worker#dump_log.opt_reply_to == undefined -> - %% The same threshold has been exceeded again, - %% before we have had the possibility to - %% process the older one. - DetectedBy = {dump_log, InitBy}, - Event = {mnesia_overload, DetectedBy}, - mnesia_lib:report_system_event(Event); - _ -> - ignore - end, + Status = + case lists:keymember(InitBy, #dump_log.initiated_by, Queue) of + true when Worker#dump_log.opt_reply_to == undefined -> + %% The same threshold has been exceeded again, + %% before we have had the possibility to + %% process the older one. + DetectedBy = {dump_log, InitBy}, + Event = {mnesia_overload, DetectedBy}, + mnesia_lib:report_system_event(Event), + true; + _ -> + false + end, + mnesia_recover:log_dump_overload(Status), Queue2 = Queue ++ [Worker], State2 = State#state{dumper_queue = Queue2}, opt_start_worker(State2); diff --git a/lib/mnesia/src/mnesia_dumper.erl b/lib/mnesia/src/mnesia_dumper.erl index f669d009c6..f8d7664156 100644 --- a/lib/mnesia/src/mnesia_dumper.erl +++ b/lib/mnesia/src/mnesia_dumper.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -214,7 +214,12 @@ insert_rec(Rec, InPlace, InitBy, LogV) when is_record(Rec, commit) -> {Tid, committed} -> do_insert_rec(Tid, Rec, InPlace, InitBy, LogV); {Tid, aborted} -> - mnesia_schema:undo_prepare_commit(Tid, Rec) + case InitBy of + startup -> + mnesia_schema:undo_prepare_commit(Tid, Rec); + _ -> + ok + end end; insert_rec(H, _InPlace, _InitBy, _LogV) when is_record(H, log_header) -> CurrentVersion = mnesia_log:version(), @@ -359,7 +364,7 @@ dets_insert(Op,Tab,Key,Val) -> ok = dets:delete_object(Tab, Val); clear_table -> dets_cleared(Tab), - ok = dets:match_delete(Tab, '_') + ok = dets:delete_all_objects(Tab) end. dets_updated(Tab,Key) -> @@ -643,7 +648,7 @@ insert_op(Tid, _, {op, create_table, TabDef}, InPlace, InitBy) -> true -> ignore; false -> mnesia_log:open_log(temp, - mnesia_log:dcl_log_header(), + mnesia_log:dcd_log_header(), Dcd, false, false, @@ -871,7 +876,11 @@ insert_op(Tid, _, {op, add_index, Pos, TabDef}, InPlace, InitBy) -> startup -> ignore; _ -> - mnesia_index:init_indecies(Tab, Storage, [Pos]) + case val({Tab,where_to_read}) of + nowhere -> ignore; + _ -> + mnesia_index:init_indecies(Tab, Storage, [Pos]) + end end; insert_op(Tid, _, {op, del_index, Pos, TabDef}, InPlace, InitBy) -> @@ -896,6 +905,14 @@ insert_op(Tid, _, {op, change_table_access_mode,TabDef, _OldAccess, _Access}, In end, insert_cstruct(Tid, Cs, true, InPlace, InitBy); +insert_op(Tid, _, {op, change_table_majority,TabDef, _OldAccess, _Access}, InPlace, InitBy) -> + Cs = mnesia_schema:list2cs(TabDef), + case InitBy of + startup -> ignore; + _ -> mnesia_controller:change_table_majority(Cs) + end, + insert_cstruct(Tid, Cs, true, InPlace, InitBy); + insert_op(Tid, _, {op, change_table_load_order, TabDef, _OldLevel, _Level}, InPlace, InitBy) -> Cs = mnesia_schema:list2cs(TabDef), insert_cstruct(Tid, Cs, true, InPlace, InitBy); diff --git a/lib/mnesia/src/mnesia_frag.erl b/lib/mnesia/src/mnesia_frag.erl index a2958ab461..9e77fe0b9f 100644 --- a/lib/mnesia/src/mnesia_frag.erl +++ b/lib/mnesia/src/mnesia_frag.erl @@ -1,7 +1,7 @@ %%% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -209,7 +209,7 @@ first(ActivityId, Opaque, Tab) -> end end. -search_first(ActivityId, Opaque, Tab, N, FH) when N =< FH#frag_state.n_fragments -> +search_first(ActivityId, Opaque, Tab, N, FH) when N < FH#frag_state.n_fragments -> NextN = N + 1, NextFrag = n_to_frag_name(Tab, NextN), case mnesia:first(ActivityId, Opaque, NextFrag) of @@ -448,13 +448,15 @@ do_remote_select(_ReplyTo, _Ref, [], _MatchSpec) -> local_collect(Ref, Pid, Type, LocalMatch, OldSelectFun) -> receive - {local_select, Ref, LocalRes} -> - remote_collect(Ref, Type, LocalRes, LocalMatch, OldSelectFun); + {local_select, Ref, ok} -> + remote_collect_ok(Ref, Type, LocalMatch, OldSelectFun); + {local_select, Ref, {error, Reason}} -> + remote_collect_error(Ref, Type, Reason, OldSelectFun); {'EXIT', Pid, Reason} -> - remote_collect(Ref, Type, {error, Reason}, [], OldSelectFun) + remote_collect_error(Ref, Type, Reason, OldSelectFun) end. -remote_collect(Ref, Type, LocalRes = ok, Acc, OldSelectFun) -> +remote_collect_ok(Ref, Type, Acc, OldSelectFun) -> receive {remote_select, Ref, Node, RemoteRes} -> case RemoteRes of @@ -463,19 +465,21 @@ remote_collect(Ref, Type, LocalRes = ok, Acc, OldSelectFun) -> ordered_set -> lists:merge(RemoteMatch, Acc); _ -> RemoteMatch ++ Acc end, - remote_collect(Ref, Type, LocalRes, Matches, OldSelectFun); + remote_collect_ok(Ref, Type, Matches, OldSelectFun); _ -> - remote_collect(Ref, Type, {error, {node_not_running, Node}}, [], OldSelectFun) + Reason = {node_not_running, Node}, + remote_collect_error(Ref, Type, Reason, OldSelectFun) end after 0 -> Acc - end; -remote_collect(Ref, Type, LocalRes = {error, Reason}, _Acc, OldSelectFun) -> + end. + +remote_collect_error(Ref, Type, Reason, OldSelectFun) -> receive {remote_select, Ref, _Node, _RemoteRes} -> - remote_collect(Ref, Type, LocalRes, [], OldSelectFun) + remote_collect_error(Ref, Type, Reason, OldSelectFun) after 0 -> - mnesia:abort(Reason) + mnesia:abort({error, Reason}) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/mnesia/src/mnesia_index.erl b/lib/mnesia/src/mnesia_index.erl index 4e6e8a997c..61210d7e55 100644 --- a/lib/mnesia/src/mnesia_index.erl +++ b/lib/mnesia/src/mnesia_index.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/mnesia/src/mnesia_lib.erl b/lib/mnesia/src/mnesia_lib.erl index dba808e66e..e8b8c58c70 100644 --- a/lib/mnesia/src/mnesia_lib.erl +++ b/lib/mnesia/src/mnesia_lib.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% @@ -96,6 +96,8 @@ exists/1, fatal/2, get_node_number/0, + have_majority/2, + have_majority/3, fix_error/1, important/2, incr_counter/1, @@ -113,6 +115,9 @@ mkcore/1, not_active_here/1, other_val/2, + overload_read/0, + overload_read/1, + overload_set/2, pad_name/3, random_time/2, read_counter/1, @@ -396,7 +401,7 @@ other_val(Var, Other) -> pr_other(Var, Other) end. --spec(pr_other/2 :: (_,_) -> no_return()). +-spec pr_other(_,_) -> no_return(). pr_other(Var, Other) -> Why = @@ -551,6 +556,33 @@ cs_to_nodes(Cs) -> Cs#cstruct.disc_only_copies ++ Cs#cstruct.disc_copies ++ Cs#cstruct.ram_copies. + +overload_types() -> + [mnesia_tm, mnesia_dump_log]. + +valid_overload_type(T) -> + case lists:member(T, overload_types()) of + false -> + erlang:error(bad_type); + true -> + true + end. + +overload_set(Type, Bool) when is_boolean(Bool) -> + valid_overload_type(Type), + set({overload, Type}, Bool). + +overload_read() -> + [{T, overload_read(T)} || T <- overload_types()]. + +overload_read(T) -> + case ?catch_val({overload, T}) of + {'EXIT',_} -> + valid_overload_type(T), + false; + Flag when is_boolean(Flag) -> + Flag + end. dist_coredump() -> dist_coredump(all_nodes()). @@ -630,6 +662,14 @@ proc_info(_) -> false. get_node_number() -> {node(), self()}. +have_majority(Tab, HaveNodes) -> + have_majority(Tab, val({Tab, all_nodes}), HaveNodes). + +have_majority(_Tab, AllNodes, HaveNodes) -> + Missing = AllNodes -- HaveNodes, + Present = AllNodes -- Missing, + length(Present) > length(Missing). + read_log_files() -> [{F, catch file:read_file(F)} || F <- mnesia_log:log_files()]. @@ -1101,12 +1141,18 @@ db_erase(ram_copies, Tab, Key) -> ?ets_delete(Tab, Key), ok; db_erase(disc_copies, Tab, Key) -> ?ets_delete(Tab, Key), ok; db_erase(disc_only_copies, Tab, Key) -> dets:delete(Tab, Key). +db_match_erase(Tab, '_') -> + db_delete_all(val({Tab, storage_type}),Tab); db_match_erase(Tab, Pat) -> db_match_erase(val({Tab, storage_type}), Tab, Pat). db_match_erase(ram_copies, Tab, Pat) -> ?ets_match_delete(Tab, Pat), ok; db_match_erase(disc_copies, Tab, Pat) -> ?ets_match_delete(Tab, Pat), ok; db_match_erase(disc_only_copies, Tab, Pat) -> dets:match_delete(Tab, Pat). +db_delete_all(ram_copies, Tab) -> ets:delete_all_objects(Tab); +db_delete_all(disc_copies, Tab) -> ets:delete_all_objects(Tab); +db_delete_all(disc_only_copies, Tab) -> dets:delete_all_objects(Tab). + db_first(Tab) -> db_first(val({Tab, storage_type}), Tab). db_first(ram_copies, Tab) -> ?ets_first(Tab); diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl index 3de329503e..eb83168498 100644 --- a/lib/mnesia/src/mnesia_loader.erl +++ b/lib/mnesia/src/mnesia_loader.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2010. All Rights Reserved. +%% Copyright Ericsson AB 1998-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -27,7 +27,6 @@ net_load_table/4, send_table/3]). --export([old_node_init_table/6]). %% Spawned old node protocol conversion hack -export([spawned_receiver/8]). %% Spawned lock taking process -import(mnesia_lib, [set/2, fatal/2, verbose/2, dbg_out/2]). @@ -36,7 +35,7 @@ val(Var) -> case ?catch_val(Var) of - {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason); + {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason); Value -> Value end. @@ -51,7 +50,7 @@ disc_load_table(Tab, Reason) -> ?eval_debug_fun({?MODULE, do_get_disc_copy}, [{tab, Tab}, {reason, Reason}, - {storage, Storage}, + {storage, Storage}, {type, Type}]), do_get_disc_copy2(Tab, Reason, Storage, Type). @@ -63,19 +62,19 @@ do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == disc_copies -> %% NOW we create the actual table Repair = mnesia_monitor:get_env(auto_repair), Args = [{keypos, 2}, public, named_table, Type], - case Reason of + case Reason of {dumper, _} -> %% Resources allready allocated ignore; _ -> mnesia_monitor:mktab(Tab, Args), - Count = mnesia_log:dcd2ets(Tab, Repair), + Count = mnesia_log:dcd2ets(Tab, Repair), case ets:info(Tab, size) of X when X < Count * 4 -> - ok = mnesia_log:ets2dcd(Tab); + ok = mnesia_log:ets2dcd(Tab); _ -> ignore end - end, + end, mnesia_index:init_index(Tab, Storage), snmpify(Tab, Storage), set({Tab, load_node}, node()), @@ -84,7 +83,7 @@ do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == disc_copies -> do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == ram_copies -> Args = [{keypos, 2}, public, named_table, Type], - case Reason of + case Reason of {dumper, _} -> %% Resources allready allocated ignore; _ -> @@ -94,12 +93,12 @@ do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == ram_copies -> Repair = mnesia_monitor:get_env(auto_repair), case mnesia_monitor:use_dir() of true -> - case mnesia_lib:exists(Fname) of + case mnesia_lib:exists(Fname) of true -> mnesia_log:dcd2ets(Tab, Repair); false -> case mnesia_lib:exists(Datname) of true -> - mnesia_lib:dets_to_ets(Tab, Tab, Datname, + mnesia_lib:dets_to_ets(Tab, Tab, Datname, Type, Repair, no); false -> false @@ -154,11 +153,11 @@ do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == disc_only_copies - %% Disable rehashing of table %% Release read lock on table %% Send table to receiver in chunks -%% +%% %% Grab read lock on table %% Block dirty updates %% Update wherabouts -%% +%% %% Cancel the update subscription %% Process the subscription events %% Optionally dump to disc @@ -166,7 +165,7 @@ do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == disc_only_copies - %% Release read lock on table %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --define(MAX_TRANSFER_SIZE, 7500). +-define(MAX_TRANSFER_SIZE, 7500). -define(MAX_RAM_FILE_SIZE, 1000000). -define(MAX_RAM_TRANSFERS, (?MAX_RAM_FILE_SIZE div ?MAX_TRANSFER_SIZE) + 1). -define(MAX_NOPACKETS, 20). @@ -187,14 +186,14 @@ try_net_load_table(Tab, Reason, Ns, Cs) -> do_get_network_copy(Tab, _Reason, _Ns, unknown, _Cs) -> verbose("Local table copy of ~p has recently been deleted, ignored.~n", [Tab]), {not_loaded, storage_unknown}; -do_get_network_copy(Tab, Reason, Ns, Storage, Cs) -> +do_get_network_copy(Tab, Reason, Ns, Storage, Cs) -> [Node | Tail] = Ns, case lists:member(Node,val({current, db_nodes})) of true -> dbg_out("Getting table ~p (~p) from node ~p: ~p~n", [Tab, Storage, Node, Reason]), ?eval_debug_fun({?MODULE, do_get_network_copy}, - [{tab, Tab}, {reason, Reason}, + [{tab, Tab}, {reason, Reason}, {nodes, Ns}, {storage, Storage}]), case init_receiver(Node, Tab, Storage, Cs, Reason) of ok -> @@ -208,7 +207,7 @@ do_get_network_copy(Tab, Reason, Ns, Storage, Cs) -> restart -> try_net_load_table(Tab, Reason, Tail ++ [Node], Cs); down -> - try_net_load_table(Tab, Reason, Tail, Cs) + try_net_load_table(Tab, Reason, Tail, Cs) end; false -> try_net_load_table(Tab, Reason, Tail, Cs) @@ -223,10 +222,10 @@ do_snmpify(Tab, Us, Storage) -> Snmp = mnesia_snmp_hook:create_table(Us, Tab, Storage), set({Tab, {index, snmp}}, Snmp). -%% Start the recieiver +%% Start the recieiver init_receiver(Node, Tab, Storage, Cs, Reas={dumper,add_table_copy}) -> case start_remote_sender(Node, Tab, Storage) of - {SenderPid, TabSize, DetsData} -> + {SenderPid, TabSize, DetsData} -> start_receiver(Tab,Storage,Cs,SenderPid,TabSize,DetsData,Reas); Else -> Else @@ -234,21 +233,21 @@ init_receiver(Node, Tab, Storage, Cs, Reas={dumper,add_table_copy}) -> init_receiver(Node, Tab,Storage,Cs,Reason) -> %% Grab a schema lock to avoid deadlock between table_loader and schema_commit dumping. %% Both may grab tables-locks in different order. - Load = - fun() -> - {_,Tid,Ts} = get(mnesia_activity_state), + Load = + fun() -> + {_,Tid,Ts} = get(mnesia_activity_state), mnesia_locker:rlock(Tid, Ts#tidstore.store, {schema, Tab}), - %% Check that table still exists + %% Check that table still exists Active = val({Tab, active_replicas}), %% Check that we havn't loaded it already case val({Tab,where_to_read}) == node() of true -> ok; _ -> - %% And that sender still got a copy - %% (something might have happend while + %% And that sender still got a copy + %% (something might have happend while %% we where waiting for the lock) true = lists:member(Node, Active), - {SenderPid, TabSize, DetsData} = + {SenderPid, TabSize, DetsData} = start_remote_sender(Node,Tab,Storage), Init = table_init_fun(SenderPid), Args = [self(),Tab,Storage,Cs,SenderPid, @@ -258,18 +257,18 @@ init_receiver(Node, Tab,Storage,Cs,Reason) -> wait_on_load_complete(Pid) end end, - Res = + Res = case mnesia:transaction(Load, 20) of - {atomic, {error,Result}} when - element(1,Reason) == dumper -> + {atomic, {error,Result}} when + element(1,Reason) == dumper -> {error,Result}; - {atomic, {error,Result}} -> + {atomic, {error,Result}} -> fatal("Cannot create table ~p: ~p~n", [[Tab, Storage], Result]); {atomic, Result} -> Result; {aborted, nomore} -> restart; - {aborted, _Reas} -> - verbose("Receiver failed on ~p from ~p:~nReason: ~p~n", + {aborted, _Reas} -> + verbose("Receiver failed on ~p from ~p:~nReason: ~p~n", [Tab,Node,_Reas]), down %% either this node or sender is dying end, @@ -279,7 +278,7 @@ init_receiver(Node, Tab,Storage,Cs,Reason) -> start_remote_sender(Node,Tab,Storage) -> mnesia_controller:start_remote_sender(Node, Tab, self(), Storage), put(mnesia_table_sender_node, {Tab, Node}), - receive + receive {SenderPid, {first, TabSize}} -> {SenderPid, TabSize, false}; {SenderPid, {first, TabSize, DetsData}} -> @@ -291,22 +290,14 @@ start_remote_sender(Node,Tab,Storage) -> end. table_init_fun(SenderPid) -> - PConv = mnesia_monitor:needs_protocol_conversion(node(SenderPid)), - MeMyselfAndI = self(), fun(read) -> - Receiver = - if - PConv == true -> - MeMyselfAndI ! {actual_tabrec, self()}, - MeMyselfAndI; %% Old mnesia - PConv == false -> self() - end, + Receiver = self(), SenderPid ! {Receiver, more}, get_data(SenderPid, Receiver) end. %% Add_table_copy get's it's own locks. -start_receiver(Tab,Storage,Cs,SenderPid,TabSize,DetsData,{dumper,add_table_copy}) -> +start_receiver(Tab,Storage,Cs,SenderPid,TabSize,DetsData,{dumper,add_table_copy}) -> Init = table_init_fun(SenderPid), case do_init_table(Tab,Storage,Cs,SenderPid,TabSize,DetsData,self(), Init) of Err = {error, _} -> @@ -317,8 +308,8 @@ start_receiver(Tab,Storage,Cs,SenderPid,TabSize,DetsData,{dumper,add_table_copy} end. spawned_receiver(ReplyTo,Tab,Storage,Cs, SenderPid,TabSize,DetsData, Init) -> - process_flag(trap_exit, true), - Done = do_init_table(Tab,Storage,Cs, + process_flag(trap_exit, true), + Done = do_init_table(Tab,Storage,Cs, SenderPid,TabSize,DetsData, ReplyTo, Init), ReplyTo ! {self(),Done}, @@ -327,17 +318,17 @@ spawned_receiver(ReplyTo,Tab,Storage,Cs, SenderPid,TabSize,DetsData, Init) -> exit(normal). wait_on_load_complete(Pid) -> - receive - {Pid, Res} -> + receive + {Pid, Res} -> Res; - {'EXIT', Pid, Reason} -> + {'EXIT', Pid, Reason} -> exit(Reason); - Else -> + Else -> Pid ! Else, wait_on_load_complete(Pid) end. -do_init_table(Tab,Storage,Cs,SenderPid, +do_init_table(Tab,Storage,Cs,SenderPid, TabSize,DetsInfo,OrigTabRec,Init) -> case create_table(Tab, TabSize, Storage, Cs) of {Storage,Tab} -> @@ -345,11 +336,9 @@ do_init_table(Tab,Storage,Cs,SenderPid, Node = node(SenderPid), put(mnesia_table_receiver, {Tab, Node, SenderPid}), mnesia_tm:block_tab(Tab), - PConv = mnesia_monitor:needs_protocol_conversion(Node), - - case init_table(Tab,Storage,Init,PConv,DetsInfo,SenderPid) of - ok -> - tab_receiver(Node,Tab,Storage,Cs,PConv,OrigTabRec); + case init_table(Tab,Storage,Init,DetsInfo,SenderPid) of + ok -> + tab_receiver(Node,Tab,Storage,Cs,OrigTabRec); Reason -> Msg = "[d]ets:init table failed", verbose("~s: ~p: ~p~n", [Msg, Tab, Reason]), @@ -360,7 +349,7 @@ do_init_table(Tab,Storage,Cs,SenderPid, end. create_table(Tab, TabSize, Storage, Cs) -> - if + if Storage == disc_only_copies -> mnesia_lib:lock_table(Tab), Tmp = mnesia_lib:tab2tmp(Tab), @@ -390,54 +379,30 @@ create_table(Tab, TabSize, Storage, Cs) -> end end. -tab_receiver(Node, Tab, Storage, Cs, PConv, OrigTabRec) -> +tab_receiver(Node, Tab, Storage, Cs, OrigTabRec) -> receive - {SenderPid, {no_more, DatBin}} when PConv == false -> + {SenderPid, {no_more, DatBin}} -> finish_copy(Storage,Tab,Cs,SenderPid,DatBin,OrigTabRec); - - %% Protocol conversion hack - {SenderPid, {no_more, DatBin}} when is_pid(PConv) -> - PConv ! {SenderPid, no_more}, - receive - {old_init_table_complete, ok} -> - finish_copy(Storage, Tab, Cs, SenderPid, DatBin,OrigTabRec); - {old_init_table_complete, Reason} -> - Msg = "OLD: [d]ets:init table failed", - verbose("~s: ~p: ~p~n", [Msg, Tab, Reason]), - down(Tab, Storage) - end; - - {actual_tabrec, Pid} -> - tab_receiver(Node, Tab, Storage, Cs, Pid,OrigTabRec); - - {SenderPid, {more, [Recs]}} when is_pid(PConv) -> - PConv ! {SenderPid, {more, Recs}}, %% Forward Msg to OldNodes - tab_receiver(Node, Tab, Storage, Cs, PConv,OrigTabRec); - {'EXIT', PConv, Reason} -> %% [d]ets:init process crashed - Msg = "Receiver crashed", - verbose("~s: ~p: ~p~n", [Msg, Tab, Reason]), - down(Tab, Storage); - %% Protocol conversion hack {copier_done, Node} -> verbose("Sender of table ~p crashed on node ~p ~n", [Tab, Node]), down(Tab, Storage); - + {'EXIT', Pid, Reason} -> handle_exit(Pid, Reason), - tab_receiver(Node, Tab, Storage, Cs, PConv,OrigTabRec) + tab_receiver(Node, Tab, Storage, Cs, OrigTabRec) end. make_table_fun(Pid, TabRec) -> fun(close) -> ok; (read) -> - get_data(Pid, TabRec) + get_data(Pid, TabRec) end. get_data(Pid, TabRec) -> - receive + receive {Pid, {more_z, CompressedRecs}} when is_binary(CompressedRecs) -> Pid ! {TabRec, more}, {zlib_uncompress(CompressedRecs), make_table_fun(Pid,TabRec)}; @@ -448,7 +413,7 @@ get_data(Pid, TabRec) -> end_of_input; {copier_done, Node} -> case node(Pid) of - Node -> + Node -> {copier_done, Node}; _ -> get_data(Pid, TabRec) @@ -458,10 +423,10 @@ get_data(Pid, TabRec) -> get_data(Pid, TabRec) end. -init_table(Tab, disc_only_copies, Fun, false, DetsInfo,Sender) -> +init_table(Tab, disc_only_copies, Fun, DetsInfo,Sender) -> ErtsVer = erlang:system_info(version), case DetsInfo of - {ErtsVer, DetsData} -> + {ErtsVer, DetsData} -> Res = (catch dets:is_compatible_bchunk_format(Tab, DetsData)), case Res of {'EXIT',{undef,[{dets,_,_}|_]}} -> @@ -481,28 +446,19 @@ init_table(Tab, disc_only_copies, Fun, false, DetsInfo,Sender) -> _ -> dets:init_table(Tab, Fun) end; -init_table(Tab, _, Fun, false, _DetsInfo,_) -> +init_table(Tab, _, Fun, _DetsInfo,_) -> case catch ets:init_table(Tab, Fun) of true -> ok; {'EXIT', Else} -> Else - end; -init_table(Tab, Storage, Fun, true, _DetsInfo, Sender) -> %% Old Nodes - spawn_link(?MODULE, old_node_init_table, - [Tab, Storage, Fun, self(), false, Sender]), - ok. + end. -old_node_init_table(Tab, Storage, Fun, TabReceiver, DetsInfo,Sender) -> - Res = init_table(Tab, Storage, Fun, false, DetsInfo,Sender), - TabReceiver ! {old_init_table_complete, Res}, - unlink(TabReceiver), - ok. finish_copy(Storage,Tab,Cs,SenderPid,DatBin,OrigTabRec) -> TabRef = {Storage, Tab}, subscr_receiver(TabRef, Cs#cstruct.record_name), case handle_last(TabRef, Cs#cstruct.type, DatBin) of - ok -> + ok -> mnesia_index:init_index(Tab, Storage), snmpify(Tab, Storage), %% OrigTabRec must not be the spawned tab-receiver @@ -534,7 +490,7 @@ subscr_receiver(TabRef = {_, Tab}, RecName) -> ok end. -handle_event(TabRef, write, Rec) -> +handle_event(TabRef, write, Rec) -> db_put(TabRef, Rec); handle_event(TabRef, delete, {_Tab, Key}) -> db_erase(TabRef, Key); @@ -545,8 +501,8 @@ handle_event(TabRef, clear_table, {_Tab, _Key}) -> handle_last({disc_copies, Tab}, _Type, nobin) -> Ret = mnesia_log:ets2dcd(Tab), - Fname = mnesia_lib:tab2dat(Tab), - case mnesia_lib:exists(Fname) of + Fname = mnesia_lib:tab2dat(Tab), + case mnesia_lib:exists(Fname) of true -> %% Remove old .DAT files. file:delete(Fname); false -> @@ -653,31 +609,29 @@ send_table(Pid, Tab, RemoteS) -> {error, {no_exists, Tab}}; Storage -> %% Send first - TabSize = mnesia:table_info(Tab, size), - Pconvert = mnesia_monitor:needs_protocol_conversion(node(Pid)), + TabSize = mnesia:table_info(Tab, size), KeysPerTransfer = calc_nokeys(Storage, Tab), ChunkData = dets:info(Tab, bchunk_format), - UseDetsChunk = - Storage == RemoteS andalso - Storage == disc_only_copies andalso - ChunkData /= undefined andalso - Pconvert == false, - if + UseDetsChunk = + Storage == RemoteS andalso + Storage == disc_only_copies andalso + ChunkData /= undefined, + if UseDetsChunk == true -> DetsInfo = erlang:system_info(version), Pid ! {self(), {first, TabSize, {DetsInfo, ChunkData}}}; true -> Pid ! {self(), {first, TabSize}} end, - + %% Debug info put(mnesia_table_sender, {Tab, node(Pid), Pid}), {Init, Chunk} = reader_funcs(UseDetsChunk, Tab, Storage, KeysPerTransfer), - + SendIt = fun() -> prepare_copy(Pid, Tab, Storage), - send_more(Pid, 1, Chunk, Init(), Tab, Pconvert), + send_more(Pid, 1, Chunk, Init(), Tab), finish_copy(Pid, Tab, Storage, RemoteS) end, @@ -698,11 +652,11 @@ send_table(Pid, Tab, RemoteS) -> {error, Reason} end end. - + prepare_copy(Pid, Tab, Storage) -> Trans = fun() -> - mnesia:write_lock_table(Tab), + mnesia:lock_table(Tab, load), mnesia_subscr:subscribe(Pid, {table, Tab}), update_where_to_write(Tab, node(Pid)), mnesia_lib:db_fixtable(Storage, Tab, true), @@ -717,11 +671,11 @@ prepare_copy(Pid, Tab, Storage) -> update_where_to_write(Tab, Node) -> case val({Tab, access_mode}) of - read_only -> + read_only -> ignore; - read_write -> + read_write -> Current = val({current, db_nodes}), - Ns = + Ns = case lists:member(Node, Current) of true -> Current; false -> [Node | Current] @@ -729,27 +683,27 @@ update_where_to_write(Tab, Node) -> update_where_to_write(Ns, Tab, Node) end. -update_where_to_write([], _, _) -> +update_where_to_write([], _, _) -> ok; update_where_to_write([H|T], Tab, AddNode) -> - rpc:call(H, mnesia_controller, call, + rpc:call(H, mnesia_controller, call, [{update_where_to_write, [add, Tab, AddNode], self()}]), update_where_to_write(T, Tab, AddNode). -send_more(Pid, N, Chunk, DataState, Tab, OldNode) -> +send_more(Pid, N, Chunk, DataState, Tab) -> receive {NewPid, more} -> - case send_packet(N - 1, NewPid, Chunk, DataState, OldNode) of - New when is_integer(New) -> + case send_packet(N - 1, NewPid, Chunk, DataState) of + New when is_integer(New) -> New - 1; NewData -> - send_more(NewPid, ?MAX_NOPACKETS, Chunk, NewData, Tab, OldNode) + send_more(NewPid, ?MAX_NOPACKETS, Chunk, NewData, Tab) end; {_NewPid, {old_protocol, Tab}} -> Storage = val({Tab, storage_type}), - {Init, NewChunk} = + {Init, NewChunk} = reader_funcs(false, Tab, Storage, calc_nokeys(Storage, Tab)), - send_more(Pid, 1, NewChunk, Init(), Tab, OldNode); + send_more(Pid, 1, NewChunk, Init(), Tab); {copier_done, Node} when Node == node(Pid)-> verbose("Receiver of table ~p crashed on ~p (more)~n", [Tab, Node]), @@ -770,7 +724,7 @@ dets_bchunk(Tab, Chunk) -> %% Arrg case dets:bchunk(Tab, Chunk) of {Cont, Data} -> {Data, Cont}; Else -> Else - end. + end. zlib_compress(Data, Level) -> BinData = term_to_binary(Data), @@ -793,28 +747,20 @@ compression_level() -> Val -> Val end. -send_packet(N, Pid, _Chunk, '$end_of_table', OldNode) -> - case OldNode of - true -> ignore; %% Old nodes can't handle the new no_more - false -> Pid ! {self(), no_more} - end, +send_packet(N, Pid, _Chunk, '$end_of_table') -> + Pid ! {self(), no_more}, N; -send_packet(N, Pid, Chunk, {[], Cont}, OldNode) -> - send_packet(N, Pid, Chunk, Chunk(Cont), OldNode); -send_packet(N, Pid, Chunk, {Recs, Cont}, OldNode) when N < ?MAX_NOPACKETS -> - case OldNode of - true -> - Pid ! {self(), {more, [Recs]}}; %% Old need's wrapping list - false -> - case compression_level() of - 0 -> - Pid ! {self(), {more, Recs}}; - Level -> - Pid ! {self(), {more_z, zlib_compress(Recs, Level)}} - end +send_packet(N, Pid, Chunk, {[], Cont}) -> + send_packet(N, Pid, Chunk, Chunk(Cont)); +send_packet(N, Pid, Chunk, {Recs, Cont}) when N < ?MAX_NOPACKETS -> + case compression_level() of + 0 -> + Pid ! {self(), {more, Recs}}; + Level -> + Pid ! {self(), {more_z, zlib_compress(Recs, Level)}} end, - send_packet(N+1, Pid, Chunk, Chunk(Cont), OldNode); -send_packet(_N, _Pid, _Chunk, DataState, _OldNode) -> + send_packet(N+1, Pid, Chunk, Chunk(Cont)); +send_packet(_N, _Pid, _Chunk, DataState) -> DataState. finish_copy(Pid, Tab, Storage, RemoteS) -> @@ -855,5 +801,5 @@ dat2bin(_Tab, _LocalS, _RemoteS) -> handle_exit(Pid, Reason) when node(Pid) == node() -> exit(Reason); -handle_exit(_Pid, _Reason) -> %% Not from our node, this will be handled by +handle_exit(_Pid, _Reason) -> %% Not from our node, this will be handled by ignore. %% mnesia_down soon. diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl index cfa3f171b2..0492d794f3 100644 --- a/lib/mnesia/src/mnesia_locker.erl +++ b/lib/mnesia/src/mnesia_locker.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -40,7 +40,8 @@ sticky_wlock_table/3, wlock/3, wlock_no_exist/4, - wlock_table/3 + wlock_table/3, + load_lock_table/3 ]). %% sys callback functions @@ -49,6 +50,8 @@ system_code_change/4 ]). +-compile({no_auto_import,[error/2]}). + -include("mnesia.hrl"). -import(mnesia_lib, [dbg_out/2, error/2, verbose/2]). @@ -654,16 +657,17 @@ rwlock(Tid, Store, Oid) -> Lock = write, case need_lock(Store, Tab, Key, Lock) of yes -> - Ns = w_nodes(Tab), + {Ns, Majority} = w_nodes(Tab), + check_majority(Majority, Tab, Ns), Res = get_rwlocks_on_nodes(Ns, rwlock, Node, Store, Tid, Oid), ?ets_insert(Store, {{locks, Tab, Key}, Lock}), Res; no -> if Key == ?ALL -> - w_nodes(Tab); + element(2, w_nodes(Tab)); Tab == ?GLOBAL -> - w_nodes(Tab); + element(2, w_nodes(Tab)); true -> dirty_rpc(Node, Tab, Key, Lock) end @@ -675,12 +679,34 @@ rwlock(Tid, Store, Oid) -> %% in the local store under the key == nodes w_nodes(Tab) -> - Nodes = ?catch_val({Tab, where_to_write}), - case Nodes of - [_ | _] -> Nodes; + case ?catch_val({Tab, where_to_wlock}) of + {[_ | _], _} = Where -> Where; _ -> mnesia:abort({no_exists, Tab}) end. +%% If the table has the 'majority' flag set, we can +%% only take a write lock if we see a majority of the +%% nodes. + + +check_majority(true, Tab, HaveNs) -> + check_majority(Tab, HaveNs); +check_majority(false, _, _) -> + ok. + +check_majority(Tab, HaveNs) -> + case ?catch_val({Tab, majority}) of + true -> + case mnesia_lib:have_majority(Tab, HaveNs) of + true -> + ok; + false -> + mnesia:abort({no_majority, Tab}) + end; + _ -> + ok + end. + %% aquire a sticky wlock, a sticky lock is a lock %% which remains at this node after the termination of the %% transaction. @@ -706,12 +732,14 @@ sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> end. do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> + {WNodes, Majority} = w_nodes(Tab), + sticky_check_majority(Lock, Tab, Majority, WNodes), ?MODULE ! {self(), {test_set_sticky, Tid, Oid, Lock}}, N = node(), receive {?MODULE, N, granted} -> ?ets_insert(Store, {{locks, Tab, Key}, write}), - [?ets_insert(Store, {nodes, Node}) || Node <- w_nodes(Tab)], + [?ets_insert(Store, {nodes, Node}) || Node <- WNodes], granted; {?MODULE, N, {granted, Val}} -> %% for rwlocks case opt_lookup_in_client(Val, Oid, write) of @@ -719,7 +747,7 @@ do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> exit({aborted, C}); Val2 -> ?ets_insert(Store, {{locks, Tab, Key}, write}), - [?ets_insert(Store, {nodes, Node}) || Node <- w_nodes(Tab)], + [?ets_insert(Store, {nodes, Node}) || Node <- WNodes], Val2 end; {?MODULE, N, {not_granted, Reason}} -> @@ -735,6 +763,16 @@ do_sticky_lock(Tid, Store, {Tab, Key} = Oid, Lock) -> dirty_sticky_lock(Tab, Key, [N], Lock) end. +sticky_check_majority(W, Tab, true, WNodes) when W==write; W==read_write -> + case mnesia_lib:have_majority(Tab, WNodes) of + true -> + ok; + false -> + mnesia:abort({no_majority, Tab}) + end; +sticky_check_majority(_, _, _, _) -> + ok. + not_stuck(Tid, Store, Tab, _Key, Oid, _Lock, N) -> rlock(Tid, Store, {Tab, ?ALL}), %% needed? wlock(Tid, Store, Oid), %% perfect sync @@ -771,22 +809,33 @@ sticky_wlock_table(Tid, Store, Tab) -> %% local store when we have aquired the lock. %% wlock(Tid, Store, Oid) -> + wlock(Tid, Store, Oid, _CheckMajority = true). + +wlock(Tid, Store, Oid, CheckMajority) -> {Tab, Key} = Oid, case need_lock(Store, Tab, Key, write) of yes -> - Ns = w_nodes(Tab), + {Ns, Majority} = w_nodes(Tab), + if CheckMajority -> + check_majority(Majority, Tab, Ns); + true -> + ignore + end, Op = {self(), {write, Tid, Oid}}, ?ets_insert(Store, {{locks, Tab, Key}, write}), get_wlocks_on_nodes(Ns, Ns, Store, Op, Oid); no when Key /= ?ALL, Tab /= ?GLOBAL -> []; no -> - w_nodes(Tab) + element(2, w_nodes(Tab)) end. wlock_table(Tid, Store, Tab) -> wlock(Tid, Store, {Tab, ?ALL}). +load_lock_table(Tid, Store, Tab) -> + wlock(Tid, Store, {Tab, ?ALL}, _CheckMajority = false). + %% Write lock even if the table does not exist wlock_no_exist(Tid, Store, Tab, Ns) -> @@ -1102,6 +1151,7 @@ do_stop() -> system_continue(_Parent, _Debug, State) -> loop(State). +-spec system_terminate(_, _, _, _) -> no_return(). system_terminate(_Reason, _Parent, _Debug, _State) -> do_stop(). diff --git a/lib/mnesia/src/mnesia_log.erl b/lib/mnesia/src/mnesia_log.erl index 00ec4740ee..94153473cb 100644 --- a/lib/mnesia/src/mnesia_log.erl +++ b/lib/mnesia/src/mnesia_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -182,6 +182,8 @@ ]). +-compile({no_auto_import,[error/2]}). + -include("mnesia.hrl"). -import(mnesia_lib, [val/1, dir/1]). -import(mnesia_lib, [exists/1, fatal/2, error/2, dbg_out/2]). @@ -1019,7 +1021,8 @@ add_recs([LogH|Rest], N) LogH#log_header.log_version >= "1.0" -> add_recs(Rest, N); add_recs([{{Tab, _Key}, _Val, clear_table} | Rest], N) -> - true = ets:match_delete(Tab, '_'), - add_recs(Rest, N+ets:info(Tab, size)); + Size = ets:info(Tab, size), + true = ets:delete_all_objects(Tab), + add_recs(Rest, N+Size); add_recs([], N) -> N. diff --git a/lib/mnesia/src/mnesia_monitor.erl b/lib/mnesia/src/mnesia_monitor.erl index 5df5df4969..e110ad3241 100644 --- a/lib/mnesia/src/mnesia_monitor.erl +++ b/lib/mnesia/src/mnesia_monitor.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -70,17 +70,19 @@ negotiate_protocol_impl/2 ]). +-compile({no_auto_import,[error/2]}). + -import(mnesia_lib, [dbg_out/2, verbose/2, error/2, fatal/2, set/2]). -include("mnesia.hrl"). --record(state, {supervisor, pending_negotiators = [], +-record(state, {supervisor, pending_negotiators = [], going_down = [], tm_started = false, early_connects = [], connecting, mq = []}). --define(current_protocol_version, {7,6}). +-define(current_protocol_version, {8,0}). --define(previous_protocol_version, {7,5}). +-define(previous_protocol_version, {7,6}). start() -> gen_server:start_link({local, ?MODULE}, ?MODULE, @@ -149,12 +151,12 @@ check_protocol([{Node, {accept, Mon, Version, Protocol}} | Tail], Protocols) -> case lists:member(Protocol, Protocols) of true -> case Protocol == protocol_version() of - true -> + true -> set({protocol, Node}, {Protocol, false}); false -> set({protocol, Node}, {Protocol, true}) end, - [node(Mon) | check_protocol(Tail, Protocols)]; + [node(Mon) | check_protocol(Tail, Protocols)]; false -> verbose("Failed to connect with ~p. ~p protocols rejected. " "expected version = ~p, expected protocol = ~p~n", @@ -177,7 +179,7 @@ check_protocol([], [Protocol | _Protocols]) -> set(protocol_version, Protocol), []. -protocol_version() -> +protocol_version() -> case ?catch_val(protocol_version) of {'EXIT', _} -> ?current_protocol_version; Version -> Version @@ -187,14 +189,14 @@ protocol_version() -> %% preferred protocols are first in the list acceptable_protocol_versions() -> [protocol_version(), ?previous_protocol_version]. - + needs_protocol_conversion(Node) -> case {?catch_val({protocol, Node}), protocol_version()} of {{'EXIT', _}, _} -> false; - {{_, Bool}, ?current_protocol_version} -> + {{_, Bool}, ?current_protocol_version} -> Bool; - {{_, Bool}, _} -> + {{_, Bool}, _} -> not Bool end. @@ -253,14 +255,15 @@ terminate_proc(Who, Reason, _State) -> %%---------------------------------------------------------------------- init([Parent]) -> process_flag(trap_exit, true), - ?ets_new_table(mnesia_gvar, [set, public, named_table]), - ?ets_new_table(mnesia_stats, [set, public, named_table]), + ?ets_new_table(mnesia_gvar, [set, public, named_table]), + ?ets_new_table(mnesia_stats, [set, public, named_table]), set(subscribers, []), + set(activity_subscribers, []), mnesia_lib:verbose("~p starting: ~p~n", [?MODULE, self()]), Version = mnesia:system_info(version), set(version, Version), dbg_out("Version: ~p~n", [Version]), - + case catch process_config_args(env()) of ok -> mnesia_lib:set({'$$$_report', current_pos}, 0), @@ -280,7 +283,7 @@ init([Parent]) -> set(checkpoints, []), set(pending_checkpoints, []), set(pending_checkpoint_pids, []), - + {ok, #state{supervisor = Parent}}; {'EXIT', Reason} -> mnesia_lib:report_fatal("Bad configuration: ~p~n", [Reason]), @@ -395,9 +398,9 @@ handle_call({unsafe_close_log, Name}, _From, State) -> disk_log:close(Name), {reply, ok, State}; -handle_call({negotiate_protocol, Mon, _Version, _Protocols}, _From, State) +handle_call({negotiate_protocol, Mon, _Version, _Protocols}, _From, State) when State#state.tm_started == false -> - State2 = State#state{early_connects = [node(Mon) | State#state.early_connects]}, + State2 = State#state{early_connects = [node(Mon) | State#state.early_connects]}, {reply, {node(), {reject, self(), uninitialized, uninitialized}}, State2}; %% From remote monitor.. @@ -409,7 +412,7 @@ handle_call({negotiate_protocol, Mon, Version, Protocols}, From, State) true -> accept_protocol(Mon, MyVersion, Protocol, From, State); false -> - %% in this release we should be able to handle the previous + %% in this release we should be able to handle the previous %% protocol case hd(Protocols) of ?previous_protocol_version -> @@ -424,7 +427,7 @@ handle_call({negotiate_protocol, Mon, Version, Protocols}, From, State) end; %% Local request to negotiate with other monitors (nodes). -handle_call({negotiate_protocol, Nodes}, From, State) -> +handle_call({negotiate_protocol, Nodes}, From, State) -> case mnesia_lib:intersect(State#state.going_down, Nodes) of [] -> spawn_link(?MODULE, negotiate_protocol_impl, [Nodes, From]), @@ -458,7 +461,7 @@ accept_protocol(Mon, Version, Protocol, From, State) -> %% No need for wait link(Mon), %% link to remote Monitor case Protocol == protocol_version() of - true -> + true -> set({protocol, Node}, {Protocol, false}); false -> set({protocol, Node}, {Protocol, true}) @@ -506,7 +509,7 @@ handle_cast({disconnect, Node}, State) -> ignore; undefined -> ignore; - RemoteMon when is_pid(RemoteMon) -> + RemoteMon when is_pid(RemoteMon) -> unlink(RemoteMon) end, {noreply, State}; @@ -531,7 +534,7 @@ handle_info({'EXIT', Pid, R}, State) when Pid == State#state.supervisor -> dbg_out("~p was ~p by supervisor~n",[?MODULE, R]), {stop, R, State}; -handle_info({'EXIT', Pid, fatal}, State) when node(Pid) == node() -> +handle_info({'EXIT', Pid, fatal}, State) when node(Pid) == node() -> dbg_out("~p got FATAL ERROR from: ~p~n",[?MODULE, Pid]), exit(State#state.supervisor, shutdown), {noreply, State}; @@ -547,7 +550,7 @@ handle_info(Msg = {'EXIT',Pid,_}, State) -> Node /= node() -> {noreply, State#state{mq = State#state.mq ++ [{info, Msg}]}}; true -> - %% We have probably got an exit signal from + %% We have probably got an exit signal from %% disk_log or dets Hint = "Hint: check that the disk still is writable", fatal("~p got unexpected info: ~p; ~p~n", @@ -564,10 +567,10 @@ handle_info({nodeup, Node}, State) -> %% Let's check if Mnesia is running there in order %% to detect if the network has been partitioned %% due to communication failure. - + HasDown = mnesia_recover:has_mnesia_down(Node), ImRunning = mnesia_lib:is_running(), - + if %% If I'm not running the test will be made later. HasDown == true, ImRunning == yes -> @@ -586,7 +589,7 @@ handle_info({disk_log, _Node, Log, Info}, State) -> {truncated, _No} -> ok; _ -> - mnesia_lib:important("Warning Log file ~p error reason ~s~n", + mnesia_lib:important("Warning Log file ~p error reason ~s~n", [Log, disk_log:format_error(Info)]) end, {noreply, State}; @@ -678,38 +681,38 @@ env() -> send_compressed ]. -default_env(access_module) -> +default_env(access_module) -> mnesia; -default_env(auto_repair) -> +default_env(auto_repair) -> true; -default_env(backup_module) -> +default_env(backup_module) -> mnesia_backup; -default_env(debug) -> +default_env(debug) -> none; default_env(dir) -> Name = lists:concat(["Mnesia.", node()]), filename:absname(Name); -default_env(dump_log_load_regulation) -> +default_env(dump_log_load_regulation) -> false; -default_env(dump_log_time_threshold) -> +default_env(dump_log_time_threshold) -> timer:minutes(3); -default_env(dump_log_update_in_place) -> +default_env(dump_log_update_in_place) -> true; default_env(dump_log_write_threshold) -> 1000; -default_env(embedded_mnemosyne) -> +default_env(embedded_mnemosyne) -> false; -default_env(event_module) -> +default_env(event_module) -> mnesia_event; -default_env(extra_db_nodes) -> +default_env(extra_db_nodes) -> []; -default_env(ignore_fallback_at_startup) -> +default_env(ignore_fallback_at_startup) -> false; default_env(fallback_error_function) -> {mnesia, lkill}; -default_env(max_wait_for_decision) -> +default_env(max_wait_for_decision) -> infinity; -default_env(schema_location) -> +default_env(schema_location) -> opt_disc; default_env(core_dir) -> false; @@ -729,7 +732,7 @@ check_type(Env, Val) -> NewVal -> NewVal end. - + do_check_type(access_module, A) when is_atom(A) -> A; do_check_type(auto_repair, B) -> bool(B); do_check_type(backup_module, B) when is_atom(B) -> B; @@ -746,7 +749,7 @@ do_check_type(dump_log_update_in_place, B) -> bool(B); do_check_type(dump_log_write_threshold, I) when is_integer(I), I > 0 -> I; do_check_type(event_module, A) when is_atom(A) -> A; do_check_type(ignore_fallback_at_startup, B) -> bool(B); -do_check_type(fallback_error_function, {Mod, Func}) +do_check_type(fallback_error_function, {Mod, Func}) when is_atom(Mod), is_atom(Func) -> {Mod, Func}; do_check_type(embedded_mnemosyne, B) -> bool(B); do_check_type(extra_db_nodes, L) when is_list(L) -> @@ -801,8 +804,8 @@ detect_inconcistency(Nodes, Context) -> has_remote_mnesia_down(Node) -> HasDown = mnesia_recover:has_mnesia_down(Node), Master = mnesia_recover:get_master_nodes(schema), - if - HasDown == true, Master == [] -> + if + HasDown == true, Master == [] -> {true, node()}; true -> {false, node()} diff --git a/lib/mnesia/src/mnesia_recover.erl b/lib/mnesia/src/mnesia_recover.erl index 6c53c2e752..4750291a10 100644 --- a/lib/mnesia/src/mnesia_recover.erl +++ b/lib/mnesia/src/mnesia_recover.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% @@ -36,6 +36,7 @@ incr_trans_tid_serial/0, init/0, log_decision/1, + log_dump_overload/1, log_master_nodes/3, log_mnesia_down/1, log_mnesia_up/1, @@ -61,6 +62,7 @@ code_change/3 ]). +-compile({no_auto_import,[error/2]}). -include("mnesia.hrl"). -import(mnesia_lib, [set/2, verbose/2, error/2, fatal/2]). @@ -70,6 +72,7 @@ unclear_decision, unclear_waitfor, tm_queue_len = 0, + log_dump_overload = false, initiated = false, early_msgs = [] }). @@ -224,11 +227,13 @@ do_log_decision(D, DoTell, NodeD) -> note_outcome(D2), case mnesia_monitor:use_dir() of true -> - mnesia_log:append(latest_log, D2), if DoTell == true, Outcome /= unclear -> tell_im_certain(NodeD#decision.disc_nodes--[node()],D2), - tell_im_certain(NodeD#decision.ram_nodes--[node()], D2); + tell_im_certain(NodeD#decision.ram_nodes--[node()], D2), + mnesia_log:log(D2); + Outcome /= unclear -> + mnesia_log:log(D2); true -> ignore end; @@ -277,6 +282,9 @@ mnesia_down(Node) -> cast({mnesia_down, Node}) end. +log_dump_overload(Flag) when is_boolean(Flag) -> + cast({log_dump_overload, Flag}). + log_master_nodes(Args, UseDir, IsRunning) -> if IsRunning == yes -> @@ -818,6 +826,12 @@ handle_cast({announce_all, Nodes}, State) -> announce_all(Nodes), {noreply, State}; +handle_cast({log_dump_overload, Flag}, State) when is_boolean(Flag) -> + Prev = State#state.log_dump_overload, + Overload = Prev orelse Flag, + mnesia_lib:overload_set(mnesia_dump_log, Overload), + {noreply, State#state{log_dump_overload = Flag}}; + handle_cast(Msg, State) -> error("~p got unexpected cast: ~p~n", [?MODULE, Msg]), {noreply, State}. @@ -851,12 +865,14 @@ handle_info(check_overload, S) -> Len > Threshold, Prev > Threshold -> What = {mnesia_tm, message_queue_len, [Prev, Len]}, mnesia_lib:report_system_event({mnesia_overload, What}), + mnesia_lib:overload_set(mnesia_tm, true), {noreply, S#state{tm_queue_len = 0}}; Len > Threshold -> {noreply, S#state{tm_queue_len = Len}}; true -> + mnesia_lib:overload_set(mnesia_tm, false), {noreply, S#state{tm_queue_len = 0}} end; undefined -> @@ -905,7 +921,23 @@ terminate(Reason, State) -> %% Purpose: Upgrade process when its code is to be changed %% Returns: {ok, NewState} %%---------------------------------------------------------------------- -code_change(_OldVsn, State, _Extra) -> +code_change(_OldVsn, {state, + Supervisor, + Unclear_pid, + Unclear_decision, + Unclear_waitfor, + Tm_queue_len, + Initiated, + Early_msgs + }, _Extra) -> + {ok, #state{supervisor = Supervisor, + unclear_pid = Unclear_pid, + unclear_decision = Unclear_decision, + unclear_waitfor = Unclear_waitfor, + tm_queue_len = Tm_queue_len, + initiated = Initiated, + early_msgs = Early_msgs}}; +code_change(_OldVsn, #state{} = State, _Extra) -> {ok, State}. %%%---------------------------------------------------------------------- diff --git a/lib/mnesia/src/mnesia_registry.erl b/lib/mnesia/src/mnesia_registry.erl index 9805d48697..202689ae5e 100644 --- a/lib/mnesia/src/mnesia_registry.erl +++ b/lib/mnesia/src/mnesia_registry.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -65,6 +65,8 @@ %%%---------------------------------------------------------------------- %% External exports +%% Avoid warning for local function max/2 clashing with autoimported BIF. +-compile({no_auto_import,[max/2]}). -export([start_dump/2, start_restore/2]). -export([create_table/1, create_table/2]). diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl index 354431a296..05be474aea 100644 --- a/lib/mnesia/src/mnesia_schema.erl +++ b/lib/mnesia/src/mnesia_schema.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% @@ -37,6 +37,7 @@ change_table_copy_type/3, change_table_access_mode/2, change_table_load_order/2, + change_table_majority/2, change_table_frag/2, clear_table/1, create_table/1, @@ -62,6 +63,7 @@ list2cs/1, lock_schema/0, merge_schema/0, + merge_schema/1, move_table/3, opt_create_dir/2, prepare_commit/3, @@ -98,7 +100,7 @@ ]). %% Needed outside to be able to use/set table_properties -%% from user (not supported) +%% from user (not supported) -export([schema_transaction/1, insert_schema_ops/2, do_create_table/1, @@ -116,9 +118,9 @@ %% Here comes the init function which also resides in %% this module, it is called upon by the trans server %% at startup of the system -%% +%% %% We have a meta table which looks like -%% {table, schema, +%% {table, schema, %% {type, set}, %% {disc_copies, all}, %% {arity, 2} @@ -147,14 +149,14 @@ exit_on_error(GoodRes) -> val(Var) -> case ?catch_val(Var) of - {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason); - Value -> Value + {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason); + Value -> Value end. %% This function traverses all cstructs in the schema and %% sets all values in mnesia_gvar accordingly for each table/cstruct -set_schema('$end_of_table') -> +set_schema('$end_of_table') -> []; set_schema(Tab) -> do_set_schema(Tab), @@ -177,6 +179,8 @@ do_set_schema(Tab, Cs) -> set({Tab, disc_only_copies}, Cs#cstruct.disc_only_copies), set({Tab, load_order}, Cs#cstruct.load_order), set({Tab, access_mode}, Cs#cstruct.access_mode), + set({Tab, majority}, Cs#cstruct.majority), + set({Tab, all_nodes}, mnesia_lib:cs_to_nodes(Cs)), set({Tab, snmp}, Cs#cstruct.snmp), set({Tab, user_properties}, Cs#cstruct.user_properties), [set({Tab, user_property, element(1, P)}, P) || P <- Cs#cstruct.user_properties], @@ -249,8 +253,8 @@ version() -> incr_version(Cs) -> {{Major, Minor}, _} = Cs#cstruct.version, Nodes = mnesia_lib:intersect(val({schema, disc_copies}), - mnesia_lib:cs_to_nodes(Cs)), - V = + mnesia_lib:cs_to_nodes(Cs)), + V = case Nodes -- val({Cs#cstruct.name, active_replicas}) of [] -> {Major + 1, 0}; % All replicas are active _ -> {Major, Minor + 1} % Some replicas are inactive @@ -355,7 +359,7 @@ delete_schema2() -> {error, Reason} -> {error, Reason} end. - + ensure_no_schema([H|T]) when is_atom(H) -> case rpc:call(H, ?MODULE, remote_read_schema, []) of {badrpc, Reason} -> @@ -403,7 +407,7 @@ opt_create_dir(UseDir, Dir) when UseDir == true-> check_can_write(Dir); false -> case file:make_dir(Dir) of - ok -> + ok -> verbose("Create Directory ~p~n", [Dir]), ok; {error, Reason} -> @@ -413,7 +417,7 @@ opt_create_dir(UseDir, Dir) when UseDir == true-> end; opt_create_dir(false, _) -> {error, {has_no_disc, node()}}. - + check_can_write(Dir) -> case file:read_file_info(Dir) of {ok, FI} when FI#file_info.type == directory, @@ -446,7 +450,7 @@ read_schema(Keep) -> read_schema(Keep, IgnoreFallback) -> lock_schema(), - Res = + Res = case mnesia:system_info(is_running) of yes -> {ok, ram, get_create_list(schema)}; @@ -473,7 +477,7 @@ read_disc_schema(Keep, IgnoreFallback) -> case mnesia_bup:fallback_exists() of true when IgnoreFallback == false, Running /= yes -> mnesia_bup:fallback_to_schema(); - _ -> + _ -> %% If we're running, we read the schema file even %% if fallback exists Dat = mnesia_lib:tab2dat(schema), @@ -495,7 +499,7 @@ read_disc_schema(Keep, IgnoreFallback) -> end. do_read_disc_schema(Fname, Keep) -> - T = + T = case Keep of false -> Args = [{keypos, 2}, public, set], @@ -519,7 +523,7 @@ do_read_disc_schema(Fname, Keep) -> get_initial_schema(SchemaStorage, Nodes) -> Cs = #cstruct{name = schema, record_name = schema, - attributes = [table, cstruct]}, + attributes = [table, cstruct]}, Cs2 = case SchemaStorage of ram_copies -> Cs#cstruct{ram_copies = Nodes}; @@ -528,7 +532,7 @@ get_initial_schema(SchemaStorage, Nodes) -> cs2list(Cs2). read_cstructs_from_disc() -> - %% Assumptions: + %% Assumptions: %% - local schema lock in global %% - use_dir is true %% - Mnesia is not running @@ -548,14 +552,14 @@ read_cstructs_from_disc() -> end, Cstructs = dets:traverse(Tab, Fun), dets:close(Tab), - {ok, Cstructs}; + {ok, Cstructs}; {error, Reason} -> {error, Reason} end; false -> {error, "No schema file exists"} end. - + %% We run a very special type of transactions when we %% we want to manipulate the schema. @@ -589,20 +593,20 @@ schema_transaction(Fun) -> %% This process may dump the transaction log, and should %% therefore not be run in an application process -%% +%% schema_coordinator(Client, _Fun, undefined) -> Res = {aborted, {node_not_running, node()}}, Client ! {transaction_done, Res, self()}, unlink(Client); - + schema_coordinator(Client, Fun, Controller) when is_pid(Controller) -> %% Do not trap exit in order to automatically die %% when the controller dies link(Controller), unlink(Client), - - %% Fulfull the transaction even if the client dies + + %% Fulfull the transaction even if the client dies Res = mnesia:transaction(Fun), Client ! {transaction_done, Res, self()}, unlink(Controller), % Avoids spurious exit message @@ -615,7 +619,7 @@ schema_coordinator(Client, Fun, Controller) when is_pid(Controller) -> insert_schema_ops({_Mod, _Tid, Ts}, SchemaIOps) -> do_insert_schema_ops(Ts#tidstore.store, SchemaIOps). - + do_insert_schema_ops(Store, [Head | Tail]) -> ?ets_insert(Store, Head), do_insert_schema_ops(Store, Tail); @@ -624,15 +628,56 @@ do_insert_schema_ops(_Store, []) -> cs2list(Cs) when is_record(Cs, cstruct) -> Tags = record_info(fields, cstruct), - rec2list(Tags, 2, Cs); + rec2list(Tags, Tags, 2, Cs); cs2list(CreateList) when is_list(CreateList) -> - CreateList. - -rec2list([Tag | Tags], Pos, Rec) -> + CreateList; +%% 4.4.19 +cs2list(Cs) when element(1, Cs) == cstruct, tuple_size(Cs) == 18 -> + Tags = [name,type,ram_copies,disc_copies,disc_only_copies, + load_order,access_mode,majority,index,snmp,local_content, + record_name,attributes,user_properties,frag_properties, + cookie,version], + rec2list(Tags, Tags, 2, Cs); +%% 4.4.18 and earlier +cs2list(Cs) when element(1, Cs) == cstruct, tuple_size(Cs) == 17 -> + Tags = [name,type,ram_copies,disc_copies,disc_only_copies, + load_order,access_mode,index,snmp,local_content, + record_name,attributes,user_properties,frag_properties, + cookie,version], + rec2list(Tags, Tags, 2, Cs). + +cs2list(false, Cs) -> + cs2list(Cs); +cs2list(ver4_4_18, Cs) -> + Orig = record_info(fields, cstruct), + Tags = [name,type,ram_copies,disc_copies,disc_only_copies, + load_order,access_mode,index,snmp,local_content, + record_name,attributes,user_properties,frag_properties, + cookie,version], + rec2list(Tags, Orig, 2, Cs); +cs2list(ver4_4_19, Cs) -> + Orig = record_info(fields, cstruct), + Tags = [name,type,ram_copies,disc_copies,disc_only_copies, + load_order,access_mode,majority,index,snmp,local_content, + record_name,attributes,user_properties,frag_properties, + cookie,version], + rec2list(Tags, Orig, 2, Cs). + +rec2list([Tag | Tags], [Tag | Orig], Pos, Rec) -> Val = element(Pos, Rec), - [{Tag, Val} | rec2list(Tags, Pos + 1, Rec)]; -rec2list([], _Pos, _Rec) -> - []. + [{Tag, Val} | rec2list(Tags, Orig, Pos + 1, Rec)]; +rec2list([], _, _Pos, _Rec) -> + []; +rec2list(Tags, [_|Orig], Pos, Rec) -> + rec2list(Tags, Orig, Pos+1, Rec). + +api_list2cs(List) when is_list(List) -> + Name = pick(unknown, name, List, must), + Keys = check_keys(Name, List, record_info(fields, cstruct)), + check_duplicates(Name, Keys), + list2cs(List); +api_list2cs(Other) -> + mnesia:abort({badarg, Other}). list2cs(List) when is_list(List) -> Name = pick(unknown, name, List, must), @@ -650,6 +695,7 @@ list2cs(List) when is_list(List) -> Snmp = pick(Name, snmp, List, []), LoadOrder = pick(Name, load_order, List, 0), AccessMode = pick(Name, access_mode, List, read_write), + Majority = pick(Name, majority, List, false), UserProps = pick(Name, user_properties, List, []), verify({alt, [nil, list]}, mnesia_lib:etype(UserProps), {bad_type, Name, {user_properties, UserProps}}), @@ -662,10 +708,7 @@ list2cs(List) when is_list(List) -> Frag = pick(Name, frag_properties, List, []), verify({alt, [nil, list]}, mnesia_lib:etype(Frag), - {badarg, Name, {frag_properties, Frag}}), - - Keys = check_keys(Name, List, record_info(fields, cstruct)), - check_duplicates(Name, Keys), + {badarg, Name, {frag_properties, Frag}}), #cstruct{name = Name, ram_copies = Rc, disc_copies = Dc, @@ -675,15 +718,14 @@ list2cs(List) when is_list(List) -> snmp = Snmp, load_order = LoadOrder, access_mode = AccessMode, + majority = Majority, local_content = LC, record_name = RecName, attributes = Attrs, user_properties = lists:sort(UserProps), frag_properties = lists:sort(Frag), cookie = Cookie, - version = Version}; -list2cs(Other) -> - mnesia:abort({badarg, Other}). + version = Version}. pick(Tab, Key, List, Default) -> case lists:keysearch(Key, 1, List) of @@ -702,7 +744,7 @@ attr_tab_to_pos(_Tab, Pos) when is_integer(Pos) -> Pos; attr_tab_to_pos(Tab, Attr) -> attr_to_pos(Attr, val({Tab, attributes})). - + %% Convert attribute name to integer if neccessary attr_to_pos(Pos, _Attrs) when is_integer(Pos) -> Pos; @@ -717,7 +759,7 @@ attr_to_pos(Attr, [_ | Attrs], Pos) -> attr_to_pos(Attr, Attrs, Pos + 1); attr_to_pos(Attr, _, _) -> mnesia:abort({bad_type, Attr}). - + check_keys(Tab, [{Key, _Val} | Tail], Items) -> case lists:member(Key, Items) of true -> [Key | check_keys(Tab, Tail, Items)]; @@ -753,7 +795,7 @@ verify_cstruct(Cs) when is_record(Cs, cstruct) -> {bad_type, Tab, {type, Type}}), %% Currently ordered_set is not supported for disk_only_copies. - if + if Type == ordered_set, Cs#cstruct.disc_only_copies /= [] -> mnesia:abort({bad_type, Tab, {not_supported, Type, disc_only_copies}}); true -> @@ -770,10 +812,10 @@ verify_cstruct(Cs) when is_record(Cs, cstruct) -> Arity = length(Attrs) + 1, verify(true, Arity > 2, {bad_type, Tab, {attributes, Attrs}}), - + lists:foldl(fun(Attr,_Other) when Attr == snmp -> mnesia:abort({bad_type, Tab, {attributes, [Attr]}}); - (Attr,Other) -> + (Attr,Other) -> verify(atom, mnesia_lib:etype(Attr), {bad_type, Tab, {attributes, [Attr]}}), verify(false, lists:member(Attr, Other), @@ -786,7 +828,7 @@ verify_cstruct(Cs) when is_record(Cs, cstruct) -> Index = Cs#cstruct.index, verify({alt, [nil, list]}, mnesia_lib:etype(Index), {bad_type, Tab, {index, Index}}), - + IxFun = fun(Pos) -> verify(true, fun() -> @@ -801,14 +843,23 @@ verify_cstruct(Cs) when is_record(Cs, cstruct) -> {bad_type, Tab, {index, [Pos]}}) end, lists:foreach(IxFun, Index), - + LC = Cs#cstruct.local_content, verify({alt, [true, false]}, LC, {bad_type, Tab, {local_content, LC}}), Access = Cs#cstruct.access_mode, verify({alt, [read_write, read_only]}, Access, {bad_type, Tab, {access_mode, Access}}), - + Majority = Cs#cstruct.majority, + verify({alt, [true, false]}, Majority, + {bad_type, Tab, {majority, Majority}}), + case Majority of + true -> + verify(false, LC, + {combine_error, Tab, [{local_content,true},{majority,true}]}); + false -> + ok + end, Snmp = Cs#cstruct.snmp, verify(true, mnesia_snmp_hook:check_ustruct(Snmp), {badarg, Tab, {snmp, Snmp}}), @@ -819,7 +870,7 @@ verify_cstruct(Cs) when is_record(Cs, cstruct) -> lists:foreach(CheckProp, Cs#cstruct.user_properties), case Cs#cstruct.cookie of - {{MegaSecs, Secs, MicroSecs}, _Node} + {{MegaSecs, Secs, MicroSecs}, _Node} when is_integer(MegaSecs), is_integer(Secs), is_integer(MicroSecs), is_atom(node) -> ok; @@ -855,15 +906,15 @@ verify_nodes(Cs) -> end, verify(integer, mnesia_lib:etype(LoadOrder), {bad_type, Tab, {load_order, LoadOrder}}), - + Nodes = Ram ++ Disc ++ DiscOnly, verify(list, mnesia_lib:etype(Nodes), {combine_error, Tab, [{ram_copies, []}, {disc_copies, []}, {disc_only_copies, []}]}), verify(false, has_duplicates(Nodes), {combine_error, Tab, Nodes}), - AtomCheck = fun(N) -> verify(atom, mnesia_lib:etype(N), {bad_type, Tab, N}) end, + AtomCheck = fun(N) -> verify(atom, mnesia_lib:etype(N), {bad_type, Tab, N}) end, lists:foreach(AtomCheck, Nodes). - + verify(Expected, Fun, Error) when is_function(Fun) -> do_verify(Expected, catch Fun(), Error); verify(Expected, Actual, Error) -> @@ -894,7 +945,7 @@ ensure_active(Cs, What) -> W = {Tab, What}, ensure_non_empty(W), Nodes = mnesia_lib:intersect(val({schema, disc_copies}), - mnesia_lib:cs_to_nodes(Cs)), + mnesia_lib:cs_to_nodes(Cs)), case Nodes -- val(W) of [] -> ok; @@ -921,7 +972,7 @@ ensure_non_empty({Tab, Vhat}) -> ensure_not_active(Tab = schema, Node) -> Active = val({Tab, active_replicas}), - case lists:member(Node, Active) of + case lists:member(Node, Active) of false when Active =/= [] -> ok; false -> @@ -955,7 +1006,7 @@ create_table(TabDef) -> do_multi_create_table(TabDef) -> get_tid_ts_and_lock(schema, write), ensure_writable(schema), - Cs = list2cs(TabDef), + Cs = api_list2cs(TabDef), case Cs#cstruct.frag_properties of [] -> do_create_table(Cs); @@ -984,7 +1035,7 @@ unsafe_make_create_table(Cs) -> {_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, none), verify_cstruct(Cs), Tab = Cs#cstruct.name, - + %% Check that we have all disc replica nodes running DiscNodes = Cs#cstruct.disc_copies ++ Cs#cstruct.disc_only_copies, RunningNodes = val({current, db_nodes}), @@ -1002,7 +1053,7 @@ unsafe_make_create_table(Cs) -> check_if_exists(Tab) -> TidTs = get_tid_ts_and_lock(schema, write), {_, _, Ts} = TidTs, - Store = Ts#tidstore.store, + Store = Ts#tidstore.store, ets:foldl( fun({op, create_table, [{name, T}|_]}, _Acc) when T==Tab -> true; @@ -1039,7 +1090,7 @@ make_delete_table(Tab, Mode) -> %% nodes etc. TidTs = get_tid_ts_and_lock(schema, write), {_, _, Ts} = TidTs, - Store = Ts#tidstore.store, + Store = Ts#tidstore.store, Deleted = ets:select_delete( Store, [{{op,'$1',[{name,Tab}|'_']}, [{'or', @@ -1062,9 +1113,9 @@ make_delete_table(Tab, Mode) -> [] -> [make_delete_table2(Tab)]; _Props -> - %% Check if it is a base table - mnesia_frag:lookup_frag_hash(Tab), - + %% Check if it is a base table + mnesia_frag:lookup_frag_hash(Tab), + %% Check for foreigners F = mnesia_frag:lookup_foreigners(Tab), verify([], F, {combine_error, @@ -1086,7 +1137,7 @@ make_delete_table2(Tab) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Change fragmentation of a table - + change_table_frag(Tab, Change) -> schema_transaction(fun() -> do_change_table_frag(Tab, Change) end). @@ -1097,7 +1148,7 @@ do_change_table_frag(Tab, Change) when is_atom(Tab), Tab /= schema -> ok; do_change_table_frag(Tab, _Change) -> mnesia:abort({bad_type, Tab}). - + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Clear a table @@ -1135,7 +1186,7 @@ make_add_table_copy(Tab, Node, Storage) -> verify(false, lists:member(Node, Ns), {already_exists, Tab, Node}), Cs2 = new_cs(Cs, Node, Storage, add), verify_cstruct(Cs2), - + %% Check storage and if node is running IsRunning = lists:member(Node, val({current, db_nodes})), if @@ -1162,21 +1213,21 @@ del_table_copy(Tab, Node) -> do_del_table_copy(Tab, Node) when is_atom(Node) -> TidTs = get_tid_ts_and_lock(schema, write), -%% get_tid_ts_and_lock(Tab, write), +%% get_tid_ts_and_lock(Tab, write), insert_schema_ops(TidTs, make_del_table_copy(Tab, Node)); do_del_table_copy(Tab, Node) -> mnesia:abort({badarg, Tab, Node}). - + make_del_table_copy(Tab, Node) -> ensure_writable(schema), Cs = incr_version(val({Tab, cstruct})), Storage = mnesia_lib:schema_cs_to_storage_type(Node, Cs), - Cs2 = new_cs(Cs, Node, Storage, del), + Cs2 = new_cs(Cs, Node, Storage, del), case mnesia_lib:cs_to_nodes(Cs2) of [] when Tab == schema -> mnesia:abort({combine_error, Tab, "Last replica"}); [] -> - ensure_active(Cs), + ensure_active(Cs), dbg_out("Last replica deleted in table ~p~n", [Tab]), make_delete_table(Tab, whole_table); _ when Tab == schema -> @@ -1195,14 +1246,14 @@ remove_node_from_tabs([], _Node) -> []; remove_node_from_tabs([schema|Rest], Node) -> remove_node_from_tabs(Rest, Node); -remove_node_from_tabs([Tab|Rest], Node) -> - {Cs, IsFragModified} = +remove_node_from_tabs([Tab|Rest], Node) -> + {Cs, IsFragModified} = mnesia_frag:remove_node(Node, incr_version(val({Tab, cstruct}))), case mnesia_lib:schema_cs_to_storage_type(Node, Cs) of unknown -> case IsFragModified of true -> - [{op, change_table_frag, {del_node, Node}, cs2list(Cs)} | + [{op, change_table_frag, {del_node, Node}, cs2list(Cs)} | remove_node_from_tabs(Rest, Node)]; false -> remove_node_from_tabs(Rest, Node) @@ -1231,7 +1282,7 @@ new_cs(Cs, Node, ram_copies, del) -> new_cs(Cs, Node, disc_copies, del) -> Cs#cstruct{disc_copies = lists:delete(Node , Cs#cstruct.disc_copies)}; new_cs(Cs, Node, disc_only_copies, del) -> - Cs#cstruct{disc_only_copies = + Cs#cstruct{disc_only_copies = lists:delete(Node , Cs#cstruct.disc_only_copies)}; new_cs(Cs, _Node, Storage, _Op) -> mnesia:abort({badarg, Cs#cstruct.name, Storage}). @@ -1263,7 +1314,7 @@ make_move_table(Tab, FromNode, ToNode) -> Running = val({current, db_nodes}), Storage = mnesia_lib:schema_cs_to_storage_type(FromNode, Cs), verify(true, lists:member(ToNode, Running), {not_active, schema, ToNode}), - + Cs2 = new_cs(Cs, ToNode, Storage, add), Cs3 = new_cs(Cs2, FromNode, Storage, del), verify_cstruct(Cs3), @@ -1291,7 +1342,7 @@ make_change_table_copy_type(Tab, Node, unknown) -> make_change_table_copy_type(Tab, Node, ToS) -> ensure_writable(schema), Cs = incr_version(val({Tab, cstruct})), - FromS = mnesia_lib:storage_type_at_node(Node, Tab), + FromS = mnesia_lib:storage_type_at_node(Node, Tab), case compare_storage_type(false, FromS, ToS) of {same, _} -> @@ -1305,12 +1356,12 @@ make_change_table_copy_type(Tab, Node, ToS) -> Cs2 = new_cs(Cs, Node, FromS, del), Cs3 = new_cs(Cs2, Node, ToS, add), verify_cstruct(Cs3), - + [{op, change_table_copy_type, Node, FromS, ToS, cs2list(Cs3)}]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% change index functions .... -%% Pos is allready added by 1 in both of these functions +%% Pos is allready added by 1 in both of these functions add_table_index(Tab, Pos) -> schema_transaction(fun() -> do_add_table_index(Tab, Pos) end). @@ -1397,14 +1448,14 @@ make_del_snmp(Tab) -> [{op, del_snmp, cs2list(Cs2)}]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% +%% -transform_table(Tab, Fun, NewAttrs, NewRecName) - when is_function(Fun), is_list(NewAttrs), is_atom(NewRecName) -> +transform_table(Tab, Fun, NewAttrs, NewRecName) + when is_function(Fun), is_list(NewAttrs), is_atom(NewRecName) -> schema_transaction(fun() -> do_transform_table(Tab, Fun, NewAttrs, NewRecName) end); -transform_table(Tab, ignore, NewAttrs, NewRecName) - when is_list(NewAttrs), is_atom(NewRecName) -> +transform_table(Tab, ignore, NewAttrs, NewRecName) + when is_list(NewAttrs), is_atom(NewRecName) -> schema_transaction(fun() -> do_transform_table(Tab, ignore, NewAttrs, NewRecName) end); transform_table(Tab, Fun, NewAttrs, NewRecName) -> @@ -1423,7 +1474,7 @@ make_transform(Tab, Fun, NewAttrs, NewRecName) -> ensure_active(Cs), ensure_writable(Tab), case mnesia_lib:val({Tab, index}) of - [] -> + [] -> Cs2 = Cs#cstruct{attributes = NewAttrs, record_name = NewRecName}, verify_cstruct(Cs2), [{op, transform, Fun, cs2list(Cs2)}]; @@ -1449,7 +1500,7 @@ make_transform(Tab, Fun, NewAttrs, NewRecName) -> end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% +%% change_table_access_mode(Tab, Mode) -> schema_transaction(fun() -> do_change_table_access_mode(Tab, Mode) end). @@ -1494,6 +1545,43 @@ make_change_table_load_order(Tab, LoadOrder) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +change_table_majority(Tab, Majority) when is_boolean(Majority) -> + schema_transaction(fun() -> do_change_table_majority(Tab, Majority) end). + +do_change_table_majority(schema, _Majority) -> + mnesia:abort({bad_type, schema}); +do_change_table_majority(Tab, Majority) -> + TidTs = get_tid_ts_and_lock(schema, write), + get_tid_ts_and_lock(Tab, none), + insert_schema_ops(TidTs, make_change_table_majority(Tab, Majority)). + +make_change_table_majority(Tab, Majority) -> + ensure_writable(schema), + Cs = incr_version(val({Tab, cstruct})), + ensure_active(Cs), + OldMajority = Cs#cstruct.majority, + Cs2 = Cs#cstruct{majority = Majority}, + FragOps = case lists:keyfind(base_table, 1, Cs#cstruct.frag_properties) of + {_, Tab} -> + FragNames = mnesia_frag:frag_names(Tab) -- [Tab], + lists:map( + fun(T) -> + get_tid_ts_and_lock(Tab, none), + CsT = incr_version(val({T, cstruct})), + ensure_active(CsT), + CsT2 = CsT#cstruct{majority = Majority}, + verify_cstruct(CsT2), + {op, change_table_majority, cs2list(CsT2), + OldMajority, Majority} + end, FragNames); + false -> []; + {_, _} -> mnesia:abort({bad_type, Tab}) + end, + verify_cstruct(Cs2), + [{op, change_table_majority, cs2list(Cs2), OldMajority, Majority} | FragOps]. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + write_table_property(Tab, Prop) when is_tuple(Prop), size(Prop) >= 1 -> schema_transaction(fun() -> do_write_table_property(Tab, Prop) end); write_table_property(Tab, Prop) -> @@ -1546,9 +1634,9 @@ change_prop_in_existing_op(Tab, Prop, How, Store) -> false -> false end. - -update_existing_op([{op, Op, L = [{name,Tab}|_], _OldProp}|Ops], - Tab, Prop, How, Acc) when Op == write_property; + +update_existing_op([{op, Op, L = [{name,Tab}|_], _OldProp}|Ops], + Tab, Prop, How, Acc) when Op == write_property; Op == delete_property -> %% Apparently, mnesia_dumper doesn't care about OldProp here -- just L, %% so we will throw away OldProp (not that it matters...) and insert Prop. @@ -1573,7 +1661,7 @@ update_existing_op([], _, _, _, _) -> do_read_table_property(Tab, Key) -> TidTs = get_tid_ts_and_lock(schema, read), {_, _, Ts} = TidTs, - Store = Ts#tidstore.store, + Store = Ts#tidstore.store, Props = ets:foldl( fun({op, create_table, [{name, T}|Opts]}, _Acc) when T==Tab -> @@ -1637,7 +1725,7 @@ do_delete_table_property(Tab, PropKey) -> [Tab,PropKey]), %% this must be an existing table get_tid_ts_and_lock(Tab, none), - insert_schema_ops(TidTs, + insert_schema_ops(TidTs, make_delete_table_properties(Tab, [PropKey])) end. @@ -1659,17 +1747,17 @@ make_delete_table_properties(_Tab, [], _Cs) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% Ensure that the transaction can be committed even +%% Ensure that the transaction can be committed even %% if the node crashes and Mnesia is restarted prepare_commit(Tid, Commit, WaitFor) -> case Commit#commit.schema_ops of [] -> {false, Commit, optional}; OrigOps -> - {Modified, Ops, DumperMode} = + {Modified, Ops, DumperMode} = prepare_ops(Tid, OrigOps, WaitFor, false, [], optional), InitBy = schema_prepare, - GoodRes = {Modified, + GoodRes = {Modified, Commit#commit{schema_ops = lists:reverse(Ops)}, DumperMode}, case DumperMode of @@ -1685,7 +1773,7 @@ prepare_commit(Tid, Commit, WaitFor) -> end end, case Ops of - [] -> + [] -> ignore; _ -> %% We need to grab a dumper lock here, the log may not @@ -1697,20 +1785,20 @@ prepare_commit(Tid, Commit, WaitFor) -> prepare_ops(Tid, [Op | Ops], WaitFor, Changed, Acc, DumperMode) -> case prepare_op(Tid, Op, WaitFor) of - {true, mandatory} -> + {true, mandatory} -> prepare_ops(Tid, Ops, WaitFor, Changed, [Op | Acc], mandatory); - {true, optional} -> + {true, optional} -> prepare_ops(Tid, Ops, WaitFor, Changed, [Op | Acc], DumperMode); - {true, Ops2, mandatory} -> + {true, Ops2, mandatory} -> prepare_ops(Tid, Ops, WaitFor, true, Ops2 ++ Acc, mandatory); - {true, Ops2, optional} -> + {true, Ops2, optional} -> prepare_ops(Tid, Ops, WaitFor, true, Ops2 ++ Acc, DumperMode); - {false, optional} -> + {false, optional} -> prepare_ops(Tid, Ops, WaitFor, true, Acc, DumperMode) end; prepare_ops(_Tid, [], _WaitFor, Changed, Acc, DumperMode) -> {Changed, Acc, DumperMode}. - + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Prepare for commit %% returns true if Op should be included, i.e. unmodified @@ -1729,19 +1817,22 @@ prepare_op(_Tid, {op, rec, unknown, Rec}, _WaitFor) -> prepare_op(_Tid, {op, announce_im_running, Node, SchemaDef, Running, RemoteRunning}, _WaitFor) -> SchemaCs = list2cs(SchemaDef), - if - Node == node() -> %% Announce has already run on local node + if + Node == node() -> %% Announce has already run on local node ignore; %% from do_merge_schema true -> - NewNodes = mnesia_lib:uniq(Running++RemoteRunning) -- val({current,db_nodes}), + %% If a node has restarted it may still linger in db_nodes, + %% but have been removed from recover_nodes + Current = mnesia_lib:intersect(val({current,db_nodes}), [node()|val(recover_nodes)]), + NewNodes = mnesia_lib:uniq(Running++RemoteRunning) -- Current, mnesia_lib:set(prepare_op, {announce_im_running,NewNodes}), announce_im_running(NewNodes, SchemaCs) end, {false, optional}; -prepare_op(_Tid, {op, sync_trans}, {part, CoordPid}) -> +prepare_op(_Tid, {op, sync_trans}, {part, CoordPid}) -> CoordPid ! {sync_trans, self()}, - receive + receive {sync_trans, CoordPid} -> {false, optional}; {mnesia_down, _Node} = Else -> @@ -1752,7 +1843,7 @@ prepare_op(_Tid, {op, sync_trans}, {part, CoordPid}) -> mnesia:abort(Else) end; -prepare_op(_Tid, {op, sync_trans}, {coord, Nodes}) -> +prepare_op(_Tid, {op, sync_trans}, {coord, Nodes}) -> case receive_sync(Nodes, []) of {abort, Reason} -> mnesia_lib:verbose("sync_op terminated due to ~p~n", [Reason]), @@ -1783,7 +1874,7 @@ prepare_op(Tid, {op, create_table, TabDef}, _WaitFor) -> create_ram_table(Tab, Cs#cstruct.type), create_disc_table(Tab), insert_cstruct(Tid, Cs, false), - {true, optional}; + {true, optional}; disc_only_copies -> mnesia_lib:set({Tab, create_table},true), create_disc_only_table(Tab,Cs#cstruct.type), @@ -1802,15 +1893,15 @@ prepare_op(Tid, {op, add_table_copy, Storage, Node, TabDef}, _WaitFor) -> if Tab == schema -> {true, optional}; - + Node == node() -> - case mnesia_lib:val({schema, storage_type}) of - ram_copies when Storage /= ram_copies -> + case mnesia_lib:val({schema, storage_type}) of + ram_copies when Storage /= ram_copies -> Error = {combine_error, Tab, "has no disc", Node}, mnesia:abort(Error); _ -> ok - end, + end, %% Tables are created by mnesia_loader get_network code insert_cstruct(Tid, Cs, true), case mnesia_controller:get_network_copy(Tab, Cs) of @@ -1847,22 +1938,22 @@ prepare_op(Tid, {op, add_table_copy, Storage, Node, TabDef}, _WaitFor) -> prepare_op(Tid, {op, del_table_copy, _Storage, Node, TabDef}, _WaitFor) -> Cs = list2cs(TabDef), Tab = Cs#cstruct.name, - + if %% Schema table lock is always required to run a schema op. %% No need to look it. - node(Tid#tid.pid) == node(), Tab /= schema -> + node(Tid#tid.pid) == node(), Tab /= schema -> Self = self(), Pid = spawn_link(fun() -> lock_del_table(Tab, Node, Cs, Self) end), put(mnesia_lock, Pid), - receive - {Pid, updated} -> + receive + {Pid, updated} -> {true, optional}; {Pid, FailReason} -> mnesia:abort(FailReason); {'EXIT', Pid, Reason} -> mnesia:abort(Reason) - end; + end; true -> {true, optional} end; @@ -1873,12 +1964,12 @@ prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}, _WaitFor) Tab = Cs#cstruct.name, NotActive = mnesia_lib:not_active_here(Tab), - - if + + if NotActive == true -> mnesia:abort({not_active, Tab, node()}); - - Tab == schema -> + + Tab == schema -> case {FromS, ToS} of {ram_copies, disc_copies} -> case mnesia:system_info(schema_location) of @@ -1888,7 +1979,7 @@ prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}, _WaitFor) mnesia:abort({combine_error, Tab, node(), "schema_location must be opt_disc"}) end, - Dir = mnesia_lib:dir(), + Dir = mnesia_lib:dir(), case opt_create_dir(true, Dir) of ok -> purge_dir(Dir, []), @@ -1912,18 +2003,18 @@ prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}, _WaitFor) _ -> mnesia:abort({combine_error, Tab, ToS}) end; - - FromS == ram_copies -> + + FromS == ram_copies -> case mnesia_monitor:use_dir() of - true -> + true -> Dat = mnesia_lib:tab2dcd(Tab), case mnesia_lib:exists(Dat) of true -> mnesia:abort({combine_error, Tab, node(), "Table dump exists"}); false -> - case ToS of - disc_copies -> + case ToS of + disc_copies -> mnesia_log:ets2dcd(Tab, dmp); disc_only_copies -> mnesia_dumper:raw_named_dump_table(Tab, dmp) @@ -1933,7 +2024,7 @@ prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}, _WaitFor) false -> mnesia:abort({has_no_disc, node()}) end; - + FromS == disc_copies, ToS == disc_only_copies -> mnesia_dumper:raw_named_dump_table(Tab, dmp); FromS == disc_only_copies -> @@ -1965,7 +2056,7 @@ prepare_op(_Tid, {op, dump_table, unknown, TabDef}, _WaitFor) -> case lists:member(node(), Cs#cstruct.ram_copies) of true -> case mnesia_monitor:use_dir() of - true -> + true -> mnesia_log:ets2dcd(Tab, dmp), Size = mnesia:table_info(Tab, size), {true, [{op, dump_table, Size, TabDef}], optional}; @@ -2003,7 +2094,7 @@ prepare_op(_Tid, {op, transform, Fun, TabDef}, _WaitFor) -> mnesia_lib:db_fixtable(Storage, Tab, true), Key = mnesia_lib:db_first(Tab), Op = {op, transform, Fun, TabDef}, - case catch transform_objs(Fun, Tab, RecName, + case catch transform_objs(Fun, Tab, RecName, Key, NewArity, Storage, Type, [Op]) of {'EXIT', Reason} -> mnesia_lib:db_fixtable(Storage, Tab, false), @@ -2017,7 +2108,7 @@ prepare_op(_Tid, {op, transform, Fun, TabDef}, _WaitFor) -> prepare_op(_Tid, {op, merge_schema, TabDef}, _WaitFor) -> Cs = list2cs(TabDef), case verify_merge(Cs) of - ok -> + ok -> {true, optional}; Error -> verbose("Merge_Schema ~p failed on ~p: ~p~n", [_Tid,node(),Error]), @@ -2038,7 +2129,7 @@ create_ram_table(Tab, Type) -> create_disc_table(Tab) -> File = mnesia_lib:tab2dcd(Tab), file:delete(File), - FArg = [{file, File}, {name, {mnesia,create}}, + FArg = [{file, File}, {name, {mnesia,create}}, {repair, false}, {mode, read_write}], case mnesia_monitor:open_log(FArg) of {ok,Log} -> @@ -2069,7 +2160,7 @@ receive_sync([], Pids) -> receive_sync(Nodes, Pids) -> receive {sync_trans, Pid} -> - Node = node(Pid), + Node = node(Pid), receive_sync(lists:delete(Node, Nodes), [Pid | Pids]); Else -> {abort, Else} @@ -2085,16 +2176,16 @@ lock_del_table(Tab, Node, Cs, Father) -> false; ({badrpc, {'EXIT', {undef, _}}}) -> %% This will be the case we talks with elder nodes - %% than 3.8.2, they will set where_to_read without - %% getting a lock. + %% than 3.8.2, they will set where_to_read without + %% getting a lock. false; (_) -> true end, case lists:filter(Filter, Res) of - [] -> + [] -> Father ! {self(), updated}, - %% When transaction is commited the process dies + %% When transaction is commited the process dies %% and the lock is released. receive _ -> ok end; Err -> @@ -2111,7 +2202,7 @@ lock_del_table(Tab, Node, Cs, Father) -> exit(normal). set_where_to_read(Tab, Node, Cs) -> - case mnesia_lib:val({Tab, where_to_read}) of + case mnesia_lib:val({Tab, where_to_read}) of Node -> case Cs#cstruct.local_content of true -> @@ -2130,16 +2221,16 @@ transform_objs(_Fun, _Tab, _RT, '$end_of_table', _NewArity, _Storage, _Type, Acc transform_objs(Fun, Tab, RecName, Key, A, Storage, Type, Acc) -> Objs = mnesia_lib:db_get(Tab, Key), NextKey = mnesia_lib:db_next_key(Tab, Key), - Oid = {Tab, Key}, + Oid = {Tab, Key}, NewObjs = {Ws, Ds} = transform_obj(Tab, RecName, Key, Fun, Objs, A, Type, [], []), - if - NewObjs == {[], []} -> + if + NewObjs == {[], []} -> transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type, Acc); - Type == bag -> + Type == bag -> transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type, [{op, rec, Storage, {Oid, Ws, write}}, {op, rec, Storage, {Oid, [Oid], delete}} | Acc]); - Ds == [] -> + Ds == [] -> %% Type is set or ordered_set, no need to delete the record first transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type, [{op, rec, Storage, {Oid, Ws, write}} | Acc]); @@ -2160,15 +2251,15 @@ transform_obj(Tab, RecName, Key, Fun, [Obj|Rest], NewArity, Type, Ws, Ds) -> NewObj == Obj -> transform_obj(Tab, RecName, Key, Fun, Rest, NewArity, Type, Ws, Ds); RecName == element(1, NewObj), Key == element(2, NewObj) -> - transform_obj(Tab, RecName, Key, Fun, Rest, NewArity, + transform_obj(Tab, RecName, Key, Fun, Rest, NewArity, Type, [NewObj | Ws], Ds); - NewObj == delete -> - case Type of + NewObj == delete -> + case Type of bag -> %% Just don't write that object - transform_obj(Tab, RecName, Key, Fun, Rest, - NewArity, Type, Ws, Ds); + transform_obj(Tab, RecName, Key, Fun, Rest, + NewArity, Type, Ws, Ds); _ -> - transform_obj(Tab, RecName, Key, Fun, Rest, NewArity, + transform_obj(Tab, RecName, Key, Fun, Rest, NewArity, Type, Ws, [NewObj | Ds]) end; true -> @@ -2192,7 +2283,7 @@ undo_prepare_commit(Tid, Commit) -> %% Undo in reverse order undo_prepare_ops(Tid, [Op | Ops]) -> - case element(1, Op) of + case element(1, Op) of TheOp when TheOp /= op, TheOp /= restore_op -> undo_prepare_ops(Tid, Ops); _ -> @@ -2219,7 +2310,7 @@ undo_prepare_op(Tid, {op, create_table, TabDef}) -> mnesia_lib:unset({Tab, create_table}), delete_cstruct(Tid, Cs), case mnesia_lib:cs_to_storage_type(node(), Cs) of - unknown -> + unknown -> ok; ram_copies -> ram_delete_table(Tab, ram_copies); @@ -2234,7 +2325,7 @@ undo_prepare_op(Tid, {op, create_table, TabDef}) -> %% disc_delete_table(Tab, Storage), file:delete(Dat) end; - + undo_prepare_op(Tid, {op, add_table_copy, Storage, Node, TabDef}) -> Cs = list2cs(TabDef), Tab = Cs#cstruct.name, @@ -2259,21 +2350,21 @@ undo_prepare_op(Tid, {op, add_table_copy, Storage, Node, TabDef}) -> Cs2 = new_cs(Cs, Node, Storage, del), insert_cstruct(Tid, Cs2, true) % Don't care about the version end; - -undo_prepare_op(_Tid, {op, del_table_copy, _, Node, TabDef}) + +undo_prepare_op(_Tid, {op, del_table_copy, _, Node, TabDef}) when Node == node() -> Cs = list2cs(TabDef), Tab = Cs#cstruct.name, mnesia_lib:set({Tab, where_to_read}, Node); -undo_prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}) +undo_prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}) when N == node() -> Cs = list2cs(TabDef), Tab = Cs#cstruct.name, mnesia_checkpoint:tm_change_table_copy_type(Tab, ToS, FromS), Dmp = mnesia_lib:tab2dmp(Tab), - + case {FromS, ToS} of {ram_copies, disc_copies} when Tab == schema -> file:delete(Dmp), @@ -2327,9 +2418,9 @@ ram_delete_table(Tab, Storage) -> ignore; disc_only_copies -> ignore; - _Else -> + _Else -> %% delete possible index files and data ..... - %% Got to catch this since if no info has been set in the + %% Got to catch this since if no info has been set in the %% mnesia_gvar it will crash catch mnesia_index:del_transient(Tab, Storage), case ?catch_val({Tab, {index, snmp}}) of @@ -2399,7 +2490,7 @@ has_known_suffix(File, [Suffix | Tail], false) -> has_known_suffix(File, Tail, lists:suffix(Suffix, File)); has_known_suffix(_File, [], Bool) -> Bool. - + known_suffixes() -> real_suffixes() ++ tmp_suffixes(). real_suffixes() -> [".DAT", ".LOG", ".BUP", ".DCL", ".DCD"]. @@ -2422,11 +2513,11 @@ info2(Tab, [{frag_hash, _V} | Tail]) -> % Ignore frag_hash info2(Tab, [{P, V} | Tail]) -> io:format("~-20w -> ~p~n",[P,V]), info2(Tab, Tail); -info2(_, []) -> +info2(_, []) -> io:format("~n", []). get_table_properties(Tab) -> - case catch mnesia_lib:db_match_object(ram_copies, + case catch mnesia_lib:db_match_object(ram_copies, mnesia_gvar, {{Tab, '_'}, '_'}) of {'EXIT', _} -> mnesia:abort({no_exists, Tab, all}); @@ -2454,9 +2545,9 @@ get_table_properties(Tab) -> recs = error_recs }). -restore(Opaque) -> +restore(Opaque) -> restore(Opaque, [], mnesia_monitor:get_env(backup_module)). -restore(Opaque, Args) when is_list(Args) -> +restore(Opaque, Args) when is_list(Args) -> restore(Opaque, Args, mnesia_monitor:get_env(backup_module)); restore(_Opaque, BadArg) -> {aborted, {badarg, BadArg}}. @@ -2467,7 +2558,7 @@ restore(Opaque, Args, Module) when is_list(Args), is_atom(Module) -> case mnesia_bup:read_schema(R#r.module, Opaque) of {error, Reason} -> {aborted, Reason}; - BupSchema -> + BupSchema -> schema_transaction(fun() -> do_restore(R, BupSchema) end) end; {'EXIT', Reason} -> @@ -2501,8 +2592,8 @@ check_restore_arg({keep_tables, List}, R) when is_list(List) -> check_restore_arg({skip_tables, List}, R) when is_list(List) -> TableList = [{Tab, skip_tables} || Tab <- List], R#r{table_options = R#r.table_options ++ TableList}; -check_restore_arg({default_op, Op}, R) -> - case Op of +check_restore_arg({default_op, Op}, R) -> + case Op of clear_tables -> ok; recreate_tables -> ok; keep_tables -> ok; @@ -2533,12 +2624,12 @@ restore_items([Rec | Recs], Header, Schema, R) -> case lists:keysearch(Tab, 1, R#r.tables) of {value, {Tab, Where0, Snmp, RecName}} -> Where = case Where0 of - undefined -> + undefined -> val({Tab, where_to_commit}); _ -> Where0 end, - {Rest, NRecs} = restore_tab_items([Rec | Recs], Tab, + {Rest, NRecs} = restore_tab_items([Rec | Recs], Tab, RecName, Where, Snmp, R#r.recs, R#r.insert_op), restore_items(Rest, Header, Schema, R#r{recs = NRecs}); @@ -2546,12 +2637,12 @@ restore_items([Rec | Recs], Header, Schema, R) -> Rest = skip_tab_items(Recs, Tab), restore_items(Rest, Header, Schema, R) end; - + restore_items([], _Header, _Schema, R) -> R. restore_func(Tab, R) -> - case lists:keysearch(Tab, 1, R#r.table_options) of + case lists:keysearch(Tab, 1, R#r.table_options) of {value, {Tab, OP}} -> OP; false -> @@ -2563,24 +2654,24 @@ where_to_commit(Tab, CsList) -> Disc = [{N, disc_copies} || N <- pick(Tab, disc_copies, CsList, [])], DiscO = [{N, disc_only_copies} || N <- pick(Tab, disc_only_copies, CsList, [])], Ram ++ Disc ++ DiscO. - + %% Changes of the Meta info of schema itself is not allowed restore_schema([{schema, schema, _List} | Schema], R) -> restore_schema(Schema, R); restore_schema([{schema, Tab, List} | Schema], R) -> case restore_func(Tab, R) of - clear_tables -> + clear_tables -> do_clear_table(Tab), - Snmp = val({Tab, snmp}), - RecName = val({Tab, record_name}), + Snmp = val({Tab, snmp}), + RecName = val({Tab, record_name}), R2 = R#r{tables = [{Tab, undefined, Snmp, RecName} | R#r.tables]}, restore_schema(Schema, R2); - recreate_tables -> + recreate_tables -> case ?catch_val({Tab, cstruct}) of - {'EXIT', _} -> + {'EXIT', _} -> TidTs = {_Mod, Tid, Ts} = get(mnesia_activity_state), RunningNodes = val({current, db_nodes}), - Nodes = mnesia_lib:intersect(mnesia_lib:cs_to_nodes(list2cs(List)), + Nodes = mnesia_lib:intersect(mnesia_lib:cs_to_nodes(list2cs(List)), RunningNodes), mnesia_locker:wlock_no_exist(Tid, Ts#tidstore.store, Tab, Nodes), TidTs; @@ -2588,20 +2679,20 @@ restore_schema([{schema, Tab, List} | Schema], R) -> TidTs = get_tid_ts_and_lock(Tab, write) end, NC = {cookie, ?unique_cookie}, - List2 = lists:keyreplace(cookie, 1, List, NC), + List2 = lists:keyreplace(cookie, 1, List, NC), Where = where_to_commit(Tab, List2), Snmp = pick(Tab, snmp, List2, []), RecName = pick(Tab, record_name, List2, Tab), insert_schema_ops(TidTs, [{op, restore_recreate, List2}]), R2 = R#r{tables = [{Tab, Where, Snmp, RecName} | R#r.tables]}, restore_schema(Schema, R2); - keep_tables -> + keep_tables -> get_tid_ts_and_lock(Tab, write), Snmp = val({Tab, snmp}), - RecName = val({Tab, record_name}), + RecName = val({Tab, record_name}), R2 = R#r{tables = [{Tab, undefined, Snmp, RecName} | R#r.tables]}, restore_schema(Schema, R2); - skip_tables -> + skip_tables -> restore_schema(Schema, R) end; @@ -2612,7 +2703,7 @@ restore_schema([{schema, Tab} | Schema], R) -> restore_schema([], R) -> R. -restore_tab_items([Rec | Rest], Tab, RecName, Where, Snmp, Recs, Op) +restore_tab_items([Rec | Rest], Tab, RecName, Where, Snmp, Recs, Op) when element(1, Rec) == Tab -> NewRecs = Op(Rec, Recs, RecName, Where, Snmp), restore_tab_items(Rest, Tab, RecName, Where, Snmp, NewRecs, Op); @@ -2620,7 +2711,7 @@ restore_tab_items([Rec | Rest], Tab, RecName, Where, Snmp, Recs, Op) restore_tab_items(Rest, _Tab, _RecName, _Where, _Snmp, Recs, _Op) -> {Rest, Recs}. -skip_tab_items([Rec| Rest], Tab) +skip_tab_items([Rec| Rest], Tab) when element(1, Rec) == Tab -> skip_tab_items(Rest, Tab); skip_tab_items(Recs, _) -> @@ -2650,10 +2741,15 @@ make_dump_tables([]) -> %% Merge the local schema with the schema on other nodes merge_schema() -> - schema_transaction(fun() -> do_merge_schema() end). + schema_transaction(fun() -> do_merge_schema([]) end). + +merge_schema(UserFun) -> + schema_transaction(fun() -> UserFun(fun(Arg) -> do_merge_schema(Arg) end) end). -do_merge_schema() -> +do_merge_schema(LockTabs0) -> {_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, write), + LockTabs = [{T, tab_to_nodes(T)} || T <- LockTabs0], + [get_tid_ts_and_lock(T,write) || {T,_} <- LockTabs], Connected = val(recover_nodes), Running = val({current, db_nodes}), Store = Ts#tidstore.store, @@ -2665,40 +2761,47 @@ do_merge_schema() -> mnesia:abort({bad_commit, {missing_lock, Miss}}) end, case Connected -- Running of - [Node | _] -> + [Node | _] = OtherNodes -> %% Time for a schema merging party! mnesia_locker:wlock_no_exist(Tid, Store, schema, [Node]), - case rpc:call(Node, mnesia_controller, get_cstructs, []) of + [mnesia_locker:wlock_no_exist( + Tid, Store, T, mnesia_lib:intersect(Ns, OtherNodes)) + || {T,Ns} <- LockTabs], + case fetch_cstructs(Node) of {cstructs, Cstructs, RemoteRunning1} -> LockedAlready = Running ++ [Node], {New, Old} = mnesia_recover:connect_nodes(RemoteRunning1), RemoteRunning = mnesia_lib:intersect(New ++ Old, RemoteRunning1), - if + if RemoteRunning /= RemoteRunning1 -> - mnesia_lib:error("Mnesia on ~p could not connect to node(s) ~p~n", - [node(), RemoteRunning1 -- RemoteRunning]); + mnesia_lib:error("Mnesia on ~p could not connect to node(s) ~p~n", + [node(), RemoteRunning1 -- RemoteRunning]), + mnesia:abort({node_not_running, RemoteRunning1 -- RemoteRunning}); true -> ok end, NeedsLock = RemoteRunning -- LockedAlready, mnesia_locker:wlock_no_exist(Tid, Store, schema, NeedsLock), - {value, SchemaCs} = - lists:keysearch(schema, #cstruct.name, Cstructs), + [mnesia_locker:wlock_no_exist(Tid, Store, T, + mnesia_lib:intersect(Ns,NeedsLock)) + || {T,Ns} <- LockTabs], + NeedsConversion = need_old_cstructs(NeedsLock ++ LockedAlready), + {value, SchemaCs} = lists:keysearch(schema, #cstruct.name, Cstructs), + SchemaDef = cs2list(NeedsConversion, SchemaCs), %% Announce that Node is running - A = [{op, announce_im_running, node(), - cs2list(SchemaCs), Running, RemoteRunning}], + A = [{op, announce_im_running, node(), SchemaDef, Running, RemoteRunning}], do_insert_schema_ops(Store, A), - + %% Introduce remote tables to local node - do_insert_schema_ops(Store, make_merge_schema(Node, Cstructs)), - + do_insert_schema_ops(Store, make_merge_schema(Node, NeedsConversion, Cstructs)), + %% Introduce local tables to remote nodes Tabs = val({schema, tables}), Ops = [{op, merge_schema, get_create_list(T)} || T <- Tabs, not lists:keymember(T, #cstruct.name, Cstructs)], do_insert_schema_ops(Store, Ops), - + %% Ensure that the txn will be committed on all nodes NewNodes = RemoteRunning -- Running, mnesia_lib:set(prepare_op, {announce_im_running,NewNodes}), @@ -2714,15 +2817,49 @@ do_merge_schema() -> not_merged end. -make_merge_schema(Node, [Cs | Cstructs]) -> - Ops = do_make_merge_schema(Node, Cs), - Ops ++ make_merge_schema(Node, Cstructs); -make_merge_schema(_Node, []) -> +fetch_cstructs(Node) -> + case mnesia_monitor:needs_protocol_conversion(Node) of + true -> + case rpc:call(Node, mnesia_controller, get_cstructs, []) of + {cstructs, Cs0, RR} -> + {cstructs, [list2cs(cs2list(Cs)) || Cs <- Cs0], RR}; + Err -> Err + end; + false -> + rpc:call(Node, mnesia_controller, get_remote_cstructs, []) + end. + +need_old_cstructs(Nodes) -> + Filter = fun(Node) -> not mnesia_monitor:needs_protocol_conversion(Node) end, + case lists:dropwhile(Filter, Nodes) of + [] -> false; + [Node|_] -> + case rpc:call(Node, mnesia_lib, val, [{schema,cstruct}]) of + #cstruct{} -> + %% mnesia_lib:warning("Mnesia on ~p do not need to convert cstruct (~p)~n", + %% [node(), Node]), + false; + {badrpc, _} -> + need_old_cstructs(lists:delete(Node,Nodes)); + Cs when element(1, Cs) == cstruct, tuple_size(Cs) == 17 -> + ver4_4_18; % Without majority + Cs when element(1, Cs) == cstruct, tuple_size(Cs) == 18 -> + ver4_4_19 % With majority + end + end. + +tab_to_nodes(Tab) when is_atom(Tab) -> + Cs = val({Tab, cstruct}), + mnesia_lib:cs_to_nodes(Cs). + +make_merge_schema(Node, NeedsConv, [Cs | Cstructs]) -> + Ops = do_make_merge_schema(Node, NeedsConv, Cs), + Ops ++ make_merge_schema(Node, NeedsConv, Cstructs); +make_merge_schema(_Node, _, []) -> []. %% Merge definitions of schema table -do_make_merge_schema(Node, RemoteCs) - when RemoteCs#cstruct.name == schema -> +do_make_merge_schema(Node, NeedsConv, RemoteCs = #cstruct{name = schema}) -> Cs = val({schema, cstruct}), Masters = mnesia_recover:get_master_nodes(schema), HasRemoteMaster = lists:member(Node, Masters), @@ -2732,15 +2869,15 @@ do_make_merge_schema(Node, RemoteCs) StCsLocal = mnesia_lib:cs_to_storage_type(node(), Cs), StRcsLocal = mnesia_lib:cs_to_storage_type(node(), RemoteCs), StCsRemote = mnesia_lib:cs_to_storage_type(Node, Cs), - StRcsRemote = mnesia_lib:cs_to_storage_type(Node, RemoteCs), - + StRcsRemote = mnesia_lib:cs_to_storage_type(Node, RemoteCs), + if Cs#cstruct.cookie == RemoteCs#cstruct.cookie, Cs#cstruct.version == RemoteCs#cstruct.version -> %% Great, we have the same cookie and version %% and do not need to merge cstructs []; - + Cs#cstruct.cookie /= RemoteCs#cstruct.cookie, Cs#cstruct.disc_copies /= [], RemoteCs#cstruct.disc_copies /= [] -> @@ -2751,14 +2888,14 @@ do_make_merge_schema(Node, RemoteCs) HasRemoteMaster == false -> %% Choose local cstruct, %% since it's the master - [{op, merge_schema, cs2list(Cs)}]; + [{op, merge_schema, cs2list(NeedsConv, Cs)}]; HasRemoteMaster == true, HasLocalMaster == false -> %% Choose remote cstruct, %% since it's the master - [{op, merge_schema, cs2list(RemoteCs)}]; - + [{op, merge_schema, cs2list(NeedsConv, RemoteCs)}]; + true -> Str = io_lib:format("Incompatible schema cookies. " "Please, restart from old backup." @@ -2766,12 +2903,12 @@ do_make_merge_schema(Node, RemoteCs) [Node, cs2list(RemoteCs), node(), cs2list(Cs)]), throw(Str) end; - + StCsLocal /= StRcsLocal, StRcsLocal /= unknown, StCsLocal /= ram_copies -> Str = io_lib:format("Incompatible schema storage types (local). " "on ~w storage ~w, on ~w storage ~w~n", [node(), StCsLocal, Node, StRcsLocal]), - throw(Str); + throw(Str); StCsRemote /= StRcsRemote, StCsRemote /= unknown, StRcsRemote /= ram_copies -> Str = io_lib:format("Incompatible schema storage types (remote). " "on ~w cs ~w, on ~w rcs ~w~n", @@ -2782,27 +2919,27 @@ do_make_merge_schema(Node, RemoteCs) %% Choose local cstruct, %% since it involves disc nodes MergedCs = merge_cstructs(Cs, RemoteCs, Force), - [{op, merge_schema, cs2list(MergedCs)}]; - + [{op, merge_schema, cs2list(NeedsConv, MergedCs)}]; + RemoteCs#cstruct.disc_copies /= [] -> %% Choose remote cstruct, %% since it involves disc nodes MergedCs = merge_cstructs(RemoteCs, Cs, Force), - [{op, merge_schema, cs2list(MergedCs)}]; + [{op, merge_schema, cs2list(NeedsConv, MergedCs)}]; Cs > RemoteCs -> %% Choose remote cstruct MergedCs = merge_cstructs(RemoteCs, Cs, Force), - [{op, merge_schema, cs2list(MergedCs)}]; - + [{op, merge_schema, cs2list(NeedsConv, MergedCs)}]; + true -> %% Choose local cstruct MergedCs = merge_cstructs(Cs, RemoteCs, Force), - [{op, merge_schema, cs2list(MergedCs)}] + [{op, merge_schema, cs2list(NeedsConv, MergedCs)}] end; %% Merge definitions of normal table -do_make_merge_schema(Node, RemoteCs) -> +do_make_merge_schema(Node, NeedsConv, RemoteCs = #cstruct{}) -> Tab = RemoteCs#cstruct.name, Masters = mnesia_recover:get_master_nodes(schema), HasRemoteMaster = lists:member(Node, Masters), @@ -2811,27 +2948,27 @@ do_make_merge_schema(Node, RemoteCs) -> case ?catch_val({Tab, cstruct}) of {'EXIT', _} -> %% A completely new table, created while Node was down - [{op, merge_schema, cs2list(RemoteCs)}]; + [{op, merge_schema, cs2list(NeedsConv, RemoteCs)}]; Cs when Cs#cstruct.cookie == RemoteCs#cstruct.cookie -> if Cs#cstruct.version == RemoteCs#cstruct.version -> %% We have exactly the same version of the %% table def []; - + Cs#cstruct.version > RemoteCs#cstruct.version -> %% Oops, we have different versions %% of the table def, lets merge them. %% The only changes that may have occurred %% is that new replicas may have been added. MergedCs = merge_cstructs(Cs, RemoteCs, Force), - [{op, merge_schema, cs2list(MergedCs)}]; - + [{op, merge_schema, cs2list(NeedsConv, MergedCs)}]; + Cs#cstruct.version < RemoteCs#cstruct.version -> %% Oops, we have different versions %% of the table def, lets merge them MergedCs = merge_cstructs(RemoteCs, Cs, Force), - [{op, merge_schema, cs2list(MergedCs)}] + [{op, merge_schema, cs2list(NeedsConv, MergedCs)}] end; Cs -> %% Different cookies, not possible to merge @@ -2840,14 +2977,14 @@ do_make_merge_schema(Node, RemoteCs) -> HasRemoteMaster == false -> %% Choose local cstruct, %% since it's the master - [{op, merge_schema, cs2list(Cs)}]; + [{op, merge_schema, cs2list(NeedsConv, Cs)}]; HasRemoteMaster == true, HasLocalMaster == false -> %% Choose remote cstruct, %% since it's the master - [{op, merge_schema, cs2list(RemoteCs)}]; - + [{op, merge_schema, cs2list(NeedsConv, RemoteCs)}]; + true -> Str = io_lib:format("Bad cookie in table definition" " ~w: ~w = ~w, ~w = ~w~n", @@ -2917,7 +3054,7 @@ compare_storage_type(true, One, Another) -> compare_storage_type(false, Another, One); compare_storage_type(false, _One, _Another) -> incompatible. - + change_storage_type(N, ram_copies, Cs) -> Nodes = [N | Cs#cstruct.ram_copies], Cs#cstruct{ram_copies = mnesia_lib:uniq(Nodes)}; @@ -2953,6 +3090,7 @@ merge_versions(AnythingNew, Cs, RemoteCs, Force) -> Cs#cstruct.index == RemoteCs#cstruct.index, Cs#cstruct.snmp == RemoteCs#cstruct.snmp, Cs#cstruct.access_mode == RemoteCs#cstruct.access_mode, + Cs#cstruct.majority == RemoteCs#cstruct.majority, Cs#cstruct.load_order == RemoteCs#cstruct.load_order, Cs#cstruct.user_properties == RemoteCs#cstruct.user_properties -> do_merge_versions(AnythingNew, Cs, RemoteCs); @@ -2998,21 +3136,23 @@ verify_merge(RemoteCs) -> if StCsLocal == StRcsLocal -> ok; StCsLocal == unknown -> ok; - (StRcsLocal == unknown), (HasRemoteMaster == false) -> + (StRcsLocal == unknown), (HasRemoteMaster == false) -> {merge_error, Cs, RemoteCs}; %% Trust the merger true -> ok end end. -announce_im_running([N | Ns], SchemaCs) -> +announce_im_running([N | Ns], SchemaCs) -> {L1, L2} = mnesia_recover:connect_nodes([N]), case lists:member(N, L1) or lists:member(N, L2) of true -> mnesia_lib:add({current, db_nodes}, N), mnesia_controller:add_active_replica(schema, N, SchemaCs); false -> - ignore + mnesia_lib:error("Mnesia on ~p could not connect to node ~p~n", + [node(), N]), + mnesia:abort({node_not_running, N}) end, announce_im_running(Ns, SchemaCs); announce_im_running([], _) -> @@ -3020,7 +3160,7 @@ announce_im_running([], _) -> unannounce_im_running([N | Ns]) -> mnesia_lib:del({current, db_nodes}, N), - mnesia_controller:del_active_replica(schema, N), + mnesia_controller:del_active_replica(schema, N), unannounce_im_running(Ns); unannounce_im_running([]) -> ok. diff --git a/lib/mnesia/src/mnesia_snmp_hook.erl b/lib/mnesia/src/mnesia_snmp_hook.erl index 8b4b5231e1..893b39f3c0 100644 --- a/lib/mnesia/src/mnesia_snmp_hook.erl +++ b/lib/mnesia/src/mnesia_snmp_hook.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/mnesia/src/mnesia_subscr.erl b/lib/mnesia/src/mnesia_subscr.erl index afd1704dec..415c69d508 100644 --- a/lib/mnesia/src/mnesia_subscr.erl +++ b/lib/mnesia/src/mnesia_subscr.erl @@ -1,19 +1,19 @@ %% %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. -%% +%% +%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in %% compliance with the License. You should have received a copy of the %% Erlang Public License along with this software. If not, it can be %% retrieved online at http://www.erlang.org/. -%% +%% %% Software distributed under the License is distributed on an "AS IS" %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See %% the License for the specific language governing rights and limitations %% under the License. -%% +%% %% %CopyrightEnd% %% @@ -30,7 +30,8 @@ subscribers/0, report_table_event/4, report_table_event/5, - report_table_event/6 + report_table_event/6, + report_activity/1 ]). %% gen_server callbacks @@ -42,6 +43,8 @@ code_change/3 ]). +-compile({no_auto_import,[error/2]}). + -include("mnesia.hrl"). -import(mnesia_lib, [error/2]). @@ -91,6 +94,8 @@ set_debug_level(Level, OldEnv) -> subscribe(ClientPid, system) -> change_subscr(activate, ClientPid, system); +subscribe(ClientPid, activity) -> + change_subscr(activate, ClientPid, activity); subscribe(ClientPid, {table, Tab}) -> change_subscr(activate, ClientPid, {table, Tab, simple}); subscribe(ClientPid, {table, Tab, simple}) -> @@ -102,6 +107,8 @@ subscribe(_ClientPid, What) -> unsubscribe(ClientPid, system) -> change_subscr(deactivate, ClientPid, system); +unsubscribe(ClientPid, activity) -> + change_subscr(deactivate, ClientPid, activity); unsubscribe(ClientPid, {table, Tab}) -> change_subscr(deactivate, ClientPid, {table, Tab, simple}); unsubscribe(ClientPid, {table, Tab, simple}) -> @@ -120,6 +127,15 @@ change_subscr(Kind, ClientPid, What) -> subscribers() -> [whereis(mnesia_event) | mnesia_lib:val(subscribers)]. +report_activity({dirty, _pid}) -> + ok; +report_activity(Tid) -> + case ?catch_val(activity_subscribers) of + {'EXIT', _} -> ok; + Subscribers -> + deliver(Subscribers, {mnesia_activity_event, {complete, Tid}}) + end. + report_table_event(Tab, Tid, Obj, Op) -> case ?catch_val({Tab, commit_work}) of {'EXIT', _} -> ok; @@ -300,6 +316,9 @@ code_change(_OldVsn, State, _Extra) -> do_change({activate, ClientPid, system}, SubscrTab) when is_pid(ClientPid) -> Var = subscribers, activate(ClientPid, system, Var, subscribers(), SubscrTab); +do_change({activate, ClientPid, activity}, SubscrTab) when is_pid(ClientPid) -> + Var = activity_subscribers, + activate(ClientPid, activity, Var, mnesia_lib:val(Var), SubscrTab); do_change({activate, ClientPid, {table, Tab, How}}, SubscrTab) when is_pid(ClientPid) -> case ?catch_val({Tab, where_to_read}) of Node when Node == node() -> @@ -313,6 +332,9 @@ do_change({activate, ClientPid, {table, Tab, How}}, SubscrTab) when is_pid(Clien do_change({deactivate, ClientPid, system}, SubscrTab) -> Var = subscribers, deactivate(ClientPid, system, Var, SubscrTab); +do_change({deactivate, ClientPid, activity}, SubscrTab) -> + Var = activity_subscribers, + deactivate(ClientPid, activity, Var, SubscrTab); do_change({deactivate, ClientPid, {table, Tab, How}}, SubscrTab) -> Var = {Tab, commit_work}, deactivate(ClientPid, {table, Tab, How}, Var, SubscrTab); @@ -345,7 +367,7 @@ do_change(_, _) -> activate(ClientPid, What, Var, OldSubscribers, SubscrTab) -> Old = - if Var == subscribers -> + if Var == subscribers orelse Var == activity_subscribers -> OldSubscribers; true -> case lists:keysearch(subscribers, 1, OldSubscribers) of @@ -379,6 +401,9 @@ activate(ClientPid, What, Var, OldSubscribers, SubscrTab) -> add_subscr(subscribers, _What, Pid) -> mnesia_lib:add(subscribers, Pid), {ok, node()}; +add_subscr(activity_subscribers, _What, Pid) -> + mnesia_lib:add(activity_subscribers, Pid), + {ok, node()}; add_subscr({Tab, commit_work}, What, Pid) -> Commit = mnesia_lib:val({Tab, commit_work}), case lists:keysearch(subscribers, 1, Commit) of @@ -427,6 +452,8 @@ deactivate(ClientPid, What, Var, SubscrTab) -> del_subscr(subscribers, _What, Pid) -> mnesia_lib:del(subscribers, Pid); +del_subscr(activity_subscribers, _What, Pid) -> + mnesia_lib:del(activity_subscribers, Pid); del_subscr({Tab, commit_work}, What, Pid) -> Commit = mnesia_lib:val({Tab, commit_work}), case lists:keysearch(subscribers, 1, Commit) of @@ -473,6 +500,8 @@ do_handle_exit([{ClientPid, What} | Tail]) -> case What of system -> del_subscr(subscribers, What, ClientPid); + activity -> + del_subscr(activity_subscribers, What, ClientPid); {_, Tab, _Level} -> del_subscr({Tab, commit_work}, What, ClientPid) end, diff --git a/lib/mnesia/src/mnesia_text.erl b/lib/mnesia/src/mnesia_text.erl index f1a28bf43d..0906d18da9 100644 --- a/lib/mnesia/src/mnesia_text.erl +++ b/lib/mnesia/src/mnesia_text.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -20,6 +20,8 @@ %% -module(mnesia_text). +%% Avoid warning for local function error/1 clashing with autoimported BIF. +-compile({no_auto_import,[error/1]}). -export([parse/1, file/1, load_textfile/1, dump_to_textfile/1]). load_textfile(File) -> @@ -179,9 +181,6 @@ read_term_from_stream(Stream, File, Line) -> Str = Mod:format_error(What), io:format("Error in line:~p of:~p ~s\n", [NewLine, File, Str]), - error; - T -> - io:format("Error2 **~p~n",[T]), error end; {eof,_EndLine} -> diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl index d42109c3da..f62f7cb7c8 100644 --- a/lib/mnesia/src/mnesia_tm.erl +++ b/lib/mnesia/src/mnesia_tm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -64,7 +64,8 @@ prev_tab = [], % initiate to a non valid table name prev_types, prev_snmp, - types + types, + majority = [] }). -record(participant, {tid, pid, commit, disc_nodes = [], @@ -1100,9 +1101,12 @@ t_commit(Type) -> case arrange(Tid, Store, Type) of {N, Prep} when N > 0 -> multi_commit(Prep#prep.protocol, + majority_attr(Prep), Tid, Prep#prep.records, Store); {0, Prep} -> - multi_commit(read_only, Tid, Prep#prep.records, Store) + multi_commit(read_only, + majority_attr(Prep), + Tid, Prep#prep.records, Store) end; true -> %% nested commit @@ -1117,6 +1121,10 @@ t_commit(Type) -> do_commit_nested end. +majority_attr(#prep{majority = M}) -> + M. + + %% This function arranges for all objects we shall write in S to be %% in a list of {Node, CommitRecord} %% Important function for the performance of mnesia. @@ -1222,11 +1230,13 @@ prepare_items(Tid, Tab, Key, Items, Prep) -> {blocked, _} -> unblocked = req({unblock_me, Tab}), prepare_items(Tid, Tab, Key, Items, Prep); - _ -> + _ -> + Majority = needs_majority(Tab, Prep), Snmp = val({Tab, snmp}), Recs2 = do_prepare_items(Tid, Tab, Key, Types, Snmp, Items, Prep#prep.records), Prep2 = Prep#prep{records = Recs2, prev_tab = Tab, + majority = Majority, prev_types = Types, prev_snmp = Snmp}, check_prep(Prep2, Types) end. @@ -1235,6 +1245,33 @@ do_prepare_items(Tid, Tab, Key, Types, Snmp, Items, Recs) -> Recs2 = prepare_snmp(Tid, Tab, Key, Types, Snmp, Items, Recs), % May exit prepare_nodes(Tid, Types, Items, Recs2, normal). + +needs_majority(Tab, #prep{majority = M}) -> + case lists:keymember(Tab, 1, M) of + true -> + M; + false -> + case ?catch_val({Tab, majority}) of + {'EXIT', _} -> + M; + false -> + M; + true -> + CopyHolders = val({Tab, all_nodes}), + [{Tab, CopyHolders} | M] + end + end. + +have_majority([], _) -> + ok; +have_majority([{Tab, AllNodes} | Rest], Nodes) -> + case mnesia_lib:have_majority(Tab, AllNodes, Nodes) of + true -> + have_majority(Rest, Nodes); + false -> + {error, Tab} + end. + prepare_snmp(Tab, Key, Items) -> case val({Tab, snmp}) of [] -> @@ -1261,10 +1298,15 @@ prepare_snmp(Tid, Tab, Key, Types, Us, Items, Recs) -> prepare_nodes(Tid, Types, [{clear_table, Tab}], Recs, snmp) end. -check_prep(Prep, Types) when Prep#prep.types == Types -> +check_prep(#prep{majority = [], types = Types} = Prep, Types) -> Prep; -check_prep(Prep, Types) when Prep#prep.types == undefined -> - Prep#prep{types = Types}; +check_prep(#prep{majority = M, types = undefined} = Prep, Types) -> + Protocol = if M == [] -> + Prep#prep.protocol; + true -> + asym_trans + end, + Prep#prep{protocol = Protocol, types = Types}; check_prep(Prep, _Types) -> Prep#prep{protocol = asym_trans}. @@ -1311,7 +1353,7 @@ prepare_node(_Node, _Storage, [], Rec, _Kind) -> %% multi_commit((Protocol, Tid, CommitRecords, Store) %% Local work is always performed in users process -multi_commit(read_only, Tid, CR, _Store) -> +multi_commit(read_only, _Maj = [], Tid, CR, _Store) -> %% This featherweight commit protocol is used when no %% updates has been performed in the transaction. @@ -1324,7 +1366,7 @@ multi_commit(read_only, Tid, CR, _Store) -> ?MODULE ! {delete_transaction, Tid}, do_commit; -multi_commit(sym_trans, Tid, CR, Store) -> +multi_commit(sym_trans, _Maj = [], Tid, CR, Store) -> %% This lightweight commit protocol is used when all %% the involved tables are replicated symetrically. %% Their storage types must match on each node. @@ -1376,7 +1418,7 @@ multi_commit(sym_trans, Tid, CR, Store) -> [{tid, Tid}, {outcome, Outcome}]), Outcome; -multi_commit(sync_sym_trans, Tid, CR, Store) -> +multi_commit(sync_sym_trans, _Maj = [], Tid, CR, Store) -> %% This protocol is the same as sym_trans except that it %% uses syncronized calls to disk_log and syncronized commits %% when several nodes are involved. @@ -1408,7 +1450,7 @@ multi_commit(sync_sym_trans, Tid, CR, Store) -> [{tid, Tid}, {outcome, Outcome}]), Outcome; -multi_commit(asym_trans, Tid, CR, Store) -> +multi_commit(asym_trans, Majority, Tid, CR, Store) -> %% This more expensive commit protocol is used when %% table definitions are changed (schema transactions). %% It is also used when the involved tables are @@ -1469,6 +1511,10 @@ multi_commit(asym_trans, Tid, CR, Store) -> {D2, CR2} = commit_decision(D, CR, [], []), DiscNs = D2#decision.disc_nodes, RamNs = D2#decision.ram_nodes, + case have_majority(Majority, DiscNs ++ RamNs) of + ok -> ok; + {error, Tab} -> mnesia:abort({no_majority, Tab}) + end, Pending = mnesia_checkpoint:tm_enter_pending(Tid, DiscNs, RamNs), ?ets_insert(Store, Pending), {WaitFor, Local} = ask_commit(asym_trans, Tid, CR2, DiscNs, RamNs), @@ -1604,6 +1650,7 @@ tell_participants([Pid | Pids], Msg) -> tell_participants([], _Msg) -> ok. +-spec commit_participant(_, _, _, _, _) -> no_return(). %% Trap exit because we can get a shutdown from application manager commit_participant(Coord, Tid, Bin, DiscNs, RamNs) when is_binary(Bin) -> process_flag(trap_exit, true), @@ -1733,7 +1780,9 @@ do_commit(Tid, C, DumperMode) -> R = do_snmp(Tid, C#commit.snmp), R2 = do_update(Tid, ram_copies, C#commit.ram_copies, R), R3 = do_update(Tid, disc_copies, C#commit.disc_copies, R2), - do_update(Tid, disc_only_copies, C#commit.disc_only_copies, R3). + R4 = do_update(Tid, disc_only_copies, C#commit.disc_only_copies, R3), + mnesia_subscr:report_activity(Tid), + R4. %% Update the items do_update(Tid, Storage, [Op | Ops], OldRes) -> @@ -2277,6 +2326,7 @@ fixtable(Tab, Lock, Me) -> system_continue(_Parent, _Debug, State) -> doit_loop(State). +-spec system_terminate(_, _, _, _) -> no_return(). system_terminate(_Reason, _Parent, _Debug, State) -> do_stop(State). diff --git a/lib/mnesia/test/.gitignore b/lib/mnesia/test/.gitignore new file mode 100644 index 0000000000..1e9a9933ed --- /dev/null +++ b/lib/mnesia/test/.gitignore @@ -0,0 +1,9 @@ + + +# Test generates +MnesiaCore* +Mnesia.* + +tempfile* +mnesia_test_case_info +test_log*
\ No newline at end of file diff --git a/lib/mnesia/test/Makefile b/lib/mnesia/test/Makefile index a4f32e3f78..ae4c9626c7 100644 --- a/lib/mnesia/test/Makefile +++ b/lib/mnesia/test/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2009. All Rights Reserved. +# Copyright Ericsson AB 1996-2011. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -42,6 +42,7 @@ MODULES= \ mnesia_dirty_access_test \ mnesia_atomicity_test \ mnesia_consistency_test \ + mnesia_majority_test \ mnesia_isolation_test \ mnesia_durability_test \ mnesia_recovery_test \ @@ -108,9 +109,9 @@ release_spec: opt release_tests_spec: opt $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) mnesia.spec mnesia.spec.vxworks $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_PROGRAM) mt $(INSTALL_PROGS) $(RELSYSDIR) -# chmod -f -R u+w $(RELSYSDIR) + $(INSTALL_DATA) mnesia.spec mnesia.cover $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) + $(INSTALL_SCRIPT) mt $(INSTALL_PROGS) $(RELSYSDIR) +# chmod -R u+w $(RELSYSDIR) # @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) release_docs_spec: diff --git a/lib/mnesia/test/mnesia.cover b/lib/mnesia/test/mnesia.cover new file mode 100644 index 0000000000..66ffc06e89 --- /dev/null +++ b/lib/mnesia/test/mnesia.cover @@ -0,0 +1,2 @@ +{incl_app,mnesia,details}. + diff --git a/lib/mnesia/test/mnesia.spec b/lib/mnesia/test/mnesia.spec index 596f8b917d..204d1519cb 100644 --- a/lib/mnesia/test/mnesia.spec +++ b/lib/mnesia/test/mnesia.spec @@ -1,23 +1,76 @@ -{topcase, {dir, "../mnesia_test"}}. -{require_nodenames, 2}. -{skip, {mnesia_measure_test, ram_meter, "Takes to long time"}}. -{skip, {mnesia_measure_test, disc_meter, "Takes to long time"}}. -{skip, {mnesia_measure_test, disc_only_meter, "Takes to long time"}}. -{skip, {mnesia_measure_test, cost, "Takes to long time"}}. -{skip, {mnesia_measure_test, dbn_meters, "Takes to long time"}}. -{skip, {mnesia_measure_test, tpcb, "Takes to long time"}}. -{skip, {mnesia_measure_test, prediction, "Not yet implemented"}}. -{skip, {mnesia_measure_test, consumption, "Not yet implemented"}}. -{skip, {mnesia_measure_test, scalability, "Not yet implemented"}}. -{skip, {mnesia_measure_test, tpcb, "Takes too much time and memory"}}. -{skip, {mnesia_measure_test, measure_all_api_functions, "Not yet implemented"}}. -{skip, {mnesia_measure_test, mnemosyne_vs_mnesia_kernel, "Not yet implemented"}}. -{skip, {mnesia_examples_test, company, "Not yet implemented"}}. -{skip, {mnesia_config_test, ignore_fallback_at_startup, "Not yet implemented"}}. -{skip, {mnesia_evil_backup, local_backup_checkpoint, "Not yet implemented"}}. -{skip, {mnesia_config_test, max_wait_for_decision, "Not yet implemented"}}. -{skip, {mnesia_recovery_test, after_full_disc_partition, "Not yet implemented"}}. -{skip, {mnesia_recovery_test, system_upgrade, "Not yet implemented"}}. -{skip, {mnesia_consistency_test, consistency_after_change_table_copy_type, "Not yet implemented"}}. -{skip, {mnesia_consistency_test, consistency_after_transform_table, "Not yet implemented"}}. -{skip, {mnesia_consistency_test, consistency_after_rename_of_node, "Not yet implemented"}}. +{suites,"../mnesia_test",all}. +{skip_cases,"../mnesia_test",mnesia_measure_test, + [ram_meter], + "Takes to long time"}. +{skip_cases,"../mnesia_test",mnesia_measure_test, + [disc_meter], + "Takes to long time"}. +{skip_cases,"../mnesia_test",mnesia_measure_test, + [disc_only_meter], + "Takes to long time"}. +{skip_cases,"../mnesia_test",mnesia_measure_test,[cost],"Takes to long time"}. +{skip_cases,"../mnesia_test",mnesia_measure_test, + [dbn_meters], + "Takes to long time"}. +{skip_cases,"../mnesia_test",mnesia_measure_test, + [ram_tpcb,disc_tpcb,disc_only_tpcb], + "Takes to long time"}. +{skip_cases,"../mnesia_test",mnesia_measure_test, + [reader_disturbed_by_node_down,writer_disturbed_by_node_down, + reader_disturbed_by_node_up,writer_disturbed_by_node_up, + reader_disturbed_by_schema_ops,writer_disturbed_by_schema_ops, + reader_disturbed_by_checkpoint,writer_disturbed_by_checkpoint, + reader_disturbed_by_dump_log,writer_disturbed_by_dump_log, + reader_disturbed_by_backup,writer_disturbed_by_backup, + reader_disturbed_by_restore,writer_disturbed_by_restore, + reader_competing_with_reader,reader_competing_with_writer, + writer_competing_with_reader,writer_competing_with_writer], + "Not yet implemented"}. +{skip_cases,"../mnesia_test",mnesia_measure_test, + [measure_resource_consumption,determine_resource_leakage], + "Not yet implemented"}. +{skip_cases,"../mnesia_test",mnesia_measure_test, + [determine_system_limits,performance_at_min_config, + performance_at_max_config,performance_at_full_load, + resource_consumption_at_min_config, + resource_consumption_at_max_config, + resource_consumption_at_full_load], + "Not yet implemented"}. +{skip_cases,"../mnesia_test",mnesia_measure_test, + [ram_tpcb,disc_tpcb,disc_only_tpcb], + "Takes too much time and memory"}. +{skip_cases,"../mnesia_test",mnesia_measure_test, + [measure_all_api_functions], + "Not yet implemented"}. +{skip_cases,"../mnesia_test",mnesia_measure_test, + [mnemosyne_vs_mnesia_kernel], + "Not yet implemented"}. +{skip_cases,"../mnesia_test",mnesia_examples_test, + [company], + "Not yet implemented"}. +{skip_cases,"../mnesia_test",mnesia_config_test, + [ignore_fallback_at_startup], + "Not yet implemented"}. +{skip_cases,"../mnesia_test",mnesia_evil_backup, + [local_backup_checkpoint], + "Not yet implemented"}. +{skip_cases,"../mnesia_test",mnesia_config_test, + [max_wait_for_decision], + "Not yet implemented"}. +{skip_cases,"../mnesia_test",mnesia_recovery_test, + [after_full_disc_partition], + "Not yet implemented"}. +{skip_cases,"../mnesia_test",mnesia_recovery_test, + [system_upgrade], + "Not yet implemented"}. +{skip_cases,"../mnesia_test",mnesia_consistency_test, + [consistency_after_change_table_copy_type], + "Not yet implemented"}. +{skip_cases,"../mnesia_test",mnesia_consistency_test, + [consistency_after_transform_table_ram, + consistency_after_transform_table_disc, + consistency_after_transform_table_disc_only], + "Not yet implemented"}. +{skip_cases,"../mnesia_test",mnesia_consistency_test, + [consistency_after_rename_of_node], + "Not yet implemented"}. diff --git a/lib/mnesia/test/mnesia_SUITE.erl b/lib/mnesia/test/mnesia_SUITE.erl index b28deaf330..2267a94164 100644 --- a/lib/mnesia/test/mnesia_SUITE.erl +++ b/lib/mnesia/test/mnesia_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -26,135 +26,124 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Verify that Mnesia really is a distributed real-time DBMS", - "This is the test suite of the Mnesia DBMS. The test suite", - "covers many aspects of usage and is indended to be developed", - "incrementally. The test suite is divided into a hierarchy of test", - "suites where the leafs actually implements the test cases.", - "The intention of each test case and sub test suite can be", - "read in comments where they are implemented or in worst cases", - "from their long mnemonic names. ", - "", - "The most simple test case of them all is called 'silly'", - "and is useful to run now and then, e.g. when some new fatal", - "bug has been introduced. It may be run even if Mnesia is in", - "such a bad shape that the test machinery cannot be used.", - "NB! Invoke the function directly with mnesia_SUITE:silly()", - "and do not involve the normal test machinery."]; -all(suite) -> - [ - light, - medium, - heavy, - clean_up_suite - ]. +suite() -> [{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}]. + + +%% Verify that Mnesia really is a distributed real-time DBMS. +%% This is the test suite of the Mnesia DBMS. The test suite +%% covers many aspects of usage and is indended to be developed +%% incrementally. The test suite is divided into a hierarchy of test +%% suites where the leafs actually implements the test cases. +%% The intention of each test case and sub test suite can be +%% read in comments where they are implemented or in worst cases +%% from their long mnemonic names. +%% +%% The most simple test case of them all is called 'silly' +%% and is useful to run now and then, e.g. when some new fatal +%% bug has been introduced. It may be run even if Mnesia is in +%% such a bad shape that the test machinery cannot be used. +%% NB! Invoke the function directly with mnesia_SUITE:silly() +%% and do not involve the normal test machinery. + +all() -> + [{group, light}, {group, medium}, {group, heavy}, + clean_up_suite]. + +groups() -> + %% The 'light' test suite runs a selected set of test suites and is + %% intended to be the smallest test suite that is meaningful + %% to run. It starts with an installation test (which in essence is the + %% 'silly' test case) and then it covers all functions in the API in + %% various depths. All configuration parameters and examples are also + %% covered. + [{light, [], + [{group, install}, {group, nice}, {group, evil}, + {group, mnesia_frag_test, light}, {group, qlc}, + {group, registry}, {group, config}, {group, examples}]}, + {install, [], [{mnesia_install_test, all}]}, + {nice, [], [{mnesia_nice_coverage_test, all}]}, + {evil, [], [{mnesia_evil_coverage_test, all}]}, + {qlc, [], [{mnesia_qlc_test, all}]}, + {registry, [], [{mnesia_registry_test, all}]}, + {config, [], [{mnesia_config_test, all}]}, + {examples, [], [{mnesia_examples_test, all}]}, + %% The 'medium' test suite verfies the ACID (atomicity, consistency + %% isolation and durability) properties and various recovery scenarios + %% These tests may take quite while to run. + {medium, [], + [{group, install}, {group, atomicity}, + {group, isolation}, {group, durability}, + {group, recovery}, {group, consistency}, + {group, majority}, + {group, mnesia_frag_test, medium}]}, + {atomicity, [], [{mnesia_atomicity_test, all}]}, + {isolation, [], [{mnesia_isolation_test, all}]}, + {durability, [], [{mnesia_durability_test, all}]}, + {recovery, [], [{mnesia_recovery_test, all}]}, + {consistency, [], [{mnesia_consistency_test, all}]}, + {majority, [], [{mnesia_majority_test, all}]}, + %% The 'heavy' test suite runs some resource consuming tests and + %% benchmarks + {heavy, [], [{group, measure}]}, + {measure, [], [{mnesia_measure_test, all}]}, + {prediction, [], + [{group, mnesia_measure_test, prediction}]}, + {fairness, [], + [{group, mnesia_measure_test, fairness}]}, + {benchmarks, [], + [{group, mnesia_measure_test, benchmarks}]}, + {consumption, [], + [{group, mnesia_measure_test, consumption}]}, + {scalability, [], + [{group, mnesia_measure_test, scalability}]}, + %% This test suite is an extract of the grand Mnesia suite + %% it contains OTP R4B specific test cases + {otp_r4b, [], + [{mnesia_config_test, access_module}, + {mnesia_config_test, dump_log_load_regulation}, + {mnesia_config_test, embedded_mnemosyne}, + {mnesia_config_test, ignore_fallback_at_startup}, + {mnesia_config_test, max_wait_for_decision}, + {mnesia_consistency_test, consistency_after_restore}, + {mnesia_evil_backup, restore}, + {mnesia_evil_coverage_test, offline_set_master_nodes}, + {mnesia_evil_coverage_test, record_name}, + {mnesia_evil_coverage_test, user_properties}, + {mnesia_registry_test, all}, {group, otp_2363}]}, + %% Index on disc only tables + {otp_2363, [], + [{mnesia_dirty_access_test, + dirty_index_match_object_disc_only}, + {mnesia_dirty_access_test, dirty_index_read_disc_only}, + {mnesia_dirty_access_test, + dirty_index_update_bag_disc_only}, + {mnesia_dirty_access_test, + dirty_index_update_set_disc_only}, + {mnesia_evil_coverage_test, + create_live_table_index_disc_only}]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +init_per_suite(Config) -> + Config. + +end_per_suite(Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% silly() -> mnesia_install_test:silly(). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -light(doc) -> - ["The 'light' test suite runs a selected set of test suites and is", - "intended to be the smallest test suite that is meaningful", - "to run. It starts with an installation test (which in essence is the", - "'silly' test case) and then it covers all functions in the API in", - "various depths. All configuration parameters and examples are also", - "covered."]; -light(suite) -> - [ - install, - nice, - evil, - {mnesia_frag_test, light}, - qlc, - registry, - config, - examples - ]. - -install(suite) -> - [{mnesia_install_test, all}]. - -nice(suite) -> - [{mnesia_nice_coverage_test, all}]. - -evil(suite) -> - [{mnesia_evil_coverage_test, all}]. - -qlc(suite) -> - [{mnesia_qlc_test, all}]. - -registry(suite) -> - [{mnesia_registry_test, all}]. - -config(suite) -> - [{mnesia_config_test, all}]. - -examples(suite) -> - [{mnesia_examples_test, all}]. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -medium(doc) -> - ["The 'medium' test suite verfies the ACID (atomicity, consistency", - "isolation and durability) properties and various recovery scenarios", - "These tests may take quite while to run."]; -medium(suite) -> - [ - install, - atomicity, - isolation, - durability, - recovery, - consistency, - {mnesia_frag_test, medium} - ]. - -atomicity(suite) -> - [{mnesia_atomicity_test, all}]. - -isolation(suite) -> - [{mnesia_isolation_test, all}]. - -durability(suite) -> - [{mnesia_durability_test, all}]. - -recovery(suite) -> - [{mnesia_recovery_test, all}]. - -consistency(suite) -> - [{mnesia_consistency_test, all}]. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -heavy(doc) -> - ["The 'heavy' test suite runs some resource consuming tests and", - "benchmarks"]; -heavy(suite) -> - [measure]. - -measure(suite) -> - [{mnesia_measure_test, all}]. - -prediction(suite) -> - [{mnesia_measure_test, prediction}]. - -fairness(suite) -> - [{mnesia_measure_test, fairness}]. - -benchmarks(suite) -> - [{mnesia_measure_test, benchmarks}]. - -consumption(suite) -> - [{mnesia_measure_test, consumption}]. - -scalability(suite) -> - [{mnesia_measure_test, scalability}]. - clean_up_suite(doc) -> ["Not a test case only kills mnesia and nodes, that where" "started during the tests"]; @@ -169,35 +158,7 @@ clean_up_suite(Config) when is_list(Config)-> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -otp_r4b(doc) -> - ["This test suite is an extract of the grand Mnesia suite", - "it contains OTP R4B specific test cases"]; -otp_r4b(suite) -> - [ - {mnesia_config_test, access_module}, - {mnesia_config_test, dump_log_load_regulation}, - {mnesia_config_test, embedded_mnemosyne}, - {mnesia_config_test, ignore_fallback_at_startup}, - {mnesia_config_test, max_wait_for_decision}, - {mnesia_consistency_test, consistency_after_restore}, - {mnesia_evil_backup, restore}, - {mnesia_evil_coverage_test, offline_set_master_nodes}, - {mnesia_evil_coverage_test, record_name}, - {mnesia_evil_coverage_test, user_properties}, - {mnesia_registry_test, all}, - otp_2363 - ]. - -otp_2363(doc) -> - ["Index on disc only tables"]; -otp_2363(suite) -> - [ - {mnesia_dirty_access_test, dirty_index_match_object_disc_only}, - {mnesia_dirty_access_test,dirty_index_read_disc_only}, - {mnesia_dirty_access_test,dirty_index_update_bag_disc_only}, - {mnesia_dirty_access_test,dirty_index_update_set_disc_only}, - {mnesia_evil_coverage_test, create_live_table_index_disc_only} - ]. + diff --git a/lib/mnesia/test/mnesia_atomicity_test.erl b/lib/mnesia/test/mnesia_atomicity_test.erl index 645c203a91..cf878fc820 100644 --- a/lib/mnesia/test/mnesia_atomicity_test.erl +++ b/lib/mnesia/test/mnesia_atomicity_test.erl @@ -27,24 +27,46 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Verify atomicity of transactions", - "Verify that transactions are atomic, i.e. either all operations", - "in a transaction will be performed or none of them. It must be", - "assured that no partitially completed operations leaves any", - "effects in the database."]; -all(suite) -> - [ - explicit_abort_in_middle_of_trans, +all() -> + [explicit_abort_in_middle_of_trans, runtime_error_in_middle_of_trans, - kill_self_in_middle_of_trans, - throw_in_middle_of_trans, - mnesia_down_in_middle_of_trans - ]. + kill_self_in_middle_of_trans, throw_in_middle_of_trans, + {group, mnesia_down_in_middle_of_trans}]. + +groups() -> + [{mnesia_down_in_middle_of_trans, [], + [mnesia_down_during_infinite_trans, + {group, lock_waiter}, {group, restart_check}]}, + {lock_waiter, [], + [lock_waiter_sw_r, lock_waiter_sw_rt, lock_waiter_sw_wt, + lock_waiter_wr_r, lock_waiter_srw_r, lock_waiter_sw_sw, + lock_waiter_sw_w, lock_waiter_sw_wr, lock_waiter_sw_srw, + lock_waiter_wr_wt, lock_waiter_srw_wt, + lock_waiter_wr_sw, lock_waiter_srw_sw, lock_waiter_wr_w, + lock_waiter_srw_w, lock_waiter_r_sw, lock_waiter_r_w, + lock_waiter_r_wt, lock_waiter_rt_sw, lock_waiter_rt_w, + lock_waiter_rt_wt, lock_waiter_wr_wr, + lock_waiter_srw_srw, lock_waiter_wt_r, lock_waiter_wt_w, + lock_waiter_wt_rt, lock_waiter_wt_wt, lock_waiter_wt_wr, + lock_waiter_wt_srw, lock_waiter_wt_sw, lock_waiter_w_wr, + lock_waiter_w_srw, lock_waiter_w_sw, lock_waiter_w_r, + lock_waiter_w_w, lock_waiter_w_rt, lock_waiter_w_wt]}, + {restart_check, [], + [restart_r_one, restart_w_one, restart_rt_one, + restart_wt_one, restart_wr_one, restart_sw_one, + restart_r_two, restart_w_two, restart_rt_two, + restart_wt_two, restart_wr_two, restart_sw_two]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% explicit_abort_in_middle_of_trans(suite) -> []; @@ -259,12 +281,6 @@ throw_in_middle_of_trans(Config) when is_list(Config) -> ?verify_mnesia(Nodes, []). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -mnesia_down_in_middle_of_trans(suite) -> - [ - mnesia_down_during_infinite_trans, - lock_waiter, - restart_check - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% mnesia_down_during_infinite_trans(suite) -> []; @@ -304,56 +320,6 @@ mnesia_down_during_infinite_trans(Config) when is_list(Config) -> ?verify_mnesia([Node2], [Node1]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -lock_waiter(doc) -> - ["The purpose of this test case is to test the following situation:", - "process B locks an object, process A accesses that object as", - "well, but A has to wait for the lock to be released. Then", - "mnesia of B goes down. Question: will A get the lock ?", - "important: the transaction of A is the oldest one !!! (= a little tricky)", - "", - "several different access operations shall be tested", - "rt = read_lock_table, wt = write_lock_table, r = read,", - "sw = s_write, w = write, wr = wread"]; -lock_waiter(suite) -> - [ - lock_waiter_sw_r, - lock_waiter_sw_rt, - lock_waiter_sw_wt, - lock_waiter_wr_r, - lock_waiter_srw_r, - lock_waiter_sw_sw, - lock_waiter_sw_w, - lock_waiter_sw_wr, - lock_waiter_sw_srw, - lock_waiter_wr_wt, - lock_waiter_srw_wt, - lock_waiter_wr_sw, - lock_waiter_srw_sw, - lock_waiter_wr_w, - lock_waiter_srw_w, - lock_waiter_r_sw, - lock_waiter_r_w, - lock_waiter_r_wt, - lock_waiter_rt_sw, - lock_waiter_rt_w, - lock_waiter_rt_wt, - lock_waiter_wr_wr, - lock_waiter_srw_srw, - lock_waiter_wt_r, - lock_waiter_wt_w, - lock_waiter_wt_rt, - lock_waiter_wt_wt, - lock_waiter_wt_wr, - lock_waiter_wt_srw, - lock_waiter_wt_sw, - lock_waiter_w_wr, - lock_waiter_w_srw, - lock_waiter_w_sw, - lock_waiter_w_r, - lock_waiter_w_w, - lock_waiter_w_rt, - lock_waiter_w_wt - ]. lock_waiter_sw_r(suite) -> []; lock_waiter_sw_r(Config) when is_list(Config) -> @@ -649,29 +615,6 @@ wait(Mseconds) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -restart_check (doc) -> - [ - "test case:'A' performs a transaction on a table which", - "is only replicated on node B. During that transaction", - "mnesia on node B is killed. The transaction of A should", - "be stopped, since there is no further replica", - "rt = read_lock_table, wt = write_lock_table, r = read,", - "sw = s_write, w = write, wr = wread,"]; -restart_check(suite) -> - [ - restart_r_one, - restart_w_one, - restart_rt_one, - restart_wt_one, - restart_wr_one, - restart_sw_one, - restart_r_two, - restart_w_two, - restart_rt_two, - restart_wt_two, - restart_wr_two, - restart_sw_two - ]. restart_r_one(suite) -> []; restart_r_one(Config) when is_list(Config) -> diff --git a/lib/mnesia/test/mnesia_config_backup.erl b/lib/mnesia/test/mnesia_config_backup.erl index a33ec6ac5c..0916e255e2 100644 --- a/lib/mnesia/test/mnesia_config_backup.erl +++ b/lib/mnesia/test/mnesia_config_backup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/mnesia/test/mnesia_config_event.erl b/lib/mnesia/test/mnesia_config_event.erl index 6c1dea7ed5..832bf94eb9 100644 --- a/lib/mnesia/test/mnesia_config_event.erl +++ b/lib/mnesia/test/mnesia_config_event.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/mnesia/test/mnesia_config_test.erl b/lib/mnesia/test/mnesia_config_test.erl index 7b62c63a62..93510d539c 100644 --- a/lib/mnesia/test/mnesia_config_test.erl +++ b/lib/mnesia/test/mnesia_config_test.erl @@ -27,14 +27,14 @@ -record(test_table2,{i, b}). -export([ - all/1, + all/0,groups/0,init_per_group/2,end_per_group/2, access_module/1, auto_repair/1, backup_module/1, debug/1, dir/1, dump_log_load_regulation/1, - dump_log_thresholds/1, + dump_log_update_in_place/1, embedded_mnemosyne/1, event_module/1, @@ -44,7 +44,7 @@ send_compressed/1, app_test/1, - schema_config/1, + schema_merge/1, unknown_config/1, @@ -56,13 +56,13 @@ start_first_one_disc_less_then_two_more_disc_less/1, schema_location_and_extra_db_nodes_combinations/1, table_load_to_disc_less_nodes/1, - dynamic_connect/1, + dynamic_basic/1, dynamic_ext/1, dynamic_bad/1, init_per_testcase/2, - fin_per_testcase/2, + end_per_testcase/2, c_nodes/0 ]). @@ -95,46 +95,40 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - [ - "Test all configuration parameters", - "Perform an exhaustive test of all the various parameters that", - "may be used to configure the Mnesia application.", - "", - "Hint: Check out the unofficial function mnesia:start/1.", - " But be careful to cleanup all configuration parameters", - " afterwards since the rest of the test suite may rely on", - " these default configurations. Perhaps it is best to run", - " these tests in a separate node which is dropped afterwards.", - "Are really all configuration parameters covered?"]; - -all(suite) -> - [ - access_module, - auto_repair, - backup_module, - debug, - dir, - dump_log_load_regulation, - dump_log_thresholds, - dump_log_update_in_place, - embedded_mnemosyne, - event_module, - ignore_fallback_at_startup, - inconsistent_database, - max_wait_for_decision, - send_compressed, - - app_test, - schema_config, - unknown_config - ]. +all() -> + [access_module, auto_repair, backup_module, debug, dir, + dump_log_load_regulation, {group, dump_log_thresholds}, + dump_log_update_in_place, embedded_mnemosyne, + event_module, ignore_fallback_at_startup, + inconsistent_database, max_wait_for_decision, + send_compressed, app_test, {group, schema_config}, + unknown_config]. + +groups() -> + [{dump_log_thresholds, [], + [dump_log_time_threshold, dump_log_write_threshold]}, + {schema_config, [], + [start_one_disc_full_then_one_disc_less, + start_first_one_disc_less_then_one_disc_full, + start_first_one_disc_less_then_two_more_disc_less, + schema_location_and_extra_db_nodes_combinations, + table_load_to_disc_less_nodes, schema_merge, + {group, dynamic_connect}]}, + {dynamic_connect, [], + [dynamic_basic, dynamic_ext, dynamic_bad]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -445,21 +439,6 @@ dump_log_update_in_place(Config) when is_list(Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -dump_log_thresholds(doc) -> - ["Elaborate with various values of the dump log thresholds and how", - "they affects each others. Both the dump_log_time_threshold and the", - "dump_log_write_threshold must be covered. Do also check that both", - "kinds of overload events are generated as expected.", - "", - "Logs are checked by first doing whatever has to be done to trigger ", - "a dump, and then stopping Mnesia and then look in the ", - "data files and see that the correct amount of transactions ", - "have been done."]; -dump_log_thresholds(suite) -> - [ - dump_log_time_threshold, - dump_log_write_threshold - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dump_log_write_threshold(doc)-> @@ -783,22 +762,6 @@ event_module(Config) when is_list(Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -schema_config(doc) -> - ["Try many configurations with various schema_location's with and", - "without explicit extra_db_nodes. Do also provoke various schema merge", - "situations. Most of the other test suites focusses on tests where the", - "schema is residing on disc. Now it is time to perform an exhaustive", - "elaboration with various disc less configurations."]; -schema_config(suite) -> - [ - start_one_disc_full_then_one_disc_less, - start_first_one_disc_less_then_one_disc_full, - start_first_one_disc_less_then_two_more_disc_less, - schema_location_and_extra_db_nodes_combinations, - table_load_to_disc_less_nodes, - schema_merge, - dynamic_connect - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% start_one_disc_full_then_one_disc_less(doc)-> ["Start a disk node and then a disk less one. Distribute some", @@ -1160,15 +1123,6 @@ sort(NS) when is_list(NS) -> lists:sort(NS). -dynamic_connect(doc) -> - ["Test the new functionality where we start mnesia first and then " - "connect to the other mnesia nodes"]; -dynamic_connect(suite) -> - [ - dynamic_basic, - dynamic_ext, - dynamic_bad - ]. dynamic_basic(suite) -> []; diff --git a/lib/mnesia/test/mnesia_consistency_test.erl b/lib/mnesia/test/mnesia_consistency_test.erl index ffe8ab7ac3..f38e13f3a2 100644 --- a/lib/mnesia/test/mnesia_consistency_test.erl +++ b/lib/mnesia/test/mnesia_consistency_test.erl @@ -27,33 +27,121 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Verify transaction consistency", - "Consistency is the property of the application that requires any", - "execution of the transaction to take the database from one", - "consistent state to another. Verify that the database is", - "consistent at any point in time.", - "Verify for various configurations.", - " Verify for both set and bag"]; -all(suite) -> - [ - consistency_after_restart, - consistency_after_dump_tables, - consistency_after_add_replica, - consistency_after_del_replica, - consistency_after_move_replica, - consistency_after_transform_table, +all() -> + [{group, consistency_after_restart}, + {group, consistency_after_dump_tables}, + {group, consistency_after_add_replica}, + {group, consistency_after_del_replica}, + {group, consistency_after_move_replica}, + {group, consistency_after_transform_table}, consistency_after_change_table_copy_type, - consistency_after_fallback, - consistency_after_restore, + {group, consistency_after_fallback}, + {group, consistency_after_restore}, consistency_after_rename_of_node, - checkpoint_retainer_consistency, - backup_consistency - ]. + {group, checkpoint_retainer_consistency}, + {group, backup_consistency}]. + +groups() -> + [{consistency_after_restart, [], + [consistency_after_restart_1_ram, + consistency_after_restart_1_disc, + consistency_after_restart_1_disc_only, + consistency_after_restart_2_ram, + consistency_after_restart_2_disc, + consistency_after_restart_2_disc_only]}, + {consistency_after_dump_tables, [], + [consistency_after_dump_tables_1_ram, + consistency_after_dump_tables_2_ram]}, + {consistency_after_add_replica, [], + [consistency_after_add_replica_2_ram, + consistency_after_add_replica_2_disc, + consistency_after_add_replica_2_disc_only, + consistency_after_add_replica_3_ram, + consistency_after_add_replica_3_disc, + consistency_after_add_replica_3_disc_only]}, + {consistency_after_del_replica, [], + [consistency_after_del_replica_2_ram, + consistency_after_del_replica_2_disc, + consistency_after_del_replica_2_disc_only, + consistency_after_del_replica_3_ram, + consistency_after_del_replica_3_disc, + consistency_after_del_replica_3_disc_only]}, + {consistency_after_move_replica, [], + [consistency_after_move_replica_2_ram, + consistency_after_move_replica_2_disc, + consistency_after_move_replica_2_disc_only, + consistency_after_move_replica_3_ram, + consistency_after_move_replica_3_disc, + consistency_after_move_replica_3_disc_only]}, + {consistency_after_transform_table, [], + [consistency_after_transform_table_ram, + consistency_after_transform_table_disc, + consistency_after_transform_table_disc_only]}, + {consistency_after_fallback, [], + [consistency_after_fallback_2_ram, + consistency_after_fallback_2_disc, + consistency_after_fallback_2_disc_only, + consistency_after_fallback_3_ram, + consistency_after_fallback_3_disc, + consistency_after_fallback_3_disc_only]}, + {consistency_after_restore, [], + [consistency_after_restore_clear_ram, + consistency_after_restore_clear_disc, + consistency_after_restore_clear_disc_only, + consistency_after_restore_recreate_ram, + consistency_after_restore_recreate_disc, + consistency_after_restore_recreate_disc_only]}, + {checkpoint_retainer_consistency, [], + [{group, updates_during_checkpoint_activation}, + {group, updates_during_checkpoint_iteration}, + {group, load_table_with_activated_checkpoint}, + {group, + add_table_copy_to_table_with_activated_checkpoint}]}, + {updates_during_checkpoint_activation, [], + [updates_during_checkpoint_activation_2_ram, + updates_during_checkpoint_activation_2_disc, + updates_during_checkpoint_activation_2_disc_only, + updates_during_checkpoint_activation_3_ram, + updates_during_checkpoint_activation_3_disc, + updates_during_checkpoint_activation_3_disc_only]}, + {updates_during_checkpoint_iteration, [], + [updates_during_checkpoint_iteration_2_ram, + updates_during_checkpoint_iteration_2_disc, + updates_during_checkpoint_iteration_2_disc_only]}, + {load_table_with_activated_checkpoint, [], + [load_table_with_activated_checkpoint_ram, + load_table_with_activated_checkpoint_disc, + load_table_with_activated_checkpoint_disc_only]}, + {add_table_copy_to_table_with_activated_checkpoint, [], + [add_table_copy_to_table_with_activated_checkpoint_ram, + add_table_copy_to_table_with_activated_checkpoint_disc, + add_table_copy_to_table_with_activated_checkpoint_disc_only]}, + {backup_consistency, [], + [{group, interupted_install_fallback}, + {group, interupted_uninstall_fallback}, + {group, mnesia_down_during_backup_causes_switch}, + {group, mnesia_down_during_backup_causes_abort}, + {group, schema_transactions_during_backup}]}, + {interupted_install_fallback, [], + [inst_fallback_process_dies, fatal_when_inconsistency]}, + {interupted_uninstall_fallback, [], [after_delete]}, + {mnesia_down_during_backup_causes_switch, [], + [cause_switch_before, cause_switch_after]}, + {mnesia_down_during_backup_causes_abort, [], + [cause_abort_before, cause_abort_after]}, + {schema_transactions_during_backup, [], + [change_schema_before, change_schema_after]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % @@ -185,15 +273,6 @@ receive_messages(ListOfMsgs) -> end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -consistency_after_restart(suite) -> - [ - consistency_after_restart_1_ram, - consistency_after_restart_1_disc, - consistency_after_restart_1_disc_only, - consistency_after_restart_2_ram, - consistency_after_restart_2_disc, - consistency_after_restart_2_disc_only - ]. consistency_after_restart_1_ram(suite) -> []; consistency_after_restart_1_ram(Config) when is_list(Config) -> @@ -237,11 +316,6 @@ consistency_after_restart(ReplicaType, NodeConfig, Config) -> ?verify_mnesia(Nodes, []). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -consistency_after_dump_tables(suite) -> - [ - consistency_after_dump_tables_1_ram, - consistency_after_dump_tables_2_ram - ]. consistency_after_dump_tables_1_ram(suite) -> []; consistency_after_dump_tables_1_ram(Config) when is_list(Config) -> @@ -274,15 +348,6 @@ consistency_after_dump_tables(ReplicaType, NodeConfig, Config) -> ?verify_mnesia(Nodes, []). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -consistency_after_add_replica(suite) -> - [ - consistency_after_add_replica_2_ram, - consistency_after_add_replica_2_disc, - consistency_after_add_replica_2_disc_only, - consistency_after_add_replica_3_ram, - consistency_after_add_replica_3_disc, - consistency_after_add_replica_3_disc_only - ]. consistency_after_add_replica_2_ram(suite) -> []; consistency_after_add_replica_2_ram(Config) when is_list(Config) -> @@ -326,15 +391,6 @@ consistency_after_add_replica(ReplicaType, NodeConfig, Config) -> ?verify_mnesia(Nodes0, []). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -consistency_after_del_replica(suite) -> - [ - consistency_after_del_replica_2_ram, - consistency_after_del_replica_2_disc, - consistency_after_del_replica_2_disc_only, - consistency_after_del_replica_3_ram, - consistency_after_del_replica_3_disc, - consistency_after_del_replica_3_disc_only - ]. consistency_after_del_replica_2_ram(suite) -> []; consistency_after_del_replica_2_ram(Config) when is_list(Config) -> @@ -377,15 +433,6 @@ consistency_after_del_replica(ReplicaType, NodeConfig, Config) -> ?verify_mnesia(Nodes, []). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -consistency_after_move_replica(suite) -> - [ - consistency_after_move_replica_2_ram, - consistency_after_move_replica_2_disc, - consistency_after_move_replica_2_disc_only, - consistency_after_move_replica_3_ram, - consistency_after_move_replica_3_disc, - consistency_after_move_replica_3_disc_only - ]. consistency_after_move_replica_2_ram(suite) -> []; consistency_after_move_replica_2_ram(Config) when is_list(Config) -> @@ -430,16 +477,6 @@ consistency_after_move_replica(ReplicaType, NodeConfig, Config) -> ?verify_mnesia(Nodes, []). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -consistency_after_transform_table(doc) -> - ["Check that the database is consistent after transform_table.", - " While applications are updating the involved tables. "]; - -consistency_after_transform_table(suite) -> - [ - consistency_after_transform_table_ram, - consistency_after_transform_table_disc, - consistency_after_transform_table_disc_only - ]. consistency_after_transform_table_ram(suite) -> []; @@ -498,20 +535,6 @@ consistency_after_change_table_copy_type(doc) -> " While applications are updating the involved tables. "]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -consistency_after_fallback(doc) -> - ["Check that installed fallbacks are consistent. Check this by starting ", - "some nodes, run tpcb on them, take a backup at any time, install it ", - "as a fallback, kill all nodes, start mnesia again and check for ", - "any inconsistencies"]; -consistency_after_fallback(suite) -> - [ - consistency_after_fallback_2_ram, - consistency_after_fallback_2_disc, - consistency_after_fallback_2_disc_only, - consistency_after_fallback_3_ram, - consistency_after_fallback_3_disc - , consistency_after_fallback_3_disc_only - ]. consistency_after_fallback_2_ram(suite) -> []; consistency_after_fallback_2_ram(Config) when is_list(Config) -> @@ -583,18 +606,6 @@ consistency_after_fallback(ReplicaType, NodeConfig, Config) -> ?verify_mnesia(Nodes, []). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -consistency_after_restore(doc) -> - ["Verify consistency after restore operations."]; - -consistency_after_restore(suite) -> - [ - consistency_after_restore_clear_ram, - consistency_after_restore_clear_disc, - consistency_after_restore_clear_disc_only, - consistency_after_restore_recreate_ram, - consistency_after_restore_recreate_disc, - consistency_after_restore_recreate_disc_only - ]. consistency_after_restore_clear_ram(suite) -> []; consistency_after_restore_clear_ram(Config) when is_list(Config) -> @@ -716,32 +727,8 @@ consistency_after_rename_of_node(doc) -> ["Skipped because it is an unimportant case."]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -checkpoint_retainer_consistency(doc) -> - ["Verify that the contents of a checkpoint retainer has the expected", - "contents in various situations."]; -checkpoint_retainer_consistency(suite) -> - [ - updates_during_checkpoint_activation, - updates_during_checkpoint_iteration, - load_table_with_activated_checkpoint, - add_table_copy_to_table_with_activated_checkpoint - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -updates_during_checkpoint_activation(doc) -> - ["Perform updates while the checkpoint getting activated", - "and verify that all checkpoint retainers associated with", - "different replicas of the same table really has the same", - "contents."]; -updates_during_checkpoint_activation(suite) -> - [ - updates_during_checkpoint_activation_2_ram, - updates_during_checkpoint_activation_2_disc, - updates_during_checkpoint_activation_2_disc_only, - updates_during_checkpoint_activation_3_ram, - updates_during_checkpoint_activation_3_disc - , updates_during_checkpoint_activation_3_disc_only - ]. updates_during_checkpoint_activation_2_ram(suite) -> []; updates_during_checkpoint_activation_2_ram(Config) when is_list(Config) -> @@ -808,17 +795,6 @@ updates_during_checkpoint_activation(ReplicaType,NodeConfig,Config) -> ?verify_mnesia(Nodes, []). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -updates_during_checkpoint_iteration(doc) -> - ["Perform updates while someone is iterating over a checkpoint", - "and verify that the iterator really finds the expected data", - "regardless of ongoing upates."]; - -updates_during_checkpoint_iteration(suite) -> - [ - updates_during_checkpoint_iteration_2_ram, - updates_during_checkpoint_iteration_2_disc - , updates_during_checkpoint_iteration_2_disc_only - ]. updates_during_checkpoint_iteration_2_ram(suite) -> []; updates_during_checkpoint_iteration_2_ram(Config) when is_list(Config) -> @@ -890,17 +866,6 @@ loop_accounts(N_br, N_acc) when N_acc >= 1 -> loop_accounts(_,_) -> done. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -load_table_with_activated_checkpoint(doc) -> - ["Load a table with a checkpoint attached to it and verify that the", - "newly loaded replica also gets a checkpoint retainer attached to it", - "and that it is consistent with the original retainer."]; - -load_table_with_activated_checkpoint(suite) -> - [ - load_table_with_activated_checkpoint_ram, - load_table_with_activated_checkpoint_disc, - load_table_with_activated_checkpoint_disc_only - ]. load_table_with_activated_checkpoint_ram(suite) -> []; load_table_with_activated_checkpoint_ram(Config) when is_list(Config) -> @@ -986,18 +951,6 @@ view(Source, Mod) -> lists:sort(TabList). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -add_table_copy_to_table_with_activated_checkpoint(doc) -> - ["Add a replica to a table with a checkpoint attached to it", - "and verify that the new replica also gets a checkpoint", - "retainer attached to it and that it is consistent with the", - "original retainer."]; - -add_table_copy_to_table_with_activated_checkpoint(suite) -> - [ - add_table_copy_to_table_with_activated_checkpoint_ram, - add_table_copy_to_table_with_activated_checkpoint_disc, - add_table_copy_to_table_with_activated_checkpoint_disc_only - ]. add_table_copy_to_table_with_activated_checkpoint_ram(suite) -> []; add_table_copy_to_table_with_activated_checkpoint_ram(Config) when is_list(Config) -> @@ -1070,25 +1023,8 @@ add_table_copy_to_table_with_activated_checkpoint(Type,Config) -> ?verify_mnesia(Nodes, []). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -backup_consistency(suite) -> - [ - interupted_install_fallback, - interupted_uninstall_fallback, - mnesia_down_during_backup_causes_switch, - mnesia_down_during_backup_causes_abort, - schema_transactions_during_backup - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -interupted_install_fallback(doc) -> - ["Verify that a interrupted install_fallback really", - "is performed on all nodes or none"]; - -interupted_install_fallback(suite) -> - [ - inst_fallback_process_dies, - fatal_when_inconsistency - ]. inst_fallback_process_dies(suite) -> []; @@ -1232,13 +1168,6 @@ is_running(Node, Shouldbe) -> end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -interupted_uninstall_fallback(doc) -> - ["Verify that a interrupted uninstall_fallback really", - "is performed on all nodes or none"]; -interupted_uninstall_fallback(suite) -> - [ - after_delete - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -1371,17 +1300,6 @@ do_uninstall(Config,DebugPoint) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -mnesia_down_during_backup_causes_switch(doc) -> - ["Verify that an ongoing backup is not disturbed", - "even if the node hosting the replica that currently", - "is being backup'ed is stopped. The backup utility", - "is expected to switch over to another replica and", - "fulfill the backup."]; -mnesia_down_during_backup_causes_switch(suite) -> - [ - cause_switch_before, - cause_switch_after - ]. %%%%%%%%%%%%%%% @@ -1401,16 +1319,6 @@ cause_switch_after(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -mnesia_down_during_backup_causes_abort(doc) -> - ["Verify that an ongoing backup is aborted nicely", - "without leaving any backup file if the last replica", - "of a table becomes unavailable due to a node down", - "or some crash."]; -mnesia_down_during_backup_causes_abort(suite) -> - [ - cause_abort_before, - cause_abort_after - ]. %%%%%%%%%%%%%%%%%% @@ -1432,14 +1340,6 @@ cause_abort_after(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -schema_transactions_during_backup(doc) -> - ["Verify that an schema transactions does not", - "affect an ongoing backup."]; -schema_transactions_during_backup(suite) -> - [ - change_schema_before, - change_schema_after - ]. %%%%%%%%%%%%% diff --git a/lib/mnesia/test/mnesia_cost.erl b/lib/mnesia/test/mnesia_cost.erl index 54cb2b3064..3221f46f61 100644 --- a/lib/mnesia/test/mnesia_cost.erl +++ b/lib/mnesia/test/mnesia_cost.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/mnesia/test/mnesia_dirty_access_test.erl b/lib/mnesia/test/mnesia_dirty_access_test.erl index 5f9f2a9733..abbdab48c0 100644 --- a/lib/mnesia/test/mnesia_dirty_access_test.erl +++ b/lib/mnesia/test/mnesia_dirty_access_test.erl @@ -26,37 +26,72 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Evil dirty access, regardless of transaction scope.", - "Invoke all functions in the API and try to cover all legal uses", - "cases as well the illegal dito. This is a complement to the", - "other more explicit test cases."]; -all(suite) -> - [ - dirty_write, - dirty_read, - dirty_update_counter, - dirty_delete, - dirty_delete_object, - dirty_match_object, - dirty_index, - dirty_iter, - admin_tests - ]. +all() -> + [{group, dirty_write}, {group, dirty_read}, + {group, dirty_update_counter}, {group, dirty_delete}, + {group, dirty_delete_object}, + {group, dirty_match_object}, {group, dirty_index}, + {group, dirty_iter}, {group, admin_tests}]. + +groups() -> + [{dirty_write, [], + [dirty_write_ram, dirty_write_disc, + dirty_write_disc_only]}, + {dirty_read, [], + [dirty_read_ram, dirty_read_disc, + dirty_read_disc_only]}, + {dirty_update_counter, [], + [dirty_update_counter_ram, dirty_update_counter_disc, + dirty_update_counter_disc_only]}, + {dirty_delete, [], + [dirty_delete_ram, dirty_delete_disc, + dirty_delete_disc_only]}, + {dirty_delete_object, [], + [dirty_delete_object_ram, dirty_delete_object_disc, + dirty_delete_object_disc_only]}, + {dirty_match_object, [], + [dirty_match_object_ram, dirty_match_object_disc, + dirty_match_object_disc_only]}, + {dirty_index, [], + [{group, dirty_index_match_object}, + {group, dirty_index_read}, + {group, dirty_index_update}]}, + {dirty_index_match_object, [], + [dirty_index_match_object_ram, + dirty_index_match_object_disc, + dirty_index_match_object_disc_only]}, + {dirty_index_read, [], + [dirty_index_read_ram, dirty_index_read_disc, + dirty_index_read_disc_only]}, + {dirty_index_update, [], + [dirty_index_update_set_ram, + dirty_index_update_set_disc, + dirty_index_update_set_disc_only, + dirty_index_update_bag_ram, dirty_index_update_bag_disc, + dirty_index_update_bag_disc_only]}, + {dirty_iter, [], + [dirty_iter_ram, dirty_iter_disc, + dirty_iter_disc_only]}, + {admin_tests, [], + [del_table_copy_1, del_table_copy_2, del_table_copy_3, + add_table_copy_1, add_table_copy_2, add_table_copy_3, + add_table_copy_4, move_table_copy_1, move_table_copy_2, + move_table_copy_3, move_table_copy_4]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Write records dirty -dirty_write(suite) -> - [ - dirty_write_ram, - dirty_write_disc, - dirty_write_disc_only - ]. dirty_write_ram(suite) -> []; dirty_write_ram(Config) when is_list(Config) -> @@ -88,12 +123,6 @@ dirty_write(Config, Storage) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Read records dirty -dirty_read(suite) -> - [ - dirty_read_ram, - dirty_read_disc, - dirty_read_disc_only - ]. dirty_read_ram(suite) -> []; dirty_read_ram(Config) when is_list(Config) -> @@ -137,12 +166,6 @@ dirty_read(Config, Storage) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Update counter record dirty -dirty_update_counter(suite) -> - [ - dirty_update_counter_ram, - dirty_update_counter_disc, - dirty_update_counter_disc_only - ]. dirty_update_counter_ram(suite) -> []; dirty_update_counter_ram(Config) when is_list(Config) -> @@ -180,12 +203,6 @@ dirty_update_counter(Config, Storage) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Delete record dirty -dirty_delete(suite) -> - [ - dirty_delete_ram, - dirty_delete_disc, - dirty_delete_disc_only - ]. dirty_delete_ram(suite) -> []; dirty_delete_ram(Config) when is_list(Config) -> @@ -223,12 +240,6 @@ dirty_delete(Config, Storage) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Delete matching record dirty -dirty_delete_object(suite) -> - [ - dirty_delete_object_ram, - dirty_delete_object_disc, - dirty_delete_object_disc_only - ]. dirty_delete_object_ram(suite) -> []; dirty_delete_object_ram(Config) when is_list(Config) -> @@ -272,12 +283,6 @@ dirty_delete_object(Config, Storage) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Read matching records dirty -dirty_match_object(suite) -> - [ - dirty_match_object_ram, - dirty_match_object_disc, - dirty_match_object_disc_only - ]. dirty_match_object_ram(suite) -> []; dirty_match_object_ram(Config) when is_list(Config) -> @@ -311,22 +316,10 @@ dirty_match_object(Config, Storage) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -dirty_index(suite) -> - [ - dirty_index_match_object, - dirty_index_read, - dirty_index_update - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Dirty read matching records by using an index -dirty_index_match_object(suite) -> - [ - dirty_index_match_object_ram, - dirty_index_match_object_disc, - dirty_index_match_object_disc_only - ]. dirty_index_match_object_ram(suite) -> []; dirty_index_match_object_ram(Config) when is_list(Config) -> @@ -364,12 +357,6 @@ dirty_index_match_object(Config, Storage) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Read records by using an index -dirty_index_read(suite) -> - [ - dirty_index_read_ram, - dirty_index_read_disc, - dirty_index_read_disc_only - ]. dirty_index_read_ram(suite) -> []; dirty_index_read_ram(Config) when is_list(Config) -> @@ -413,19 +400,6 @@ dirty_index_read(Config, Storage) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -dirty_index_update(suite) -> - [ - dirty_index_update_set_ram, - dirty_index_update_set_disc, - dirty_index_update_set_disc_only, - dirty_index_update_bag_ram, - dirty_index_update_bag_disc, - dirty_index_update_bag_disc_only - ]; -dirty_index_update(doc) -> - ["See Ticket OTP-2083, verifies that a table with a index is " - "update in the correct way i.e. the index finds the correct " - "records after a update"]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dirty_index_update_set_ram(suite) -> []; @@ -631,12 +605,6 @@ dirty_index_update_bag(Config, Storage) -> %% Dirty iteration %% dirty_slot, dirty_first, dirty_next -dirty_iter(suite) -> - [ - dirty_iter_ram, - dirty_iter_disc, - dirty_iter_disc_only - ]. dirty_iter_ram(suite) -> []; dirty_iter_ram(Config) when is_list(Config) -> @@ -700,21 +668,6 @@ all_nexts(Tab, PrevKey) -> [PrevKey] ++ all_nexts(Tab, Key). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -admin_tests(doc) -> - ["Verifies that dirty operations work during schema operations"]; - -admin_tests(suite) -> - [del_table_copy_1, - del_table_copy_2, - del_table_copy_3, - add_table_copy_1, - add_table_copy_2, - add_table_copy_3, - add_table_copy_4, - move_table_copy_1, - move_table_copy_2, - move_table_copy_3, - move_table_copy_4]. update_trans(Tab, Key, Acc) -> Update = diff --git a/lib/mnesia/test/mnesia_durability_test.erl b/lib/mnesia/test/mnesia_durability_test.erl index b917b0ca40..2fee72f066 100644 --- a/lib/mnesia/test/mnesia_durability_test.erl +++ b/lib/mnesia/test/mnesia_durability_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,47 +28,54 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). -record(test_rec,{key,val}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Verify durability", - "Verify that the effects of committed transactions are durable.", - "The content of the tables tables must be restored at startup."]; -all(suite) -> - [ - load_tables, - durability_of_dump_tables, +all() -> + [{group, load_tables}, + {group, durability_of_dump_tables}, durability_of_disc_copies, - durability_of_disc_only_copies - ]. + durability_of_disc_only_copies]. + +groups() -> + [{load_tables, [], + [load_latest_data, load_local_contents_directly, + load_directly_when_all_are_ram_copiesA, + load_directly_when_all_are_ram_copiesB, + {group, late_load_when_all_are_ram_copies_on_ram_nodes}, + load_when_last_replica_becomes_available, + load_when_we_have_down_from_all_other_replica_nodes, + late_load_transforms_into_disc_load, + late_load_leads_to_hanging, + force_load_when_nobody_intents_to_load, + force_load_when_someone_has_decided_to_load, + force_load_when_someone_else_already_has_loaded, + force_load_when_we_has_loaded, + force_load_on_a_non_local_table, + force_load_when_the_table_does_not_exist, + {group, load_tables_with_master_tables}]}, + {late_load_when_all_are_ram_copies_on_ram_nodes, [], + [late_load_when_all_are_ram_copies_on_ram_nodes1, + late_load_when_all_are_ram_copies_on_ram_nodes2]}, + {load_tables_with_master_tables, [], + [master_nodes, starting_master_nodes, + master_on_non_local_tables, + remote_force_load_with_local_master_node]}, + {durability_of_dump_tables, [], + [dump_ram_copies, dump_disc_copies, dump_disc_only]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -load_tables(doc) -> - ["Try to provoke all kinds of table load scenarios."]; -load_tables(suite) -> - [ - load_latest_data, - load_local_contents_directly, - load_directly_when_all_are_ram_copiesA, - load_directly_when_all_are_ram_copiesB, - late_load_when_all_are_ram_copies_on_ram_nodes, - load_when_last_replica_becomes_available, - load_when_we_have_down_from_all_other_replica_nodes, - late_load_transforms_into_disc_load, - late_load_leads_to_hanging, - force_load_when_nobody_intents_to_load, - force_load_when_someone_has_decided_to_load, - force_load_when_someone_else_already_has_loaded, - force_load_when_we_has_loaded, - force_load_on_a_non_local_table, - force_load_when_the_table_does_not_exist, - load_tables_with_master_tables - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% load_latest_data(doc) -> @@ -112,8 +119,8 @@ load_latest_data(Config) when is_list(Config) -> ?match([], mnesia_test_lib:kill_mnesia([N2])), ?match(ok, mnesia:dirty_write(Rec1)), - ?match([], mnesia_test_lib:kill_mnesia([N1])), ?match([], mnesia_test_lib:kill_mnesia([N3])), + ?match([], mnesia_test_lib:kill_mnesia([N1])), ?match([], mnesia_test_lib:start_mnesia([N2], [])), %% Should wait for N1 @@ -284,13 +291,6 @@ load_directly_when_all_are_ram_copiesB(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -late_load_when_all_are_ram_copies_on_ram_nodes(doc) -> - ["Load of ram_copies tables when all replicas resides on disc less nodes"]; -late_load_when_all_are_ram_copies_on_ram_nodes(suite) -> - [ - late_load_when_all_are_ram_copies_on_ram_nodes1, - late_load_when_all_are_ram_copies_on_ram_nodes2 - ]. late_load_when_all_are_ram_copies_on_ram_nodes1(suite) -> []; late_load_when_all_are_ram_copies_on_ram_nodes1(Config) when is_list(Config) -> @@ -916,22 +916,6 @@ force_load_when_the_table_does_not_exist(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -load_tables_with_master_tables(doc) -> - ["Verifies the semantics of different master nodes settings", - "The semantics should be:", - "1. Mnesia downs, Normally decides from where mnesia should load tables", - "2. Master tables (overrides mnesia downs) ", - "3. Force load (overrides Master tables) ", - "--- 1st from active master nodes", - "--- 2nd from active nodes", - "--- 3rd get local copy (if ram create new one)" - ]; - -load_tables_with_master_tables(suite) -> - [master_nodes, - starting_master_nodes, - master_on_non_local_tables, - remote_force_load_with_local_master_node]. -define(SDwrite(Tup), fun() -> mnesia:write(Tup) end). @@ -1156,13 +1140,6 @@ remote_force_load_with_local_master_node(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -durability_of_dump_tables(doc) -> - [ "Verify that all tables contain the correct data when Mnesia", - "is restarted and tables are loaded from disc to recover", - " their previous contents. " ]; -durability_of_dump_tables(suite) -> [dump_ram_copies, - dump_disc_copies, - dump_disc_only]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/mnesia/test/mnesia_evil_backup.erl b/lib/mnesia/test/mnesia_evil_backup.erl index bbbebeb02c..63f4146d98 100644 --- a/lib/mnesia/test/mnesia_evil_backup.erl +++ b/lib/mnesia/test/mnesia_evil_backup.erl @@ -35,31 +35,30 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Checking all the functionality regarding ", - "to the backup and different ", - "kinds of restore and fallback interface"]; - -all(suite) -> - [ - backup, - bad_backup, - global_backup_checkpoint, - restore_tables, - traverse_backup, +all() -> + [backup, bad_backup, global_backup_checkpoint, + {group, restore_tables}, traverse_backup, selective_backup_checkpoint, - incremental_backup_checkpoint, -%% local_backup_checkpoint, - install_fallback, - uninstall_fallback, - local_fallback, - sops_with_checkpoint - ]. + incremental_backup_checkpoint, install_fallback, + uninstall_fallback, local_fallback, + sops_with_checkpoint]. + +groups() -> + [{restore_tables, [], + [restore_errors, restore_clear, restore_keep, + restore_recreate, restore_clear_ram]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + backup(doc) -> ["Checking the interface to the function backup", "We don't check that the backups can be used here", @@ -132,17 +131,6 @@ global_backup_checkpoint(Config) when is_list(Config) -> ?match(ok, file:delete(File2)), ?verify_mnesia(Nodes, []). -restore_tables(doc) -> - ["Tests the interface of restore"]; - -restore_tables(suite) -> - [ - restore_errors, - restore_clear, - restore_keep, - restore_recreate, - restore_clear_ram - ]. restore_errors(suite) -> []; restore_errors(Config) when is_list(Config) -> diff --git a/lib/mnesia/test/mnesia_evil_coverage_test.erl b/lib/mnesia/test/mnesia_evil_coverage_test.erl index 06674d9eef..668eba176f 100644 --- a/lib/mnesia/test/mnesia_evil_coverage_test.erl +++ b/lib/mnesia/test/mnesia_evil_coverage_test.erl @@ -30,44 +30,54 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Evil usage of the API.", - "Invoke all functions in the API and try to cover all legal uses", - "cases as well the illegal dito. This is a complement to the", - "other more explicit test cases."]; -all(suite) -> - [ - system_info, - table_info, - error_description, - db_node_lifecycle, - evil_delete_db_node, - start_and_stop, - checkpoint, - table_lifecycle, - add_copy_conflict, - add_copy_when_going_down, - replica_management, - schema_availability, - local_content, - table_access_modifications, - replica_location, - table_sync, - user_properties, - unsupp_user_props, - record_name, - snmp_access, - iteration, - debug_support, - sorted_ets, +all() -> + [system_info, table_info, error_description, + db_node_lifecycle, evil_delete_db_node, start_and_stop, + checkpoint, table_lifecycle, add_copy_conflict, + add_copy_when_going_down, replica_management, + schema_availability, local_content, + {group, table_access_modifications}, replica_location, + {group, table_sync}, user_properties, unsupp_user_props, + {group, record_name}, {group, snmp_access}, + {group, subscriptions}, {group, iteration}, + {group, debug_support}, sorted_ets, {mnesia_dirty_access_test, all}, {mnesia_trans_access_test, all}, - {mnesia_evil_backup, all} - ]. + {mnesia_evil_backup, all}]. + +groups() -> + [{table_access_modifications, [], + [change_table_access_mode, change_table_load_order, + set_master_nodes, offline_set_master_nodes]}, + {table_sync, [], + [dump_tables, dump_log, wait_for_tables, + force_load_table]}, + {snmp_access, [], + [snmp_open_table, snmp_close_table, snmp_get_next_index, + snmp_get_row, snmp_get_mnesia_key, snmp_update_counter, + snmp_order]}, + {subscriptions, [], + [subscribe_standard, subscribe_extended]}, + {iteration, [], [foldl]}, + {debug_support, [], + [info, schema_0, schema_1, view_0, view_1, view_2, + lkill, kill]}, + {record_name, [], [{group, record_name_dirty_access}]}, + {record_name_dirty_access, [], + [record_name_dirty_access_ram, + record_name_dirty_access_disc, + record_name_dirty_access_disc_only]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -909,13 +919,6 @@ local_content(Config) when is_list(Config) -> ?verify_mnesia(Nodes, []). -table_access_modifications(suite) -> - [ - change_table_access_mode, - change_table_load_order, - set_master_nodes, - offline_set_master_nodes - ]. change_table_access_mode(suite) -> []; change_table_access_mode(Config) when is_list(Config) -> @@ -1102,13 +1105,6 @@ offline_set_master_nodes(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Syncronize table with log or disc %% -table_sync(suite) -> - [ - dump_tables, - dump_log, - wait_for_tables, - force_load_table - ]. %% Dump ram tables on disc dump_tables(suite) -> []; @@ -1358,19 +1354,6 @@ unsupp_user_props(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -snmp_access(doc) -> - ["Make Mnesia table accessible via SNMP"]; - -snmp_access(suite) -> - [ - snmp_open_table, - snmp_close_table, - snmp_get_next_index, - snmp_get_row, - snmp_get_mnesia_key, - snmp_update_counter, - snmp_order - ]. snmp_open_table(suite) -> []; snmp_open_table(Config) when is_list(Config) -> @@ -1775,10 +1758,234 @@ get_keys(Tab, Key) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -iteration(doc) -> - ["Verify that the iteration functions works as expected"]; -iteration(suite) -> - [foldl]. + +-record(tab, {i, e1, e2}). % Simple test table + + +subscribe_extended(doc) -> + ["Test the extended set of events, test with and without checkpoints. "]; +subscribe_extended(suite) -> + []; +subscribe_extended(Config) when is_list(Config) -> + [N1, N2]=Nodes=?acquire_nodes(2, Config), + Tab1 = etab, + Storage = mnesia_test_lib:storage_type(ram_copies, Config), + Def1 = [{Storage, [N1, N2]}, {attributes, record_info(fields, tab)}], + ?match({atomic, ok}, mnesia:create_table(Tab1, Def1)), + + Tab2 = bag, + Def2 = [{Storage, [N1, N2]}, + {type, bag}, + {record_name, Tab1}, + {attributes, record_info(fields, tab)}], + ?match({atomic, ok}, mnesia:create_table(Tab2, Def2)), + + ?match({ok, N1}, mnesia:subscribe({table, Tab1, detailed})), + ?match({ok, N1}, mnesia:subscribe({table, Tab2, detailed})), + + ?match({error, {already_exists, _}}, mnesia:subscribe({table, Tab1, simple})), + ?match({error, {badarg, {table, Tab1, bad}}}, mnesia:subscribe({table, Tab1, bad})), + + ?match({ok, N1}, mnesia:subscribe(activity)), + test_ext_sub(Tab1, Tab2), + + ?match({ok, N1}, mnesia:unsubscribe(activity)), + ?match({ok, N1}, mnesia:subscribe({table, Tab1, detailed})), + ?match({atomic, ok}, mnesia:clear_table(Tab1)), + ?match({mnesia_table_event, {delete, schema, {schema, Tab1}, [{schema, Tab1, _}],_}}, recv_event()), + ?match({mnesia_table_event, {write, schema, {schema, Tab1, _}, [], _}}, recv_event()), + + ?match({atomic, ok}, mnesia_schema:clear_table(Tab2)), + ?match({mnesia_table_event, {delete, schema, {schema, Tab2}, [{schema, Tab2, _}],_}}, + recv_event()), + ?match({mnesia_table_event, {write, schema, {schema, Tab2, _}, [], _}}, recv_event()), + + ?match({ok, N1}, mnesia:unsubscribe({table, Tab2, detailed})), + {ok, _, _} = mnesia:activate_checkpoint([{name, testing}, + {ram_overrides_dump, true}, + {max, [Tab1, Tab2]}]), + ?match({ok, N1}, mnesia:subscribe({table, Tab2, detailed})), + ?match({ok, N1}, mnesia:subscribe(activity)), + test_ext_sub(Tab1, Tab2), + + ?verify_mnesia(Nodes, []). + +test_ext_sub(Tab1, Tab2) -> + %% The basics + Rec1 = {Tab1, 1, 0, 0}, + Rec2 = {Tab1, 1, 1, 0}, + Rec3 = {Tab1, 2, 1, 0}, + Rec4 = {Tab1, 2, 2, 0}, + + Write = fun(Tab, Rec) -> + mnesia:transaction(fun() -> mnesia:write(Tab, Rec, write) + end) + end, + Delete = fun(Tab, Rec) -> + mnesia:transaction(fun() -> mnesia:delete(Tab, Rec, write) + end) + end, + DelObj = fun(Tab, Rec) -> + mnesia:transaction(fun() -> mnesia:delete_object(Tab, Rec, write) + end) + end, + + S = self(), + D = {dirty, self()}, + %% SET + ?match(ok, mnesia:dirty_write(Rec1)), + ?match({mnesia_table_event, {write, Tab1, Rec1, [], D}}, recv_event()), + ?match(ok, mnesia:dirty_write(Rec3)), + ?match({mnesia_table_event, {write, Tab1, Rec3, [], D}}, recv_event()), + ?match(ok, mnesia:dirty_write(Rec1)), + ?match({mnesia_table_event, {write, Tab1, Rec1, [Rec1], D}}, recv_event()), + ?match({atomic, ok}, Write(Tab1, Rec2)), + ?match({mnesia_table_event, {write, Tab1, Rec2, [Rec1], {tid,_,S}}}, recv_event()), + ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()), + ?match(ok, mnesia:dirty_delete({Tab1, 2})), + ?match({mnesia_table_event, {delete, Tab1, {Tab1, 2}, [Rec3], D}}, recv_event()), + ?match({atomic, ok}, DelObj(Tab1, Rec2)), + ?match({mnesia_table_event, {delete, Tab1, Rec2, [Rec2], {tid,_,S}}}, recv_event()), + ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()), + + ?match({atomic, ok}, Delete(Tab1, 1)), + ?match({mnesia_table_event, {delete, Tab1, {Tab1, 1}, [], {tid,_,S}}}, recv_event()), + ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()), + + ?match({ok, _N1}, mnesia:unsubscribe({table, Tab1, detailed})), + + %% BAG + + ?match({atomic, ok}, Write(Tab2, Rec1)), + ?match({mnesia_table_event, {write, Tab2, Rec1, [], {tid,_,S}}}, recv_event()), + ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()), + ?match({atomic, ok}, Write(Tab2, Rec4)), + ?match({mnesia_table_event, {write, Tab2, Rec4, [], {tid,_,S}}}, recv_event()), + ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()), + ?match({atomic, ok}, Write(Tab2, Rec3)), + ?match({mnesia_table_event, {write, Tab2, Rec3, [Rec4], {tid,_,S}}}, recv_event()), + ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()), + ?match({atomic, ok}, Write(Tab2, Rec2)), + ?match({mnesia_table_event, {write, Tab2, Rec2, [Rec1], {tid,_,S}}}, recv_event()), + ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()), + ?match({atomic, ok}, Write(Tab2, Rec1)), + ?match({mnesia_table_event, {write, Tab2, Rec1, [Rec1, Rec2], {tid,_,S}}}, recv_event()), + ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()), + ?match({atomic, ok}, DelObj(Tab2, Rec2)), + ?match({mnesia_table_event, {delete, Tab2, Rec2, [Rec2], {tid,_,S}}}, recv_event()), + ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()), + ?match({atomic, ok}, Delete(Tab2, 1)), + ?match({mnesia_table_event, {delete, Tab2, {Tab2, 1}, [Rec1], {tid,_,S}}}, recv_event()), + ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()), + ?match({atomic, ok}, Delete(Tab2, 2)), + ?match({mnesia_table_event, {delete, Tab2, {Tab2, 2}, [Rec4, Rec3], {tid,_,S}}}, recv_event()), + ?match({mnesia_activity_event, {complete, {tid,_,S}}}, recv_event()), + ok. + + +subscribe_standard(doc) -> + ["Tests system events and the orignal table events"]; +subscribe_standard(suite) -> []; +subscribe_standard(Config) when is_list(Config)-> + [N1, N2]=?acquire_nodes(2, Config), + Tab = tab, + + Storage = mnesia_test_lib:storage_type(disc_copies, Config), + Def = [{Storage, [N1, N2]}, {attributes, record_info(fields, tab)}], + + ?match({atomic, ok}, mnesia:create_table(Tab, Def)), + + %% Check system events + ?match({ok, N1}, mnesia:subscribe(system)), + ?match({ok, N1}, mnesia:subscribe(activity)), + + ?match([], mnesia_test_lib:kill_mnesia([N2])), + ?match({mnesia_system_event, {mnesia_down, N2}}, recv_event()), + ?match(timeout, recv_event()), + + ?match([], mnesia_test_lib:start_mnesia([N2], [Tab])), + ?match({mnesia_activity_event, _}, recv_event()), + ?match({mnesia_system_event,{mnesia_up, N2}}, recv_event()), + + ?match(true, lists:member(self(), mnesia:system_info(subscribers))), + ?match([], mnesia_test_lib:kill_mnesia([N1])), + timer:sleep(500), + mnesia_test_lib:flush(), + ?match([], mnesia_test_lib:start_mnesia([N1], [Tab])), + ?match(timeout, recv_event()), + + ?match({ok, N1}, mnesia:subscribe(system)), + ?match({error, {already_exists, system}}, mnesia:subscribe(system)), + ?match(stopped, mnesia:stop()), + ?match({mnesia_system_event, {mnesia_down, N1}}, recv_event()), + ?match({error, {node_not_running, N1}}, mnesia:subscribe(system)), + ?match([], mnesia_test_lib:start_mnesia([N1, N2], [Tab])), + + %% Check table events + ?match({ok, N1}, mnesia:subscribe(activity)), + Old_Level = mnesia:set_debug_level(trace), + ?match({ok, N1}, mnesia:subscribe({table,Tab})), + + ?match({atomic, ok}, + mnesia:transaction(fun() -> mnesia:write(#tab{i=155}) end)), + Self = self(), + ?match({mnesia_table_event, {write, _, _}}, recv_event()), + ?match({mnesia_activity_event, {complete, {tid, _, Self}}}, recv_event()), + + ?match({ok, N1}, mnesia:unsubscribe({table,Tab})), + ?match({ok, N1}, mnesia:unsubscribe(activity)), + + ?match({atomic, ok}, + mnesia:transaction(fun() -> mnesia:write(#tab{i=255}) end)), + + ?match(timeout, recv_event()), + mnesia:set_debug_level(Old_Level), + + %% Check deletion of replica + + ?match({ok, N1}, mnesia:subscribe({table,Tab})), + ?match({ok, N1}, mnesia:subscribe(activity)), + ?match(ok, mnesia:dirty_write(#tab{i=355})), + ?match({mnesia_table_event, {write, _, _}}, recv_event()), + ?match({atomic, ok}, mnesia:del_table_copy(Tab, N1)), + ?match({mnesia_activity_event, _}, recv_event()), + ?match(ok, mnesia:dirty_write(#tab{i=455})), + ?match(timeout, recv_event()), + + ?match({atomic, ok}, mnesia:move_table_copy(Tab, N2, N1)), + ?match({mnesia_activity_event, _}, recv_event()), + ?match({ok, N1}, mnesia:subscribe({table,Tab})), + ?match(ok, mnesia:dirty_write(#tab{i=555})), + ?match({mnesia_table_event, {write, _, _}}, recv_event()), + ?match({atomic, ok}, mnesia:move_table_copy(Tab, N1, N2)), + ?match({mnesia_activity_event, _}, recv_event()), + ?match(ok, mnesia:dirty_write(#tab{i=655})), + ?match(timeout, recv_event()), + + ?match({atomic, ok}, mnesia:add_table_copy(Tab, N1, ram_copies)), + ?match({mnesia_activity_event, _}, recv_event()), + ?match({ok, N1}, mnesia:subscribe({table,Tab})), + ?match({error, {already_exists, {table,Tab, simple}}}, + mnesia:subscribe({table,Tab})), + ?match(ok, mnesia:dirty_write(#tab{i=755})), + ?match({mnesia_table_event, {write, _, _}}, recv_event()), + + ?match({atomic, ok}, mnesia:delete_table(Tab)), + ?match({mnesia_activity_event, _}, recv_event()), + ?match(timeout, recv_event()), + + mnesia_test_lib:kill_mnesia([N1]), + + ?verify_mnesia([N2], [N1]). + +recv_event() -> + receive + Event -> Event + after 1000 -> + timeout + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% foldl(suite) -> @@ -1840,19 +2047,6 @@ sort_res(Else) -> Else. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -debug_support(doc) -> - ["Check that the debug support has not decayed."]; -debug_support(suite) -> - [ - info, - schema_0, - schema_1, - view_0, - view_1, - view_2, - lkill, - kill - ]. info(suite) -> []; info(Config) when is_list(Config) -> @@ -1939,21 +2133,7 @@ kill(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -record_name(doc) -> - ["Verify that record names may be differ from the name of ", - "the hosting table. Check at least access, restore, " - "registry, subscriptions and traveres_backup"]; -record_name(suite) -> - [ - record_name_dirty_access - ]. - -record_name_dirty_access(suite) -> - [ - record_name_dirty_access_ram, - record_name_dirty_access_disc, - record_name_dirty_access_disc_only - ]. + record_name_dirty_access_ram(suite) -> []; diff --git a/lib/mnesia/test/mnesia_examples_test.erl b/lib/mnesia/test/mnesia_examples_test.erl index d1b1409c9d..373d47a05a 100644 --- a/lib/mnesia/test/mnesia_examples_test.erl +++ b/lib/mnesia/test/mnesia_examples_test.erl @@ -26,8 +26,8 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). -define(init(N, Config), mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]}, @@ -61,16 +61,21 @@ opt_load(Mod) -> end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Run all examples mentioned in the documentation", - "Are really all examples covered?"]; -all(suite) -> - [ - bup, - company, - meter, - tpcb - ]. +all() -> + [bup, company, meter, {group, tpcb}]. + +groups() -> + [{tpcb, [], + [replica_test, sticky_replica_test, dist_test, + conflict_test, frag_test, frag2_test, remote_test, + remote_frag2_test]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% bup(doc) -> ["Run the backup examples in bup.erl"]; @@ -85,19 +90,6 @@ company(doc) -> ["Run the company examples in company.erl and company_o.erl"]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -tpcb(doc) -> - ["Run the sample configurations of the stress tests in mnesia_tpcb.erl"]; -tpcb(suite) -> - [ - replica_test, - sticky_replica_test, - dist_test, - conflict_test, - frag_test, - frag2_test, - remote_test, - remote_frag2_test - ]. replica_test(suite) -> []; replica_test(Config) when is_list(Config) -> diff --git a/lib/mnesia/test/mnesia_frag_test.erl b/lib/mnesia/test/mnesia_frag_test.erl index 4add340254..d3f6762af7 100644 --- a/lib/mnesia/test/mnesia_frag_test.erl +++ b/lib/mnesia/test/mnesia_frag_test.erl @@ -27,8 +27,8 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). -define(match_dist(ExpectedRes, Expr), case ?match(ExpectedRes, Expr) of @@ -37,34 +37,29 @@ fin_per_testcase(Func, Conf) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Verify the functionality of fragmented tables"]; -all(suite) -> - [ - light, - medium - ]. - -light(suite) -> - [ - nice, - evil - ]. - -medium(suite) -> - [ - consistency - ]. +all() -> + [{group, light}, {group, medium}]. + +groups() -> + [{light, [], [{group, nice}, {group, evil}]}, + {medium, [], [consistency]}, + {nice, [], + [nice_single, nice_multi, nice_access, iter_access]}, + {evil, [], + [evil_create, evil_delete, evil_change, evil_combine, + evil_loop, evil_delete_db_node]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -nice(suite) -> - [ - nice_single, - nice_multi, - nice_access, - iter_access - ]. nice_single(suite) -> []; nice_single(Config) when is_list(Config) -> @@ -503,17 +498,6 @@ consistency(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -evil(doc) -> - ["Evil coverage of fragmentation API."]; -evil(suite) -> - [ - evil_create, - evil_delete, - evil_change, - evil_combine, - evil_loop, - evil_delete_db_node - ]. evil_create(suite) -> []; evil_create(Config) when is_list(Config) -> diff --git a/lib/mnesia/test/mnesia_inconsistent_database_test.erl b/lib/mnesia/test/mnesia_inconsistent_database_test.erl index b19cd8e01b..c4b6257d5b 100644 --- a/lib/mnesia/test/mnesia_inconsistent_database_test.erl +++ b/lib/mnesia/test/mnesia_inconsistent_database_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/mnesia/test/mnesia_install_test.erl b/lib/mnesia/test/mnesia_install_test.erl index 42a2a19f37..5d55fcac0e 100644 --- a/lib/mnesia/test/mnesia_install_test.erl +++ b/lib/mnesia/test/mnesia_install_test.erl @@ -27,29 +27,22 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Run some small but demanding test cases in order to verify", - "that the basic functionality in Mnesia still works.", - "", - "Try some very simple things to begin with and increase the", - "difficulty stepwise. This test suite should be run before", - "all the others if you expect to find bugs.", - "", - "The function mnesia_install_test:silly() does not use the whole", - "infra structure of the test suite. Invoke it on a single node to", - "begin with. If that works, proceed with pong = net_adm:ping(SomeOtherNode)", - "and rerun silly() in order to perform some distributed tests."]; -all(suite) -> - [ - silly_durability, - silly_move, - silly_upgrade - %,stress - ]. +all() -> + [silly_durability, silly_move, silly_upgrade]. + +groups() -> + [{stress, [], stress_cases()}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Stepwise of more and more advanced features @@ -86,11 +79,11 @@ silly2(Config) when is_list(Config) -> [schema])), MoveRes = silly_move(Config), UpgradeRes = silly_upgrade(Config), - StressRes = [StressFun(F) || F <- stress(suite)], + StressRes = [StressFun(F) || F <- stress_cases()], ?verify_mnesia([Node2], []), [Res, MoveRes, UpgradeRes] ++ StressRes; _ -> - StressRes = [StressFun(F) || F <- stress(suite)], + StressRes = [StressFun(F) || F <- stress_cases()], ?warning("Too few nodes. Perform net_adm:ping(OtherNode) " "and rerun!!!~n", []), [Res | StressRes] @@ -286,13 +279,9 @@ transform_some_records(Tab1, _Tab2, Old) -> lists:sort(lists:zf(Filter, Old)). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -stress(doc) -> - ["Stress the system a little"]; -stress(suite) -> - [ - conflict, - dist - ]. + +stress_cases() -> +[conflict, dist]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dist(doc) -> diff --git a/lib/mnesia/test/mnesia_isolation_test.erl b/lib/mnesia/test/mnesia_isolation_test.erl index 4fc6e8fe58..3273bc4d40 100644 --- a/lib/mnesia/test/mnesia_isolation_test.erl +++ b/lib/mnesia/test/mnesia_isolation_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -27,46 +27,53 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Verify the isolation property.", - "Operations of concurrent transactions must yield results which", - "are indistinguishable from the results which would be obtained by", - "forcing each transaction to be serially executed to completion in", - "some order. This means that repeated reads of the same records", - "within any committed transaction must have returned identical", - "data when run concurrently with any mix of arbitary transactions.", - "Updates in one transaction must not be visible in any other", - "transaction before the transaction has been committed."]; -all(suite) -> - [ - locking, - visibility - ]. +all() -> + [{group, locking}, {group, visibility}]. + +groups() -> + [{locking, [], + [no_conflict, simple_queue_conflict, + advanced_queue_conflict, simple_deadlock_conflict, + advanced_deadlock_conflict, lock_burst, + {group, sticky_locks}, {group, unbound_locking}, + {group, admin_conflict}, nasty]}, + {sticky_locks, [], [basic_sticky_functionality]}, + {unbound_locking, [], [unbound1, unbound2]}, + {admin_conflict, [], + [create_table, delete_table, move_table_copy, + add_table_index, del_table_index, transform_table, + snmp_open_table, snmp_close_table, + change_table_copy_type, change_table_access, + add_table_copy, del_table_copy, dump_tables, + {group, extra_admin_tests}]}, + {extra_admin_tests, [], + [del_table_copy_1, del_table_copy_2, del_table_copy_3, + add_table_copy_1, add_table_copy_2, add_table_copy_3, + add_table_copy_4, move_table_copy_1, move_table_copy_2, + move_table_copy_3, move_table_copy_4]}, + {visibility, [], + [dirty_updates_visible_direct, + dirty_reads_regardless_of_trans, + trans_update_invisibible_outside_trans, + trans_update_visible_inside_trans, write_shadows, + delete_shadows, write_delete_shadows_bag, + write_delete_shadows_bag2, {group, iteration}, + shadow_search, snmp_shadows]}, + {removed_resources, [], [rr_kill_copy]}, + {iteration, [], [foldl, first_next]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -locking(doc) -> - ["Verify locking semantics for various configurations", - " NoLock = lock_funs(no_lock, any_granularity)", - " SharedLock = lock_funs(shared_lock, any_granularity)", - " ExclusiveLock = lock_funs(exclusive_lock, any_granularity)", - " AnyLock = lock_funs(any_lock, any_granularity)"]; -locking(suite) -> - [no_conflict, - simple_queue_conflict, - advanced_queue_conflict, - simple_deadlock_conflict, - advanced_deadlock_conflict, - lock_burst, - sticky_locks, - unbound_locking, - admin_conflict, -%% removed_resources, - nasty - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -431,14 +438,6 @@ burst_incr(Tab, Father) -> Father ! burst_incr_done. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -sticky_locks(doc) -> - ["Simple Tests of sticky locks"]; - -sticky_locks(suite) -> - [ - basic_sticky_functionality - %% Needs to be expandand a little bit further - ]. basic_sticky_functionality(suite) -> []; basic_sticky_functionality(Config) when is_list(Config) -> @@ -519,12 +518,6 @@ get_held() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -unbound_locking(suite) -> - [unbound1, unbound2]; - -unbound_locking(doc) -> - ["Check that mnesia handles unbound key variables, GPRS bug." - "Ticket id: OTP-3342"]. unbound1(suite) -> []; unbound1(Config) when is_list(Config) -> @@ -637,25 +630,6 @@ receiver() -> end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -admin_conflict(doc) -> - ["Provoke lock conflicts with schema transactions and checkpoints."]; -admin_conflict(suite) -> - [ - create_table, - delete_table, - move_table_copy, - add_table_index, - del_table_index, - transform_table, - snmp_open_table, - snmp_close_table, - change_table_copy_type, - change_table_access, - add_table_copy, - del_table_copy, - dump_tables, - extra_admin_tests - ]. create_table(suite) -> []; create_table(Config) when is_list(Config) -> @@ -1088,18 +1062,6 @@ insert(Tab, N) when N > 0 -> ok = mnesia:sync_dirty(fun() -> mnesia:write({Tab, N, N, 0}) end), insert(Tab, N-1). -extra_admin_tests(suite) -> - [del_table_copy_1, - del_table_copy_2, - del_table_copy_3, - add_table_copy_1, - add_table_copy_2, - add_table_copy_3, - add_table_copy_4, - move_table_copy_1, - move_table_copy_2, - move_table_copy_3, - move_table_copy_4]. update_own(Tab, Key, Acc) -> Update = @@ -1347,23 +1309,6 @@ move_table(CallFrom, FromNode, ToNode, [Node1, Node2, Node3], Def) -> ?verify_mnesia([Node1, Node2, Node3], []). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -visibility(doc) -> - ["Verify the visibility semantics for various configurations"]; -visibility(suite) -> - [ - dirty_updates_visible_direct, - dirty_reads_regardless_of_trans, - trans_update_invisibible_outside_trans, - trans_update_visible_inside_trans, - write_shadows, - delete_shadows, -%% delete_shadows2, - write_delete_shadows_bag, - write_delete_shadows_bag2, - iteration, - shadow_search, - snmp_shadows - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% dirty_updates_visible_direct(doc) -> @@ -1969,10 +1914,6 @@ shadow_search(Config) when is_list(Config) -> ?verify_mnesia([Node1], []). -removed_resources(suite) -> - [rr_kill_copy]; -removed_resources(doc) -> - ["Verify that the locking behave when resources are removed"]. rr_kill_copy(suite) -> []; rr_kill_copy(Config) when is_list(Config) -> @@ -2138,11 +2079,6 @@ get_exit(Pid) -> ?error("Timeout EXIT ~p~n", [Pid]) end. -iteration(doc) -> - ["Verify that the updates before/during iteration are visable " - "and that the order is preserved for ordered_set tables"]; -iteration(suite) -> - [foldl,first_next]. foldl(doc) -> [""]; diff --git a/lib/mnesia/test/mnesia_majority_test.erl b/lib/mnesia/test/mnesia_majority_test.erl new file mode 100644 index 0000000000..41ba0fd601 --- /dev/null +++ b/lib/mnesia/test/mnesia_majority_test.erl @@ -0,0 +1,186 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%% +-module(mnesia_majority_test). +-author('[email protected]'). +-compile(export_all). +-include("mnesia_test_lib.hrl"). + +init_per_testcase(Func, Conf) -> + mnesia_test_lib:init_per_testcase(Func, Conf). + +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +all() -> + [ + write + , wread + , delete + , clear_table + , frag + , change_majority + , frag_change_majority + ]. + +write(suite) -> []; +write(Config) when is_list(Config) -> + [N1, N2, N3] = ?acquire_nodes(3, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2,N3]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok,ok],[]}, + rpc:multicall([N1,N2,N3], mnesia, wait_for_tables, [[Tab], 3000])), + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + mnesia_test_lib:kill_mnesia([N3]), + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)). + +wread(suite) -> []; +wread(Config) when is_list(Config) -> + [N1, N2] = ?acquire_nodes(2, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok],[]}, + rpc:multicall([N1,N2], mnesia, wait_for_tables, [[Tab], 3000])), + ?match({atomic,[]}, + mnesia:transaction(fun() -> mnesia:read(t,1,write) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, + mnesia:transaction(fun() -> mnesia:read(t,1,write) end)). + +delete(suite) -> []; +delete(Config) when is_list(Config) -> + [N1, N2] = ?acquire_nodes(2, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok],[]}, + rpc:multicall([N1,N2], mnesia, wait_for_tables, [[Tab], 3000])), + %% works as expected with majority of nodes present + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:delete({t,1}) end)), + ?match({atomic,[]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + %% put the record back + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,[{t,1,a}]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, + mnesia:transaction(fun() -> mnesia:delete({t,1}) end)). + +clear_table(suite) -> []; +clear_table(Config) when is_list(Config) -> + [N1, N2] = ?acquire_nodes(2, Config), + Tab = t, + Schema = [{name, Tab}, {ram_copies, [N1,N2]}, {majority,true}], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match({[ok,ok],[]}, + rpc:multicall([N1,N2], mnesia, wait_for_tables, [[Tab], 3000])), + %% works as expected with majority of nodes present + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,ok}, mnesia:clear_table(t)), + ?match({atomic,[]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + %% put the record back + ?match({atomic,ok}, + mnesia:transaction(fun() -> mnesia:write({t,1,a}) end)), + ?match({atomic,[{t,1,a}]}, + mnesia:transaction(fun() -> mnesia:read({t,1}) end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({aborted,{no_majority,Tab}}, mnesia:clear_table(t)). + +frag(suite) -> []; +frag(Config) when is_list(Config) -> + [N1] = ?acquire_nodes(1, Config), + Tab = t, + Schema = [ + {name, Tab}, {ram_copies, [N1]}, + {majority,true}, + {frag_properties, [{n_fragments, 2}]} + ], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match(true, mnesia:table_info(t, majority)), + ?match(true, mnesia:table_info(t_frag2, majority)). + +change_majority(suite) -> []; +change_majority(Config) when is_list(Config) -> + [N1,N2] = ?acquire_nodes(2, Config), + Tab = t, + Schema = [ + {name, Tab}, {ram_copies, [N1,N2]}, + {majority,false} + ], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match(false, mnesia:table_info(t, majority)), + ?match({atomic, ok}, + mnesia:change_table_majority(t, true)), + ?match(true, mnesia:table_info(t, majority)), + ?match(ok, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({'EXIT',{aborted,{no_majority,_}}}, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end)). + +frag_change_majority(suite) -> []; +frag_change_majority(Config) when is_list(Config) -> + [N1,N2] = ?acquire_nodes(2, Config), + Tab = t, + Schema = [ + {name, Tab}, {ram_copies, [N1,N2]}, + {majority,false}, + {frag_properties, + [{n_fragments, 2}, + {n_ram_copies, 2}, + {node_pool, [N1,N2]}]} + ], + ?match({atomic, ok}, mnesia:create_table(Schema)), + ?match(false, mnesia:table_info(t, majority)), + ?match(false, mnesia:table_info(t_frag2, majority)), + ?match({aborted,{bad_type,t_frag2}}, + mnesia:change_table_majority(t_frag2, true)), + ?match({atomic, ok}, + mnesia:change_table_majority(t, true)), + ?match(true, mnesia:table_info(t, majority)), + ?match(true, mnesia:table_info(t_frag2, majority)), + ?match(ok, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end, mnesia_frag)), + mnesia_test_lib:kill_mnesia([N2]), + ?match({'EXIT',{aborted,{no_majority,_}}}, + mnesia:activity(transaction, fun() -> + mnesia:write({t,1,a}) + end, mnesia_frag)). diff --git a/lib/mnesia/test/mnesia_measure_test.erl b/lib/mnesia/test/mnesia_measure_test.erl index fbf804dbec..e63689d83a 100644 --- a/lib/mnesia/test/mnesia_measure_test.erl +++ b/lib/mnesia/test/mnesia_measure_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -27,8 +27,8 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). -define(init(N, Config), mnesia_test_lib:prepare_test_case([{init_test_case, [mnesia]}, @@ -37,101 +37,62 @@ fin_per_testcase(Func, Conf) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Measure various aspects of Mnesia", - "Verify that Mnesia has predictable response times,", - "that the transaction system has fair algoritms,", - "resource consumption, scalabilitym system limits etc.", - "Perform some benchmarks."]; -all(suite) -> - [ - prediction, - consumption, - scalability, - benchmarks - ]. +all() -> + [{group, prediction}, {group, consumption}, + {group, scalability}, {group, benchmarks}]. + +groups() -> + [{prediction, [], + [reader_disturbed_by_node_down, + writer_disturbed_by_node_down, + reader_disturbed_by_node_up, + writer_disturbed_by_node_up, + reader_disturbed_by_schema_ops, + writer_disturbed_by_schema_ops, + reader_disturbed_by_checkpoint, + writer_disturbed_by_checkpoint, + reader_disturbed_by_dump_log, + writer_disturbed_by_dump_log, + reader_disturbed_by_backup, writer_disturbed_by_backup, + reader_disturbed_by_restore, + writer_disturbed_by_restore, {group, fairness}]}, + {fairness, [], + [reader_competing_with_reader, + reader_competing_with_writer, + writer_competing_with_reader, + writer_competing_with_writer]}, + {consumption, [], + [measure_resource_consumption, + determine_resource_leakage]}, + {scalability, [], + [determine_system_limits, performance_at_min_config, + performance_at_max_config, performance_at_full_load, + resource_consumption_at_min_config, + resource_consumption_at_max_config, + resource_consumption_at_full_load]}, + {benchmarks, [], + [{group, meter}, cost, dbn_meters, + measure_all_api_functions, {group, tpcb}, + mnemosyne_vs_mnesia_kernel]}, + {tpcb, [], [ram_tpcb, disc_tpcb, disc_only_tpcb]}, + {meter, [], [ram_meter, disc_meter, disc_only_meter]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -prediction(doc) -> - ["The system must have predictable response times.", - "The maintenance of the system should not impact on the", - "availability. Make sure that the response times does not vary too", - "much from the undisturbed normal usage.", - "Verify that deadlocks never occurs."]; -prediction(suite) -> - [ - reader_disturbed_by_node_down, - writer_disturbed_by_node_down, - reader_disturbed_by_node_up, - writer_disturbed_by_node_up, - reader_disturbed_by_schema_ops, - writer_disturbed_by_schema_ops, - reader_disturbed_by_checkpoint, - writer_disturbed_by_checkpoint, - reader_disturbed_by_dump_log, - writer_disturbed_by_dump_log, - reader_disturbed_by_backup, - writer_disturbed_by_backup, - reader_disturbed_by_restore, - writer_disturbed_by_restore, - fairness - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -fairness(doc) -> - ["Verify that the transaction system behaves fair, even under intense", - "stress. Combine different access patterns (transaction profiles)", - "in order to verify that concurrent applications gets a fair share", - "of the database resource. Verify that starvation never may occur."]; -fairness(suite) -> - [ - reader_competing_with_reader, - reader_competing_with_writer, - writer_competing_with_reader, - writer_competing_with_writer - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -consumption(doc) -> - ["Measure the resource consumption and publish the outcome. Make", - "sure that resources are released after failures."]; -consumption(suite) -> - [ - measure_resource_consumption, - determine_resource_leakage - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -scalability(doc) -> - ["Try out where the system limits are. We must at least meet the", - "documented system limits.", - "Redo the performance meters for various configurations and load,", - "especially near system limits."]; -scalability(suite) -> - [ - determine_system_limits, - performance_at_min_config, - performance_at_max_config, - performance_at_full_load, - resource_consumption_at_min_config, - resource_consumption_at_max_config, - resource_consumption_at_full_load - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -benchmarks(doc) -> - ["Measure typical database operations and publish them. Try to", - "verify that new releases of Mnesia always outperforms old", - "releases, or at least that the meters does not get worse."]; -benchmarks(suite) -> - [ - meter, - cost, - dbn_meters, - measure_all_api_functions, - tpcb, - mnemosyne_vs_mnesia_kernel - ]. dbn_meters(suite) -> []; dbn_meters(Config) when is_list(Config) -> @@ -139,12 +100,6 @@ dbn_meters(Config) when is_list(Config) -> ?match(ok, mnesia_dbn_meters:start()), ok. -tpcb(suite) -> - [ - ram_tpcb, - disc_tpcb, - disc_only_tpcb - ]. tpcb(ReplicaType, Config) -> HarakiriDelay = {tc_timeout, timer:minutes(20)}, @@ -171,12 +126,6 @@ disc_only_tpcb(suite) -> []; disc_only_tpcb(Config) when is_list(Config) -> tpcb(disc_only_copies, Config). -meter(suite) -> - [ - ram_meter, - disc_meter, - disc_only_meter - ]. ram_meter(suite) -> []; ram_meter(Config) when is_list(Config) -> diff --git a/lib/mnesia/test/mnesia_nice_coverage_test.erl b/lib/mnesia/test/mnesia_nice_coverage_test.erl index aa9339f6b9..78eab67b11 100644 --- a/lib/mnesia/test/mnesia_nice_coverage_test.erl +++ b/lib/mnesia/test/mnesia_nice_coverage_test.erl @@ -28,16 +28,22 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Test nice usage of the entire API", - "Invoke all functions in the API, at least once.", - "Try to verify that all functions exists and that they perform", - "reasonable things when used in the most simple way."]; -all(suite) -> [nice]. +all() -> + [nice]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + nice(doc) -> [""]; nice(suite) -> []; diff --git a/lib/mnesia/test/mnesia_qlc_test.erl b/lib/mnesia/test/mnesia_qlc_test.erl index 1e4f776c7d..5f46840ae9 100644 --- a/lib/mnesia/test/mnesia_qlc_test.erl +++ b/lib/mnesia/test/mnesia_qlc_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -22,7 +22,7 @@ -compile(export_all). --export([all/1]). +-export([all/0,groups/0,init_per_group/2,end_per_group/2]). -include("mnesia_test_lib.hrl"). -include_lib("stdlib/include/qlc.hrl"). @@ -31,20 +31,34 @@ init_per_testcase(Func, Conf) -> setup(Conf), mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). -all(doc) -> - ["Test that the qlc mnesia interface works as expected."]; -all(suite) -> +all() -> case code:which(qlc) of non_existing -> []; - _ -> - all_qlc() + _ -> all_qlc() end. -all_qlc() -> - [dirty, trans, frag, info, mnesia_down]. +groups() -> + [{dirty, [], + [dirty_nice_ram_copies, dirty_nice_disc_copies, + dirty_nice_disc_only_copies]}, + {trans, [], + [trans_nice_ram_copies, trans_nice_disc_copies, + trans_nice_disc_only_copies, {group, atomic}]}, + {atomic, [], [atomic_eval]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +all_qlc() -> + [{group, dirty}, {group, trans}, frag, info, + mnesia_down]. init_testcases(Type,Config) -> Nodes = [N1,N2] = ?acquire_nodes(2, Config), @@ -56,13 +70,10 @@ init_testcases(Type,Config) -> end, All = fun() -> [Write(Id) || Id <- lists:seq(1,10)], ok end, ?match({atomic, ok}, mnesia:sync_transaction(All)), + ?match({atomic, [{b, {b,100-1}, 1}]}, mnesia:transaction(fun() -> mnesia:read({b, {b, 99}}) end)), Nodes. %% Test cases -dirty(suite) -> - [dirty_nice_ram_copies, - dirty_nice_disc_copies, - dirty_nice_disc_only_copies]. dirty_nice_ram_copies(Setup) -> dirty_nice(Setup,ram_copies). dirty_nice_disc_copies(Setup) -> dirty_nice(Setup,disc_copies). @@ -109,12 +120,6 @@ dirty_nice(Config, Type) when is_list(Config) -> end, ?verify_mnesia(Ns, []). -trans(suite) -> - [trans_nice_ram_copies, - trans_nice_disc_copies, - trans_nice_disc_only_copies, - atomic - ]. trans_nice_ram_copies(Setup) -> trans_nice(Setup,ram_copies). trans_nice_disc_copies(Setup) -> trans_nice(Setup,disc_copies). @@ -182,9 +187,7 @@ recs() -> "-record(b, {k,v}). " "-record(k, {t,v}). " >>. - -atomic(suite) -> [atomic_eval]; -atomic(doc) -> []. + atomic_eval(suite) -> []; atomic_eval(doc) -> []; diff --git a/lib/mnesia/test/mnesia_recovery_test.erl b/lib/mnesia/test/mnesia_recovery_test.erl index f6ecf2ce2e..625e6e824c 100644 --- a/lib/mnesia/test/mnesia_recovery_test.erl +++ b/lib/mnesia/test/mnesia_recovery_test.erl @@ -28,8 +28,8 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). -define(receive_messages(Msgs), receive_messages(Msgs, ?FILE, ?LINE)). @@ -42,34 +42,93 @@ fin_per_testcase(Func, Conf) -> -endif. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Verify recoverability", - "Verify that the effects of committed transactions are preserved", - "after recovery from system failures. It must be possible to", - "restore the tables to a consistent state on a node, from (any kind", - "of) replica on other nodes as well as from local disk on the failed", - "node. The system must also recover from instantaneous", - "interruption causing disk files to not be completely synchronized."]; - -all(suite) -> - [ - mnesia_down, - explicit_stop, - coord_dies, - schema_trans, - async_dirty, - sync_dirty, - sym_trans, - asym_trans, - after_full_disc_partition, - after_corrupt_files, - disc_less, - garb_decision, - system_upgrade - ]. - -schema_trans(suite) -> - [{mnesia_schema_recovery_test, all}]. +all() -> + [{group, mnesia_down}, {group, explicit_stop}, + coord_dies, {group, schema_trans}, {group, async_dirty}, + {group, sync_dirty}, {group, sym_trans}, + {group, asym_trans}, after_full_disc_partition, + {group, after_corrupt_files}, disc_less, garb_decision, + system_upgrade]. + +groups() -> + [{schema_trans, [], + [{mnesia_schema_recovery_test, all}]}, + {mnesia_down, [], + [{group, mnesia_down_during_startup}, + {group, master_node_tests}, {group, read_during_down}, + {group, with_checkpoint}, delete_during_start]}, + {master_node_tests, [], + [no_master_2, no_master_3, one_master_2, one_master_3, + two_master_2, two_master_3, all_master_2, + all_master_3]}, + {read_during_down, [], + [dirty_read_during_down, trans_read_during_down]}, + {mnesia_down_during_startup, [], + [mnesia_down_during_startup_disk_ram, + mnesia_down_during_startup_init_ram, + mnesia_down_during_startup_init_disc, + mnesia_down_during_startup_init_disc_only, + mnesia_down_during_startup_tm_ram, + mnesia_down_during_startup_tm_disc, + mnesia_down_during_startup_tm_disc_only]}, + {with_checkpoint, [], + [with_checkpoint_same, with_checkpoint_other]}, + {explicit_stop, [], [explicit_stop_during_snmp]}, + {sym_trans, [], + [sym_trans_before_commit_kill_coord_node, + sym_trans_before_commit_kill_coord_pid, + sym_trans_before_commit_kill_part_after_ask, + sym_trans_before_commit_kill_part_before_ask, + sym_trans_after_commit_kill_coord_node, + sym_trans_after_commit_kill_coord_pid, + sym_trans_after_commit_kill_part_after_ask, + sym_trans_after_commit_kill_part_do_commit_pre, + sym_trans_after_commit_kill_part_do_commit_post]}, + {sync_dirty, [], + [sync_dirty_pre_kill_part, + sync_dirty_pre_kill_coord_node, + sync_dirty_pre_kill_coord_pid, + sync_dirty_post_kill_part, + sync_dirty_post_kill_coord_node, + sync_dirty_post_kill_coord_pid]}, + {async_dirty, [], + [async_dirty_pre_kill_part, + async_dirty_pre_kill_coord_node, + async_dirty_pre_kill_coord_pid, + async_dirty_post_kill_part, + async_dirty_post_kill_coord_node, + async_dirty_post_kill_coord_pid]}, + {asym_trans, [], + [asym_trans_kill_part_ask, + asym_trans_kill_part_commit_vote, + asym_trans_kill_part_pre_commit, + asym_trans_kill_part_log_commit, + asym_trans_kill_part_do_commit, + asym_trans_kill_coord_got_votes, + asym_trans_kill_coord_pid_got_votes, + asym_trans_kill_coord_log_commit_rec, + asym_trans_kill_coord_pid_log_commit_rec, + asym_trans_kill_coord_log_commit_dec, + asym_trans_kill_coord_pid_log_commit_dec, + asym_trans_kill_coord_rec_acc_pre_commit_log_commit, + asym_trans_kill_coord_pid_rec_acc_pre_commit_log_commit, + asym_trans_kill_coord_rec_acc_pre_commit_done_commit, + asym_trans_kill_coord_pid_rec_acc_pre_commit_done_commit]}, + {after_corrupt_files, [], + [after_corrupt_files_decision_log_head, + after_corrupt_files_decision_log_tail, + after_corrupt_files_latest_log_head, + after_corrupt_files_latest_log_tail, + after_corrupt_files_table_dat_head, + after_corrupt_files_table_dat_tail, + after_corrupt_files_schema_dat_head, + after_corrupt_files_schema_dat_tail]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. tpcb_config(ReplicaType, _NodeConfig, Nodes) -> [{n_branches, 5}, @@ -83,30 +142,7 @@ tpcb_config(ReplicaType, _NodeConfig, Nodes) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -mnesia_down(doc) -> - [" Various tests about recovery when mnesia goes down on one or several nodes."]; -mnesia_down(suite) -> - [ - mnesia_down_during_startup, - master_node_tests, - read_during_down, - with_checkpoint, - delete_during_start - ]. - -master_node_tests(doc) -> - ["Verify that mnesia loads the correct data after it has been down, regarding master node settings."]; -master_node_tests(suite) -> - [ - no_master_2, - no_master_3, - one_master_2, - one_master_3, - two_master_2, - two_master_3, - all_master_2, - all_master_3 - ]. + no_master_2(suite) -> []; no_master_2(Config) when is_list(Config) -> mnesia_down_2(no, Config). @@ -251,13 +287,6 @@ mnesia_down_3(Masters, Config) -> ?verify_mnesia(Nodes, []). -read_during_down(doc) -> - ["Verify that read operation can continue to read when mnesia goes down"]; -read_during_down(suite) -> - [ - dirty_read_during_down, - trans_read_during_down - ]. dirty_read_during_down(suite) -> []; @@ -325,20 +354,6 @@ loop_and_kill_mnesia(N, Node, Tabs) -> timer:sleep(100), loop_and_kill_mnesia(N-1, KN, Tabs). -mnesia_down_during_startup(doc) -> - ["Verify that mnesia can come back up again in a consistent state", - "after it has gone down during startup (with different store and", - "when it goes down in different situations"]; -mnesia_down_during_startup(suite) -> - [ - mnesia_down_during_startup_disk_ram, - mnesia_down_during_startup_init_ram, - mnesia_down_during_startup_init_disc, - mnesia_down_during_startup_init_disc_only, - mnesia_down_during_startup_tm_ram, - mnesia_down_during_startup_tm_disc, - mnesia_down_during_startup_tm_disc_only - ]. mnesia_down_during_startup_disk_ram(suite) -> []; mnesia_down_during_startup_disk_ram(Config) when is_list(Config)-> @@ -433,10 +448,6 @@ mnesia_down_during_startup2(Config, ReplicaType, Debug_Point, _Father) -> ?verify_mnesia(Nodes, []). -with_checkpoint(doc) -> - ["Restart mnesia with checkpoint"]; -with_checkpoint(suite) -> - [with_checkpoint_same, with_checkpoint_other]. with_checkpoint_same(suite) -> []; with_checkpoint_same(Config) when is_list(Config) -> @@ -581,10 +592,6 @@ verify_where2read([]) -> ok. %%------------------------------------------------------------------------------------------- -explicit_stop(doc) -> - ["Stop Mnesia in different situations"]; -explicit_stop(suite) -> - [explicit_stop_during_snmp]. %% This is a bad implementation, but at least gives a indication if something is wrong explicit_stop_during_snmp(suite) -> []; explicit_stop_during_snmp(Config) when is_list(Config) -> @@ -700,21 +707,7 @@ coord_dies(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -sym_trans(doc) -> - ["Recovery of symmetrical transactions in a couple of different", - "situations; when coordinator or participant or node dies"]; - -sym_trans(suite) -> - [sym_trans_before_commit_kill_coord_node, %% coordinator node dies - sym_trans_before_commit_kill_coord_pid, %% coordinator process dies - sym_trans_before_commit_kill_part_after_ask, %% participating node dies - sym_trans_before_commit_kill_part_before_ask, - sym_trans_after_commit_kill_coord_node, - sym_trans_after_commit_kill_coord_pid, - sym_trans_after_commit_kill_part_after_ask, - sym_trans_after_commit_kill_part_do_commit_pre, - sym_trans_after_commit_kill_part_do_commit_post]. + %kill_after_debug_point(Config, TestCase, {Debug_node, Debug_Point}, TransFun, Tab) @@ -828,17 +821,6 @@ do_sym_trans([Tab], _Fahter) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -sync_dirty(doc) -> - ["Verify recovery of synchronously operations in a couple of different", - "situations"]; -sync_dirty(suite) -> - [sync_dirty_pre_kill_part, - sync_dirty_pre_kill_coord_node, - sync_dirty_pre_kill_coord_pid, - sync_dirty_post_kill_part, - sync_dirty_post_kill_coord_node, - sync_dirty_post_kill_coord_pid - ]. sync_dirty_pre_kill_part(suite) -> []; sync_dirty_pre_kill_part(Config) when is_list(Config) -> @@ -916,16 +898,6 @@ do_sync_dirty([Tab], _Father) -> ?dl("SYNC_DIRTY done: ~p ", [Res]), ok. -async_dirty(doc) -> - ["Verify recovery of asynchronously dirty operations in a couple of different", - "situations"]; -async_dirty(suite) -> - [async_dirty_pre_kill_part, - async_dirty_pre_kill_coord_node, - async_dirty_pre_kill_coord_pid, - async_dirty_post_kill_part, - async_dirty_post_kill_coord_node, - async_dirty_post_kill_coord_pid]. async_dirty_pre_kill_part(suite) -> []; async_dirty_pre_kill_part(Config) when is_list(Config) -> @@ -1005,29 +977,6 @@ do_async_dirty([Tab], _Fahter) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -asym_trans(doc) -> - ["Recovery of asymmetrical transactions in a couple of different", - "situations, currently the error cases are not covered, i.e. ", - "not tested are the situations when we kill mnesia or a process", - "during a recovery"]; -asym_trans(suite) -> - [ - asym_trans_kill_part_ask, - asym_trans_kill_part_commit_vote, - asym_trans_kill_part_pre_commit, - asym_trans_kill_part_log_commit, - asym_trans_kill_part_do_commit, - asym_trans_kill_coord_got_votes, - asym_trans_kill_coord_pid_got_votes, - asym_trans_kill_coord_log_commit_rec, - asym_trans_kill_coord_pid_log_commit_rec, - asym_trans_kill_coord_log_commit_dec, - asym_trans_kill_coord_pid_log_commit_dec, - asym_trans_kill_coord_rec_acc_pre_commit_log_commit, - asym_trans_kill_coord_pid_rec_acc_pre_commit_log_commit, - asym_trans_kill_coord_rec_acc_pre_commit_done_commit, - asym_trans_kill_coord_pid_rec_acc_pre_commit_done_commit - ]. asym_trans_kill_part_ask(suite) -> []; asym_trans_kill_part_ask(Config) when is_list(Config) -> @@ -1435,18 +1384,6 @@ after_full_disc_partition(doc) -> %% interrupted_fallback_start %% is implemented in consistency interupted_install_fallback! %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -after_corrupt_files(doc) -> - ["Verify that mnesia (and dets) can handle corrupt files"]; -after_corrupt_files(suite) -> % cope with unsynced disks - [after_corrupt_files_decision_log_head, - after_corrupt_files_decision_log_tail, - after_corrupt_files_latest_log_head, - after_corrupt_files_latest_log_tail, - after_corrupt_files_table_dat_head, - after_corrupt_files_table_dat_tail, - after_corrupt_files_schema_dat_head, - after_corrupt_files_schema_dat_tail - ]. after_corrupt_files_decision_log_head(suite) -> []; after_corrupt_files_decision_log_head(Config) when is_list(Config) -> diff --git a/lib/mnesia/test/mnesia_registry_test.erl b/lib/mnesia/test/mnesia_registry_test.erl index 2305ef93b7..cf8da38632 100644 --- a/lib/mnesia/test/mnesia_registry_test.erl +++ b/lib/mnesia/test/mnesia_registry_test.erl @@ -26,17 +26,22 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Test the mnesia_registry module"]; -all(suite) -> - [ - good_dump, - bad_dump - ]. +all() -> + [good_dump, bad_dump]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% good_dump(doc) -> diff --git a/lib/mnesia/test/mnesia_schema_recovery_test.erl b/lib/mnesia/test/mnesia_schema_recovery_test.erl index 387238ae6b..0fe26efd0b 100644 --- a/lib/mnesia/test/mnesia_schema_recovery_test.erl +++ b/lib/mnesia/test/mnesia_schema_recovery_test.erl @@ -26,8 +26,8 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). -define(receive_messages(Msgs), receive_messages(Msgs, ?FILE, ?LINE)). @@ -41,92 +41,82 @@ fin_per_testcase(Func, Conf) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Verify recoverabiliy of schema transactions.", - " Verify that a schema transaction", - " can be completed when it has been logged correctly and Mnesia", - " crashed before the log has been dumped. Then the transaction ", - " should be handled during the log dump at startup" - ]; -all(suite) -> - [interrupted_before_log_dump, - interrupted_after_log_dump]. - -interrupted_before_log_dump(suite) -> - [interrupted_before_create_ram, - interrupted_before_create_disc, - interrupted_before_create_disc_only, - interrupted_before_create_nostore, - interrupted_before_delete_ram, - interrupted_before_delete_disc, - interrupted_before_delete_disc_only, - interrupted_before_add_ram, - interrupted_before_add_disc, - interrupted_before_add_disc_only, - interrupted_before_add_kill_copier, - interrupted_before_move_ram, - interrupted_before_move_disc, - interrupted_before_move_disc_only, - interrupted_before_move_kill_copier, - interrupted_before_delcopy_ram, - interrupted_before_delcopy_disc, - interrupted_before_delcopy_disc_only, - interrupted_before_delcopy_kill_copier, - interrupted_before_addindex_ram, - interrupted_before_addindex_disc, - interrupted_before_addindex_disc_only, - interrupted_before_delindex_ram, - interrupted_before_delindex_disc, - interrupted_before_delindex_disc_only, - interrupted_before_change_type_ram2disc, - interrupted_before_change_type_ram2disc_only, - interrupted_before_change_type_disc2ram, - interrupted_before_change_type_disc2disc_only, - interrupted_before_change_type_disc_only2ram, - interrupted_before_change_type_disc_only2disc, - interrupted_before_change_type_other_node, - interrupted_before_change_schema_type %% Change schema table copy type!! - ]. - -interrupted_after_log_dump(suite) -> - [interrupted_after_create_ram, - interrupted_after_create_disc, - interrupted_after_create_disc_only, - interrupted_after_create_nostore, - interrupted_after_delete_ram, - interrupted_after_delete_disc, - interrupted_after_delete_disc_only, - interrupted_after_add_ram, - interrupted_after_add_disc, - interrupted_after_add_disc_only, - interrupted_after_add_kill_copier, - interrupted_after_move_ram, - interrupted_after_move_disc, - interrupted_after_move_disc_only, - interrupted_after_move_kill_copier, - interrupted_after_delcopy_ram, - interrupted_after_delcopy_disc, - interrupted_after_delcopy_disc_only, - interrupted_after_delcopy_kill_copier, - interrupted_after_addindex_ram, - interrupted_after_addindex_disc, - interrupted_after_addindex_disc_only, - interrupted_after_delindex_ram, - interrupted_after_delindex_disc, - interrupted_after_delindex_disc_only, - interrupted_after_change_type_ram2disc, - interrupted_after_change_type_ram2disc_only, - interrupted_after_change_type_disc2ram, - interrupted_after_change_type_disc2disc_only, - interrupted_after_change_type_disc_only2ram, - interrupted_after_change_type_disc_only2disc, - interrupted_after_change_type_other_node, - interrupted_after_change_schema_type %% Change schema table copy type!! - -% interrupted_before_change_access_mode, -% interrupted_before_transform, -% interrupted_before_restore, - ]. +all() -> + [{group, interrupted_before_log_dump}, + {group, interrupted_after_log_dump}]. + +groups() -> + [{interrupted_before_log_dump, [], + [interrupted_before_create_ram, + interrupted_before_create_disc, + interrupted_before_create_disc_only, + interrupted_before_create_nostore, + interrupted_before_delete_ram, + interrupted_before_delete_disc, + interrupted_before_delete_disc_only, + interrupted_before_add_ram, interrupted_before_add_disc, + interrupted_before_add_disc_only, + interrupted_before_add_kill_copier, + interrupted_before_move_ram, + interrupted_before_move_disc, + interrupted_before_move_disc_only, + interrupted_before_move_kill_copier, + interrupted_before_delcopy_ram, + interrupted_before_delcopy_disc, + interrupted_before_delcopy_disc_only, + interrupted_before_delcopy_kill_copier, + interrupted_before_addindex_ram, + interrupted_before_addindex_disc, + interrupted_before_addindex_disc_only, + interrupted_before_delindex_ram, + interrupted_before_delindex_disc, + interrupted_before_delindex_disc_only, + interrupted_before_change_type_ram2disc, + interrupted_before_change_type_ram2disc_only, + interrupted_before_change_type_disc2ram, + interrupted_before_change_type_disc2disc_only, + interrupted_before_change_type_disc_only2ram, + interrupted_before_change_type_disc_only2disc, + interrupted_before_change_type_other_node, + interrupted_before_change_schema_type]}, + {interrupted_after_log_dump, [], + [interrupted_after_create_ram, + interrupted_after_create_disc, + interrupted_after_create_disc_only, + interrupted_after_create_nostore, + interrupted_after_delete_ram, + interrupted_after_delete_disc, + interrupted_after_delete_disc_only, + interrupted_after_add_ram, interrupted_after_add_disc, + interrupted_after_add_disc_only, + interrupted_after_add_kill_copier, + interrupted_after_move_ram, interrupted_after_move_disc, + interrupted_after_move_disc_only, + interrupted_after_move_kill_copier, + interrupted_after_delcopy_ram, + interrupted_after_delcopy_disc, + interrupted_after_delcopy_disc_only, + interrupted_after_delcopy_kill_copier, + interrupted_after_addindex_ram, + interrupted_after_addindex_disc, + interrupted_after_addindex_disc_only, + interrupted_after_delindex_ram, + interrupted_after_delindex_disc, + interrupted_after_delindex_disc_only, + interrupted_after_change_type_ram2disc, + interrupted_after_change_type_ram2disc_only, + interrupted_after_change_type_disc2ram, + interrupted_after_change_type_disc2disc_only, + interrupted_after_change_type_disc_only2ram, + interrupted_after_change_type_disc_only2disc, + interrupted_after_change_type_other_node, + interrupted_after_change_schema_type]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. interrupted_before_create_ram(suite) -> []; interrupted_before_create_ram(Config) when is_list(Config) -> diff --git a/lib/mnesia/test/mnesia_test_lib.erl b/lib/mnesia/test/mnesia_test_lib.erl index 1e98f017f7..9da45975d5 100644 --- a/lib/mnesia/test/mnesia_test_lib.erl +++ b/lib/mnesia/test/mnesia_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -130,7 +130,7 @@ doc/1, struct/1, init_per_testcase/2, - fin_per_testcase/2, + end_per_testcase/2, kill_tc/2 ]). @@ -144,7 +144,7 @@ init_per_testcase(_Func, Config) -> global:register_name(mnesia_global_logger, group_leader()), Config. -fin_per_testcase(_Func, Config) -> +end_per_testcase(_Func, Config) -> global:unregister_name(mnesia_global_logger), %% Nodes = select_nodes(all, Config, ?FILE, ?LINE), %% rpc:multicall(Nodes, mnesia, lkill, []), @@ -413,32 +413,28 @@ test_driver([T|TestCases], Config) -> [L1|L2]; test_driver({Module, TestCases}, Config) when is_list(TestCases)-> test_driver(default_module(Module, TestCases), Config); -test_driver({_, {Module, TestCase}}, Config) -> - test_driver({Module, TestCase}, Config); +test_driver({Module, all}, Config) -> + get_suite(Module, all, Config); +test_driver({Module, G={group, _}}, Config) -> + get_suite(Module, G, Config); +test_driver({_, {group, Module, Group}}, Config) -> + get_suite(Module, {group, Group}, Config); + test_driver({Module, TestCase}, Config) -> Sec = timer:seconds(1) * 1000, - case get_suite(Module, TestCase) of - [] when Config == suite -> + case Config of + suite -> {Module, TestCase, 'IMPL'}; - [] -> + _ -> log("Eval test case: ~w~n", [{Module, TestCase}]), - {T, Res} = - timer:tc(?MODULE, eval_test_case, [Module, TestCase, Config]), - log("Tested ~w in ~w sec~n", [TestCase, T div Sec]), - {T div Sec, Res}; - Suite when is_list(Suite), Config == suite -> - Res = test_driver(default_module(Module, Suite), Config), - {{Module, TestCase}, Res}; - Suite when is_list(Suite) -> - log("Expand test case ~w~n", [{Module, TestCase}]), - Def = default_module(Module, Suite), - {T, Res} = timer:tc(?MODULE, test_driver, [Def, Config]), - {T div Sec, {{Module, TestCase}, Res}}; - 'NYI' when Config == suite -> - {Module, TestCase, 'NYI'}; - 'NYI' -> - log("<WARNING> Test case ~w NYI~n", [{Module, TestCase}]), - {0, {skip, {Module, TestCase}, "NYI"}} + try timer:tc(?MODULE, eval_test_case, [Module, TestCase, Config]) of + {T, Res} -> + log("Tested ~w in ~w sec~n", [TestCase, T div Sec]), + {T div Sec, Res} + catch error:function_clause -> + log("<WARNING> Test case ~w NYI~n", [{Module, TestCase}]), + {0, {skip, {Module, TestCase}, "NYI"}} + end end; test_driver(TestCase, Config) -> DefaultModule = mnesia_SUITE, @@ -449,18 +445,50 @@ test_driver(TestCase, Config) -> default_module(DefaultModule, TestCases) when is_list(TestCases) -> Fun = fun(T) -> case T of + {group, _} -> {true, {DefaultModule, T}}; {_, _} -> true; T -> {true, {DefaultModule, T}} end end, lists:zf(Fun, TestCases). +get_suite(Module, TestCase, Config) -> + case get_suite(Module, TestCase) of + Suite when is_list(Suite), Config == suite -> + Res = test_driver(default_module(Module, Suite), Config), + {{Module, TestCase}, Res}; + Suite when is_list(Suite) -> + log("Expand test case ~w~n", [{Module, TestCase}]), + Def = default_module(Module, Suite), + {T, Res} = timer:tc(?MODULE, test_driver, [Def, Config]), + Sec = timer:seconds(1) * 1000, + {T div Sec, {{Module, TestCase}, Res}}; + 'NYI' when Config == suite -> + {Module, TestCase, 'NYI'}; + 'NYI' -> + log("<WARNING> Test case ~w NYI~n", [{Module, TestCase}]), + {0, {skip, {Module, TestCase}, "NYI"}} + end. + %% Returns a list (possibly empty) or the atom 'NYI' -get_suite(Mod, Fun) -> - case catch (apply(Mod, Fun, [suite])) of +get_suite(Mod, {group, Suite}) -> + try + Groups = Mod:groups(), + {_, _, TCList} = lists:keyfind(Suite, 1, Groups), + TCList + catch + _:Reason -> + io:format("Not implemented ~p ~p (~p ~p)~n", + [Mod,Suite,Reason, erlang:get_stacktrace()]), + 'NYI' + end; +get_suite(Mod, all) -> + case catch (apply(Mod, all, [])) of {'EXIT', _} -> 'NYI'; List when is_list(List) -> List - end. + end; +get_suite(_Mod, _Fun) -> + []. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -492,20 +520,24 @@ wait_for_evaluator(Pid, Mod, Fun, Config) -> {'EXIT', Pid, {skipped, Reason}} -> log("<WARNING> Test case ~w skipped, because ~p~n", [{Mod, Fun}, Reason]), - Mod:fin_per_testcase(Fun, Config), + Mod:end_per_testcase(Fun, Config), {skip, {Mod, Fun}, Reason}; {'EXIT', Pid, Reason} -> log("<>ERROR<> Eval process ~w exited, because ~p~n", [{Mod, Fun}, Reason]), - Mod:fin_per_testcase(Fun, Config), + Mod:end_per_testcase(Fun, Config), {crash, {Mod, Fun}, Reason} end. test_case_evaluator(Mod, Fun, [Config]) -> NewConfig = Mod:init_per_testcase(Fun, Config), - R = apply(Mod, Fun, [NewConfig]), - Mod:fin_per_testcase(Fun, NewConfig), - exit({test_case_ok, R}). + try + R = apply(Mod, Fun, [NewConfig]), + Mod:end_per_testcase(Fun, NewConfig), + exit({test_case_ok, R}) + catch error:function_clause -> + exit({skipped, 'NYI'}) + end. activity_evaluator(Coordinator) -> activity_evaluator_loop(Coordinator), diff --git a/lib/mnesia/test/mnesia_test_lib.hrl b/lib/mnesia/test/mnesia_test_lib.hrl index 85f12200d4..fc377dbd2c 100644 --- a/lib/mnesia/test/mnesia_test_lib.hrl +++ b/lib/mnesia/test/mnesia_test_lib.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/mnesia/test/mnesia_tpcb.erl b/lib/mnesia/test/mnesia_tpcb.erl index 903c53a21c..595412ff24 100644 --- a/lib/mnesia/test/mnesia_tpcb.erl +++ b/lib/mnesia/test/mnesia_tpcb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2010. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in diff --git a/lib/mnesia/test/mnesia_trans_access_test.erl b/lib/mnesia/test/mnesia_trans_access_test.erl index c67382e694..ca3f0fbf49 100644 --- a/lib/mnesia/test/mnesia_trans_access_test.erl +++ b/lib/mnesia/test/mnesia_trans_access_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -26,8 +26,8 @@ init_per_testcase(Func, Conf) -> mnesia_test_lib:init_per_testcase(Func, Conf). -fin_per_testcase(Func, Conf) -> - mnesia_test_lib:fin_per_testcase(Func, Conf). +end_per_testcase(Func, Conf) -> + mnesia_test_lib:end_per_testcase(Func, Conf). -define(receive_messages(Msgs), mnesia_recovery_test:receive_messages(Msgs, ?FILE, ?LINE)). @@ -40,18 +40,41 @@ fin_per_testcase(Func, Conf) -> -endif. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -all(doc) -> - ["Evil access of records in the scope of transactions", - "Invoke all functions in the API and try to cover all legal uses", - "cases as well the illegal dito. This is a complement to the", - "other more explicit test cases."]; -all(suite) -> - [ - write, read, wread, delete, delete_object, - match_object, select, select14, all_keys, - transaction, nested_activities, - index_tabs, index_lifecycle - ]. +all() -> + [write, read, wread, delete, delete_object, + match_object, select, select14, all_keys, transaction, + {group, nested_activities}, {group, index_tabs}, + {group, index_lifecycle}]. + +groups() -> + [{nested_activities, [], + [basic_nested, {group, nested_transactions}, + mix_of_nested_activities]}, + {nested_transactions, [], + [nested_trans_both_ok, nested_trans_child_dies, + nested_trans_parent_dies, nested_trans_both_dies]}, + {index_tabs, [], + [index_match_object, index_read, {group, index_update}, + index_write]}, + {index_update, [], + [index_update_set, index_update_bag]}, + {index_lifecycle, [], + [add_table_index_ram, add_table_index_disc, + add_table_index_disc_only, create_live_table_index_ram, + create_live_table_index_disc, + create_live_table_index_disc_only, del_table_index_ram, + del_table_index_disc, del_table_index_disc_only, + {group, idx_schema_changes}]}, + {idx_schema_changes, [], + [idx_schema_changes_ram, idx_schema_changes_disc, + idx_schema_changes_disc_only]}]. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + %% Write records @@ -404,12 +427,6 @@ transaction(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -nested_activities(suite) -> - [ - basic_nested, - nested_transactions, - mix_of_nested_activities - ]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -520,13 +537,6 @@ n_f4() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -nested_transactions(doc) -> - ["Verify that nested_transactions are handled as expected"]; -nested_transactions(suite) -> - [nested_trans_both_ok, - nested_trans_child_dies, - nested_trans_parent_dies, - nested_trans_both_dies]. nested_trans_both_ok(suite) -> []; nested_trans_both_ok(Config) when is_list(Config) -> @@ -671,13 +681,6 @@ read_op(Oid) -> Ops end. -index_tabs(suite) -> - [ - index_match_object, - index_read, - index_update, - index_write - ]. %% Read matching records by using an index @@ -767,10 +770,6 @@ index_read(Config) when is_list(Config) -> ?match({'EXIT', {aborted, no_transaction}}, mnesia:index_read(Tab, 2, ValPos)), ?verify_mnesia(Nodes, []). -index_update(suite) -> [index_update_set, index_update_bag]; -index_update(doc) -> ["See Ticket OTP-2083, verifies that a table with a index is " - "update in the correct way i.e. the index finds the correct " - "records after a update"]. index_update_set(suite) -> []; index_update_set(Config)when is_list(Config) -> [Node1] = Nodes = ?acquire_nodes(1, Config), @@ -1046,19 +1045,6 @@ index_write(Config)when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Add and drop indecies -index_lifecycle(suite) -> - [ - add_table_index_ram, - add_table_index_disc, - add_table_index_disc_only, - create_live_table_index_ram, - create_live_table_index_disc, - create_live_table_index_disc_only, - del_table_index_ram, - del_table_index_disc, - del_table_index_disc_only, - idx_schema_changes - ]. add_table_index_ram(suite) -> []; add_table_index_ram(Config) when is_list(Config) -> @@ -1116,9 +1102,9 @@ create_live_table_index_disc_only(Config) when is_list(Config) -> create_live_table_index(Config, disc_only_copies). create_live_table_index(Config, Storage) -> - [Node1] = Nodes = ?acquire_nodes(1, Config), + [N1,N2,N3] = Nodes = ?acquire_nodes(3, Config), Tab = create_live_table_index, - Schema = [{name, Tab}, {attributes, [k, v]}, {Storage, [Node1]}], + Schema = [{name, Tab}, {attributes, [k, v]}, {Storage, Nodes}], ?match({atomic, ok}, mnesia:create_table(Schema)), ValPos = 3, mnesia:dirty_write({Tab, 1, 2}), @@ -1129,9 +1115,35 @@ create_live_table_index(Config, Storage) -> end, ?match({atomic, ok}, mnesia:transaction(Fun)), ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)), + IRead = fun() -> lists:sort(mnesia:index_read(Tab, 2, ValPos)) end, + ?match({atomic, [{Tab, 1, 2},{Tab, 2, 2}]}, mnesia:transaction(IRead)), + ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)), + + %% Bug when adding index when table is still unloaded + %% By setting load order we hopefully will trigger the bug + mnesia:change_table_copy_type(Tab, N2, ram_copies), + mnesia:change_table_copy_type(Tab, N3, ram_copies), + ?match({atomic,ok}, mnesia:change_table_copy_type(schema, N2, ram_copies)), + ?match({atomic,ok}, mnesia:change_table_copy_type(schema, N3, ram_copies)), + + Create = fun(N) -> + TabN = list_to_atom("tab_" ++ integer_to_list(N)), + Def = [{ram_copies, Nodes}, {load_order, N}], + mnesia:create_table(TabN, Def) + end, + + ?match([{atomic,ok}|_], [Create(N) || N <- lists:seq(1,50)]), + + ?match([], mnesia_test_lib:stop_mnesia([N2,N3])), + ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes,[N1]}]])), + ?match(ok, rpc:call(N3, mnesia, start, [[{extra_db_nodes,[N1]}]])), + + ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)), + + ?match({atomic, [{Tab, 1, 2},{Tab, 2, 2}]}, mnesia:transaction(IRead)), ?match({atomic, [{Tab, 1, 2},{Tab, 2, 2}]}, - mnesia:transaction(fun() -> lists:sort(mnesia:index_read(Tab, 2, ValPos)) - end)), + rpc:call(N2, mnesia, transaction, [IRead])), + ?verify_mnesia(Nodes, []). %% Drop table index @@ -1171,13 +1183,6 @@ del_table_index(Config, Storage) -> ?match({atomic, ok}, mnesia:transaction(NestedFun)), ?verify_mnesia(Nodes, []). -idx_schema_changes(suite) -> [idx_schema_changes_ram, - idx_schema_changes_disc, - idx_schema_changes_disc_only]; -idx_schema_changes(doc) -> - ["Tests that index tables are handled correctly when schema changes.", - "For example when a replica is deleted or inserted", - "TICKET OTP-2XXX (ELVIRA)"]. idx_schema_changes_ram(suite) -> []; idx_schema_changes_ram(Config) when is_list(Config) -> diff --git a/lib/mnesia/test/mt.erl b/lib/mnesia/test/mt.erl index f69c4a11fd..f1152a7bc4 100644 --- a/lib/mnesia/test/mt.erl +++ b/lib/mnesia/test/mt.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2011. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -57,6 +57,7 @@ alias(heavy) -> {mnesia_SUITE, heavy}; alias(install) -> mnesia_install_test; alias(isolation) -> mnesia_isolation_test; alias(light) -> {mnesia_SUITE, light}; +alias(majority) -> mnesia_majority_test; alias(measure) -> mnesia_measure_test; alias(medium) -> {mnesia_SUITE, medium}; alias(nice) -> mnesia_nice_coverage_test; @@ -76,17 +77,24 @@ resolve(Suite0) when is_atom(Suite0) -> Suite when is_atom(Suite) -> {Suite, all}; {Suite, Case} -> - {Suite, Case} + {Suite, is_group(Suite,Case)} end; resolve({Suite0, Case}) when is_atom(Suite0), is_atom(Case) -> case alias(Suite0) of Suite when is_atom(Suite) -> - {Suite, Case}; + {Suite, is_group(Suite,Case)}; {Suite, Case2} -> - {Suite, Case2} + {Suite, is_group(Suite,Case2)} end; resolve(List) when is_list(List) -> [resolve(Case) || Case <- List]. + +is_group(Mod, Case) -> + try {_,_,_} = lists:keyfind(Case, 1, Mod:groups()), + {group, Case} + catch _:{badmatch,_} -> + Case + end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Run one or more test cases diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk index 31cc8f8513..a21ab007ef 100644 --- a/lib/mnesia/vsn.mk +++ b/lib/mnesia/vsn.mk @@ -1,16 +1 @@ - -MNESIA_VSN = 4.4.13 - -TICKETS = OTP-8402 OTP-8406 -#TICKETS_4.4.12 = OTP-8250 -#TICKETS_4.4.11 = OTP-8074 -#TICKETS_4.4.10 = OTP-7928 OTP-7968 OTP-8002 -#TICKETS_4.4.9 = OTP-7911 -#TICKETS_4.4.8 = OTP-7753 OTP-7835 -#TICKETS_4.4.7 = OTP-7524 OTP-7625 -#TICKETS_4.4.6 = OTP-7585 -#TICKETS_4.4.5 = OTP-7466 -#TICKETS_4.4.4 = OTP-7419 -#TICKETS_4.4.3 = OTP-7340 OTP-7378 OTP-7383 -#TICKETS_4.4.2 = OTP-7205 OTP-7208 -#TICKETS_4.4.1 = OTP-7170 +MNESIA_VSN = 4.5 |