diff options
Diffstat (limited to 'lib/mnesia')
37 files changed, 836 insertions, 530 deletions
diff --git a/lib/mnesia/doc/src/Mnesia_chap1.xml b/lib/mnesia/doc/src/Mnesia_chap1.xml index 9dfeb5efe4..035e934ed2 100644 --- a/lib/mnesia/doc/src/Mnesia_chap1.xml +++ b/lib/mnesia/doc/src/Mnesia_chap1.xml @@ -32,38 +32,38 @@ <rev>C</rev> <file>Mnesia_chap1.xml</file> </header> - <p>The <c>Mnesia</c> application provides a heavy duty real-time + <p>The Mnesia application provides a heavy duty real-time distributed database.</p> <section> <title>Scope</title> <p>This User's Guide describes how to - build <c>Mnesia</c> database applications, and how to integrate - and use the <c>Mnesia</c> database management system with + build Mnesia database applications, and how to integrate + and use the Mnesia database management system with OTP. Programming constructs are described, and numerous programming examples are included to illustrate the use of - <c>Mnesia</c>.</p> + Mnesia.</p> <p>This User's Guide is organized as follows:</p> <list type="bulleted"> <item><seealso marker="Mnesia_overview">Mnesia</seealso> provides an introduction to - <c>Mnesia</c>. + Mnesia. </item> <item><seealso marker="Mnesia_chap2">Getting Started</seealso> - introduces <c>Mnesia</c> with an example database. Examples + introduces Mnesia with an example database. Examples are included how to start an Erlang session, specify a - <c>Mnesia</c> database directory, initialize a database - schema, start <c>Mnesia</c>, and create tables. Initial + Mnesia database directory, initialize a database + schema, start Mnesia, and create tables. Initial prototyping of record definitions is also discussed. </item> <item><seealso marker="Mnesia_chap3">Build a Mnesia Database</seealso> more formally describes the steps - introduced in the previous section, namely the <c>Mnesia</c> - functions that define a database schema, start <c>Mnesia</c>, + introduced in the previous section, namely the Mnesia + functions that define a database schema, start Mnesia, and create the required tables. </item> <item><seealso marker="Mnesia_chap4">Transactions and Other Access Contexts</seealso> - describes the transactions properties that make <c>Mnesia</c> into + describes the transactions properties that make Mnesia into a fault tolerant, real-time distributed database management system. This section also describes the concept of locking to ensure consistency in tables, and "dirty @@ -76,16 +76,16 @@ features include indexing, checkpoints, distribution and fault tolerance, disc-less nodes, replication manipulation, local content tables, concurrency, and object-based programming in - <c>Mnesia</c>. + Mnesia. </item> <item><seealso marker="Mnesia_chap7">Mnesia System Information</seealso> describes the files contained in the - <c>Mnesia</c> database directory, database configuration data, + Mnesia database directory, database configuration data, core and table dumps, as well as the important subject of backup, fall-back, and disaster recovery principles. </item> <item><seealso marker="Mnesia_chap8">Combine Mnesia with - SNMP</seealso> is a short section that outlines <c>Mnesia</c> + SNMP</seealso> is a short section that outlines Mnesia integrated with SNMP. </item> <item><seealso marker="Mnesia_App_A">Appendix A: Backup diff --git a/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc index ba0746e736..37389ce5ae 100644 --- a/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc +++ b/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc @@ -317,7 +317,7 @@ sex = male, phone = 98108, room_no = {221, 015}}, - insert_emp(Me, 'B/SFR', [Erlang, mnesia, otp]).</code> + insert_emp(Emp, 'B/SFR', [Erlang, mnesia, otp]).</code> <note><p>For information about Funs, see "Fun Expressions" in section <c>Erlang Reference Manual</c> in System Documentation..</p> diff --git a/lib/mnesia/doc/src/Mnesia_chap3.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap3.xmlsrc index 8f1a4366ee..ffda739dfa 100644 --- a/lib/mnesia/doc/src/Mnesia_chap3.xmlsrc +++ b/lib/mnesia/doc/src/Mnesia_chap3.xmlsrc @@ -348,7 +348,7 @@ skeppet %<input>erl -sname b -mnesia dir '"/ldisc/scratch/Mnesia.company"'</inpu <p>If the startup procedure fails, the function <seealso marker="mnesia#start/0">mnesia:start()</seealso> returns the cryptic tuple - <c>{error,{shutdown, {mnesia_sup,start,[normal,[]]}}}</c>. + <c>{error,{shutdown, {mnesia_sup,start_link,[normal,[]]}}}</c>. To get more information about the start failure, use command-line arguments <c>-boot start_sasl</c> as argument to the <c>erl</c> script.</p> diff --git a/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc index a83d1d77d2..62759c624b 100644 --- a/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc +++ b/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc @@ -362,11 +362,6 @@ ok <seealso marker="mnesia_frag_hash">mnesia_frag_hash</seealso> callback behavior. This property can explicitly be set at table creation. Default is <c>mnesia_frag_hash</c>.</p> - <p>Older tables, that were created before the concept of - user-defined hash modules was introduced, use module - <c>mnesia_frag_old_hash</c> to be backwards compatible. - <c>mnesia_frag_old_hash</c> still uses the poor - deprecated function <c>erlang:hash/1</c>.</p> </item> <tag><c>{hash_state, Term}</c></tag> <item> diff --git a/lib/mnesia/doc/src/Mnesia_chap8.xml b/lib/mnesia/doc/src/Mnesia_chap8.xml index f1a469e315..4a2eed84d7 100644 --- a/lib/mnesia/doc/src/Mnesia_chap8.xml +++ b/lib/mnesia/doc/src/Mnesia_chap8.xml @@ -51,11 +51,11 @@ </item> </list> <p>All these approaches have different advantages and - disadvantages. <c>Mnesia</c> applications can easily be opened to + disadvantages. Mnesia applications can easily be opened to the SNMP protocol. A direct 1-to-1 mapping can be established - between <c>Mnesia</c> tables and SNMP tables. This means - that a <c>Mnesia</c> table can be configured to be <em>both</em> - a <c>Mnesia</c> table and an SNMP table. A number of functions to + between Mnesia tables and SNMP tables. This means + that a Mnesia table can be configured to be <em>both</em> + a Mnesia table and an SNMP table. A number of functions to control this behavior are described in the Reference Manual.</p> </section> </chapter> diff --git a/lib/mnesia/doc/src/Mnesia_overview.xml b/lib/mnesia/doc/src/Mnesia_overview.xml index d2d597b85d..63f2309284 100644 --- a/lib/mnesia/doc/src/Mnesia_overview.xml +++ b/lib/mnesia/doc/src/Mnesia_overview.xml @@ -39,14 +39,14 @@ high level of fault tolerance that is required in many nonstop systems, combined with requirements on the DBMS to run in the same address space as the application, have led us to implement a new - DBMS, called <c>Mnesia</c>.</p> - <p><c>Mnesia</c> is implemented in, and tightly connected to Erlang. + DBMS, called Mnesia.</p> + <p>Mnesia is implemented in, and tightly connected to Erlang. It provides the functionality that is necessary for the implementation of fault tolerant telecommunications systems.</p> - <p><c>Mnesia</c> is a multiuser distributed DBMS specially made for + <p>Mnesia is a multiuser distributed DBMS specially made for industrial telecommunications applications written in Erlang, which is also the intended target language. - <c>Mnesia</c> tries to address all the data + Mnesia tries to address all the data management issues required for typical telecommunications systems. It has a number of features that are not normally found in traditional databases.</p> @@ -54,7 +54,7 @@ from the features provided by traditional DBMSs. The applications now implemented in Erlang need a mixture of a broad range of features, which generally are not satisfied by traditional DBMSs. - <c>Mnesia</c> is designed with requirements like the following in + Mnesia is designed with requirements like the following in mind:</p> <list type="ordered"> <item>Fast real-time key/value lookup @@ -71,9 +71,9 @@ <item>Complex objects </item> </list> - <p><c>Mnesia</c> is designed with the typical data management problems - of telecommunications applications in mind. This sets <c>Mnesia</c> - apart from most other DBMS. Hence <c>Mnesia</c> + <p>Mnesia is designed with the typical data management problems + of telecommunications applications in mind. This sets Mnesia + apart from most other DBMS. Hence Mnesia combines many concepts found in traditional databases such as transactions and queries with concepts found in data management systems for telecommunications applications, for example:</p> @@ -86,7 +86,7 @@ suspending it. </item> </list> - <p><c>Mnesia</c> is also interesting because of its tight coupling to + <p>Mnesia is also interesting because of its tight coupling to Erlang, thus almost turning Erlang into a database programming language. This has many benefits, the foremost is that the impedance mismatch between the data format used by the DBMS @@ -97,7 +97,7 @@ <title>Mnesia Database Management System (DBMS)</title> <section> <title>Features</title> - <p><c>Mnesia</c> contains the following features that combine to + <p>Mnesia contains the following features that combine to produce a fault-tolerant, distributed DBMS written in Erlang: </p> <list type="bulleted"> @@ -118,7 +118,7 @@ functions can be called within one transaction. </item> <item>Several transactions can run concurrently, and their execution - is fully synchronized by the DBMS. <c>Mnesia</c> ensures that no + is fully synchronized by the DBMS. Mnesia ensures that no two processes manipulate data simultaneously. </item> <item>Transactions can be assigned the property of being executed on @@ -132,29 +132,29 @@ <section> <title>Add-On Application</title> - <p>Query List Comprehension (QLC) can be used with <c>Mnesia</c> + <p>Query List Comprehension (QLC) can be used with Mnesia to produce specialized functions that enhance the operational - ability of <c>Mnesia</c>. QLC has its own documentation as part + ability of Mnesia. QLC has its own documentation as part of the OTP documentation set. The main features of QLC - when used with <c>Mnesia</c> are as follows:</p> + when used with Mnesia are as follows:</p> <list type="bulleted"> - <item>QLC can optimize the query compiler for the <c>Mnesia</c> + <item>QLC can optimize the query compiler for the Mnesia DBMS, essentially making the DBMS more efficient. </item> <item>QLC can be used as a database programming - language for <c>Mnesia</c>. It includes a notation called "list + language for Mnesia. It includes a notation called "list comprehensions" and can be used to make complex database queries over a set of tables. </item> </list> <p>For information about QLC, see the <seealso marker="stdlib:qlc">qlc</seealso> manual page - in <c>STDLIB</c>.</p> + in STDLIB.</p> </section> <section> <title>When to Use Mnesia</title> - <p>Use <c>Mnesia</c> with the following types of applications:</p> + <p>Use Mnesia with the following types of applications:</p> <list type="bulleted"> <item>Applications that need to replicate data. </item> @@ -166,7 +166,7 @@ <item>Applications that use soft real-time characteristics. </item> </list> - <p><c>Mnesia</c> is not as appropriate with the + <p>Mnesia is not as appropriate with the following types of applications:</p> <list type="bulleted"> <item>Programs that process plain text or binary data files. @@ -176,14 +176,14 @@ library module <c>dets</c>, which is a disc-based version of the module <c>ets</c>. For information about <c>dets</c>, see the <seealso marker="stdlib:dets">dets</seealso> - manual page in <c>STDLIB</c>. + manual page in STDLIB. </item> <item>Applications that need disc logging facilities. Those applications can use the module <c>disk_log</c> by preference. For information about <c>disk_log</c>, see the <seealso marker="kernel:disk_log">disk_log</seealso> - manual page in <c>Kernel</c>. + manual page in Kernel. </item> <item>Hard real-time systems. </item> diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml index 437df63aab..621b6047ee 100644 --- a/lib/mnesia/doc/src/mnesia.xml +++ b/lib/mnesia/doc/src/mnesia.xml @@ -37,7 +37,7 @@ <description> <p>The following are some of the most important and attractive - capabilities provided by <c>Mnesia</c>:</p> + capabilities provided by Mnesia:</p> <list type="bulleted"> <item>A relational/object hybrid data model that is suitable for telecommunications applications. @@ -62,15 +62,15 @@ reconfigured at runtime without stopping the system. </item> </list> - <p>This Reference Manual describes the <c>Mnesia</c> API. This - includes functions that define and manipulate <c>Mnesia</c> + <p>This Reference Manual describes the Mnesia API. This + includes functions that define and manipulate Mnesia tables.</p> <p>All functions in this Reference Manual can be used in any combination with queries using the list comprehension notation. For information about the query notation, see the <seealso marker="stdlib:qlc">qlc</seealso> - manual page in <c>STDLIB</c>.</p> - <p>Data in <c>Mnesia</c> is organized as a set of tables. Each table + manual page in STDLIB.</p> + <p>Data in Mnesia is organized as a set of tables. Each table has a name that must be an atom. Each table is made up of Erlang records. The user is responsible for the record definitions. Each table also has a set of properties. The @@ -121,14 +121,14 @@ <item> <p><c>index</c>. This is a list of attribute names, or integers, which specify the tuple positions on which - <c>Mnesia</c> is to build and maintain an extra index + Mnesia is to build and maintain an extra index table.</p> </item> <item> <p><c>local_content</c>. When an application requires tables whose contents are local to each node, <c>local_content</c> tables can be used. The table name - is known to all <c>Mnesia</c> nodes, but its content is + is known to all Mnesia nodes, but its content is unique on each node. This means that access to such a table must be done locally. Set field <c>local_content</c> to <c>true</c> to enable the <c>local_content</c> @@ -143,7 +143,7 @@ avoid inconsistencies because of network splits.</p> </item> <item> - <p><c>snmp</c>. Each (set-based) <c>Mnesia</c> table can be + <p><c>snmp</c>. Each (set-based) Mnesia table can be automatically turned into a Simple Network Management Protocol (SNMP) ordered table as well. This property specifies the types of the SNMP keys.</p> @@ -174,7 +174,7 @@ copy of each modified record during the transaction. During iteration, that is, <c>mnesia:fold[lr]/4</c>, <c>mnesia:next/2</c>, <c>mnesia:prev/2</c>, and - <c>mnesia:snmp_get_next_index/2</c>, <c>Mnesia</c> + <c>mnesia:snmp_get_next_index/2</c>, Mnesia compensates for every written or deleted record, which can reduce the performance.</p> <p>If possible, avoid writing or deleting records in the same @@ -188,7 +188,7 @@ <desc> <p>Makes the transaction silently return the tuple <c>{aborted, Reason}</c>. - Termination of a <c>Mnesia</c> transaction means that + Termination of a Mnesia transaction means that an exception is thrown to an enclosing <c>catch</c>. Thus, the expression <c>catch mnesia:abort(x)</c> does not terminate the transaction.</p> @@ -324,7 +324,7 @@ <c>mnesia:ets</c>. Argument <c>AccessMod</c> is the name of a callback module, which implements the <c>mnesia_access</c> behavior.</p> - <p><c>Mnesia</c> forwards calls to the following functions:</p> + <p>Mnesia forwards calls to the following functions:</p> <list type="bulleted"> <item>mnesia:lock/2 (read_lock_table/1, write_lock_table/1) </item> @@ -391,15 +391,15 @@ </item> </list> <p><c>ActivityId</c> is a record that represents the identity - of the enclosing <c>Mnesia</c> activity. The first field + of the enclosing Mnesia activity. The first field (obtained with <c>element(1, ActivityId)</c>) contains an atom, which can be interpreted as the activity type: <c>ets</c>, <c>async_dirty</c>, <c>sync_dirty</c>, or <c>tid</c>. <c>tid</c> means that the activity is a transaction. The structure of the rest of the identity - record is internal to <c>Mnesia</c>.</p> + record is internal to Mnesia.</p> <p><c>Opaque</c> is an opaque data structure that is internal - to <c>Mnesia</c>.</p> + to Mnesia.</p> </desc> </func> <func> @@ -458,7 +458,7 @@ mnesia:add_table_index(person, age)</code> <desc> <marker id="async_dirty"></marker> <p>Calls the <c>Fun</c> in a context that is not protected by - a transaction. The <c>Mnesia</c> function calls performed in + a transaction. The Mnesia function calls performed in the <c>Fun</c> are mapped to the corresponding dirty functions. This still involves logging, replication, and subscriptions, but there is no locking, local transaction @@ -467,7 +467,7 @@ mnesia:add_table_index(person, age)</code> for normal <c>mnesia:dirty_*</c> operations, the operations are performed semi-asynchronously. For details, see <c>mnesia:activity/4</c> and the User's Guide.</p> - <p>The <c>Mnesia</c> tables can be manipulated without + <p>The Mnesia tables can be manipulated without using transactions. This has some serious disadvantages, but is considerably faster, as the transaction manager is not involved and no locks are set. A dirty operation does, @@ -480,7 +480,7 @@ mnesia:add_table_index(person, age)</code> read records dirty than within a transaction.</p> <p>Depending on the application, it can be a good idea to use the dirty functions for certain operations. Almost all - <c>Mnesia</c> functions that can be called within + Mnesia functions that can be called within transactions have a dirty equivalent, which is much more efficient.</p> <p>However, notice that there is a risk that the database can @@ -497,7 +497,7 @@ mnesia:add_table_index(person, age)</code> <fsummary>Backs up all tables in the database.</fsummary> <desc> <marker id="backup"></marker> - <p>Activates a new checkpoint covering all <c>Mnesia</c> tables, + <p>Activates a new checkpoint covering all Mnesia tables, including the schema, with maximum degree of redundancy, and performs a backup using <c>backup_checkpoint/2/3</c>. The default value of the backup callback module <c>BackupMod</c> @@ -529,16 +529,16 @@ mnesia:add_table_index(person, age)</code> <taglist> <tag><c>extra_db_nodes</c></tag> <item> - <p><c>Value</c> is a list of nodes that <c>Mnesia</c> + <p><c>Value</c> is a list of nodes that Mnesia is to try to connect to. <c>ReturnValue</c> is those - nodes in <c>Value</c> that <c>Mnesia</c> is connected + nodes in <c>Value</c> that Mnesia is connected to.</p> <p>Notice that this function must only be used to connect to newly started RAM nodes (N.D.R.S.N.) with an empty schema. If, for example, this function is used after the network has been partitioned, it can lead to inconsistent tables.</p> - <p>Notice that <c>Mnesia</c> can be connected to other + <p>Notice that Mnesia can be connected to other nodes than those returned in <c>ReturnValue</c>.</p> </item> <tag><c>dc_dump_limit</c></tag> @@ -548,7 +548,7 @@ mnesia:add_table_index(person, age)</code> Configuration Parameters</seealso>. <c>ReturnValue</c> is the new value. Notice that this configuration parameter is not persistent. It is lost when - <c>Mnesia</c> has stopped.</p> + Mnesia has stopped.</p> </item> </taglist> </desc> @@ -562,9 +562,9 @@ mnesia:add_table_index(person, age)</code> <c>read_write</c> but it can also be set to the atom <c>read_only</c>. If <c>AccessMode</c> is set to <c>read_only</c>, updates to the table cannot be - performed. At startup, <c>Mnesia</c> always loads + performed. At startup, Mnesia always loads <c>read_only</c> tables locally regardless of when and if - <c>Mnesia</c> is terminated on other nodes.</p> + Mnesia is terminated on other nodes.</p> </desc> </func> <func> @@ -620,13 +620,13 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> <desc> <marker id="create_schema"></marker> <p>Creates a new database on disc. Various files are - created in the local <c>Mnesia</c> directory of each node. + created in the local Mnesia directory of each node. Notice that the directory must be unique for each node. Two nodes must never share the same directory. If possible, use a local disc device to improve performance.</p> <p><c>mnesia:create_schema/1</c> fails if any of the Erlang nodes given as <c>DiscNodes</c> are not alive, if - <c>Mnesia</c> is running on any of the nodes, or if any + Mnesia is running on any of the nodes, or if any of the nodes already have a schema. Use <c>mnesia:delete_schema/1</c> to get rid of old faulty schemas.</p> @@ -638,10 +638,10 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> </func> <func> <name>create_table(Name, TabDef) -> {atomic, ok} | {aborted, Reason}</name> - <fsummary>Creates a <c>Mnesia</c> table called <c>Name</c>with properties as described by argument <c>TabDef</c>.</fsummary> + <fsummary>Creates a Mnesia table called <c>Name</c>with properties as described by argument <c>TabDef</c>.</fsummary> <desc> <marker id="create_table"></marker> - <p>Creates a <c>Mnesia</c> table called + <p>Creates a Mnesia table called <c>Name</c> according to argument <c>TabDef</c>. This list must be a list of <c>{Item, Value}</c> tuples, where the following values are allowed:</p> @@ -652,8 +652,8 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> set to the atom <c>read_only</c>. If <c>AccessMode</c> is set to <c>read_only</c>, updates to the table cannot be performed.</p> - <p>At startup, <c>Mnesia</c> always loads <c>read_only</c> - table locally regardless of when and if <c>Mnesia</c> is + <p>At startup, Mnesia always loads <c>read_only</c> + table locally regardless of when and if Mnesia is terminated on other nodes. This argument returns the access mode of the table. The access mode can be <c>read_only</c> or <c>read_write</c>.</p> @@ -693,7 +693,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> <item> <p><c>{index, Intlist}</c>, where <c>Intlist</c> is a list of attribute names (atoms) or - record fields for which <c>Mnesia</c> is to build and + record fields for which Mnesia is to build and maintain an extra index table. The <c>qlc</c> query compiler <em>may</em> be able to optimize queries if there are indexes available.</p> @@ -735,10 +735,10 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> <c>mnesia:create_table/2</c>, the table is immediately accessible by SNMP. Therefore applications that use SNMP to manipulate and control the system can be - designed easily, since <c>Mnesia</c> provides a + designed easily, since Mnesia provides a direct mapping between the logical tables that make up an SNMP control application and the physical data that - makes up a <c>Mnesia</c> table.</p> + makes up a Mnesia table.</p> </item> <item> <p><c>{storage_properties, [{Backend, Properties}]</c> @@ -746,7 +746,7 @@ mnesia:change_table_copy_type(person, node(), disc_copies)</code> <c>Backend</c> can currently be <c>ets</c> or <c>dets</c>. <c>Properties</c> is a list of options sent to the back end storage during table creation. <c>Properties</c> - cannot contain properties already used by <c>Mnesia</c>, + cannot contain properties already used by Mnesia, such as <c>type</c> or <c>named_table</c>.</p> <p>For example:</p> <code type="none"> @@ -776,7 +776,7 @@ mnesia:create_table(table, [{ram_copies, [node()]}, {disc_only_copies, nodes()}, mnesia:create_table(person, [{ram_copies, [N1, N2]}, {attributes, record_info(fields, person)}]).</code> - <p>If it is required that <c>Mnesia</c> must build and + <p>If it is required that Mnesia must build and maintain an extra index table on attribute <c>address</c> of all the <c>person</c> records that are inserted in the table, the following code would be issued:</p> @@ -819,8 +819,8 @@ mnesia:create_table(person, When the last replica is deleted with this function, the table disappears entirely.</p> <p>This function can also be used to delete a replica of - the table named <c>schema</c>. The <c>Mnesia</c> node is - then removed. Notice that <c>Mnesia</c> must be + the table named <c>schema</c>. The Mnesia node is + then removed. Notice that Mnesia must be stopped on the node first.</p> </desc> </func> @@ -891,9 +891,9 @@ mnesia:create_table(person, <c>mnesia:create_schema/1</c>. <c>mnesia:delete_schema/1</c> fails if any of the Erlang nodes given as <c>DiscNodes</c> are not alive, or if - <c>Mnesia</c> is running on any of the nodes.</p> + Mnesia is running on any of the nodes.</p> <p>After the database is deleted, it can still be possible - to start <c>Mnesia</c> as a disc-less node. This depends + to start Mnesia as a disc-less node. This depends on how configuration parameter <c>schema_location</c> is set.</p> <warning> @@ -1100,7 +1100,7 @@ mnesia:create_table(person, <name>dirty_update_counter(Tab, Key, Incr) -> NewVal | exit({aborted, Reason})</name> <fsummary>Dirty update of a counter record.</fsummary> <desc> - <p><c>Mnesia</c> has no special counter records. However, + <p>Mnesia has no special counter records. However, records of the form <c>{Tab, Key, Integer}</c> can be used as (possibly disc-resident) counters when <c>Tab</c> is a <c>set</c>. This function updates a counter with a positive @@ -1147,7 +1147,7 @@ mnesia:create_table(person, <desc> <marker id="dump_log"></marker> <p>Performs a user-initiated dump of the local log file. - This is usually not necessary, as <c>Mnesia</c> by default + This is usually not necessary, as Mnesia by default manages this automatically. See configuration parameters <seealso marker="#dump_log_time_threshold">dump_log_time_threshold</seealso> and @@ -1172,7 +1172,7 @@ mnesia:create_table(person, <fsummary>Dumps local tables into a text file.</fsummary> <desc> <marker id="dump_to_textfile"></marker> - <p>Dumps all local tables of a <c>Mnesia</c> system into a + <p>Dumps all local tables of a Mnesia system into a text file, which can be edited (by a normal text editor) and then be reloaded with <c>mnesia:load_textfile/1</c>. Only use this function for @@ -1182,10 +1182,10 @@ mnesia:create_table(person, </func> <func> <name>error_description(Error) -> String</name> - <fsummary>Returns a string describing a particular <c>Mnesia</c> error.</fsummary> + <fsummary>Returns a string describing a particular Mnesia error.</fsummary> <desc> <marker id="error_description"></marker> - <p>All <c>Mnesia</c> transactions, including all the schema + <p>All Mnesia transactions, including all the schema update functions, either return value <c>{atomic, Val}</c> or the tuple <c>{aborted, Reason}</c>. <c>Reason</c> can be either of the atoms in the following list. The @@ -1264,8 +1264,8 @@ mnesia:create_table(person, <desc> <marker id="ets"></marker> <p>Calls the <c>Fun</c> in a raw context that is not protected by - a transaction. The <c>Mnesia</c> function call is performed in - the <c>Fun</c> and performed directly on the local <c>ets</c> + a transaction. The Mnesia function call is performed in + the <c>Fun</c> and performed directly on the local ETS tables on the assumption that the local storage type is <c>ram_copies</c> and the tables are not replicated to other nodes. Subscriptions are not triggered and checkpoints are @@ -1321,13 +1321,13 @@ mnesia:create_table(person, <fsummary>Forces a table to be loaded into the system.</fsummary> <desc> <marker id="force_load_table"></marker> - <p>The <c>Mnesia</c> algorithm for table load can lead to a + <p>The Mnesia algorithm for table load can lead to a situation where a table cannot be loaded. This situation - occurs when a node is started and <c>Mnesia</c> concludes, or + occurs when a node is started and Mnesia concludes, or suspects, that another copy of the table was active after this local copy became inactive because of a system crash.</p> <p>If this situation is not acceptable, this function can be - used to override the strategy of the <c>Mnesia</c> table + used to override the strategy of the Mnesia table load algorithm. This can lead to a situation where some transaction effects are lost with an inconsistent database as result, but for some applications high availability is more @@ -1402,9 +1402,9 @@ mnesia:create_table(person, <desc> <marker id="info"></marker> <p>Prints system information on the terminal. - This function can be used even if <c>Mnesia</c> is not + This function can be used even if Mnesia is not started. However, more information is displayed if - <c>Mnesia</c> is started.</p> + Mnesia is started.</p> </desc> </func> <func> @@ -1431,7 +1431,7 @@ mnesia:create_table(person, <p>Installs a backup as fallback. The fallback is used to restore the database at the next startup. Installation of fallbacks requires Erlang to be operational on all the - involved nodes, but it does not matter if <c>Mnesia</c> + involved nodes, but it does not matter if Mnesia is running or not. The installation of the fallback fails if the local node is not one of the disc-resident nodes in the backup.</p> @@ -1465,14 +1465,14 @@ mnesia:create_table(person, <p><c>{mnesia_dir, AlternateDir}</c>. This argument is only valid if the scope of the installation is <c>local</c>. Normally the installation - of a fallback is targeted to the <c>Mnesia</c> directory, + of a fallback is targeted to the Mnesia directory, as configured with configuration parameter <c>-mnesia dir</c>. But by explicitly supplying an <c>AlternateDir</c>, the fallback is installed there - regardless of the <c>Mnesia</c> directory configuration + regardless of the Mnesia directory configuration parameter setting. After installation of a fallback on - an alternative <c>Mnesia</c> directory, that directory - is fully prepared for use as an active <c>Mnesia</c> + an alternative Mnesia directory, that directory + is fully prepared for use as an active Mnesia directory.</p> <p>This is a dangerous feature that must be used with care. By unintentional mixing of directories, @@ -1509,7 +1509,7 @@ mnesia:create_table(person, <marker id="load_textfile"></marker> <p>Loads a series of definitions and data found in the text file (generated with <c>mnesia:dump_to_textfile/1</c>) - into <c>Mnesia</c>. This function also starts <c>Mnesia</c> + into Mnesia. This function also starts Mnesia and possibly creates a new schema. This function is intended for educational purposes only. It is recommended to use other functions to deal with real backups.</p> @@ -1558,7 +1558,7 @@ mnesia:create_table(person, </taglist> <p>Conflicting lock requests are automatically queued if there is no risk of a deadlock. Otherwise the transaction must be - terminated and executed again. <c>Mnesia</c> does this + terminated and executed again. Mnesia does this automatically as long as the upper limit of the maximum <c>retries</c> is not reached. For details, see <c>mnesia:transaction/3</c>.</p> @@ -1726,19 +1726,19 @@ mnesia:create_table(person, </func> <func> <name>report_event(Event) -> ok</name> - <fsummary>Reports a user event to the <c>Mnesia</c> event handler.</fsummary> + <fsummary>Reports a user event to the Mnesia event handler.</fsummary> <desc> <marker id="report_event"></marker> - <p>When tracing a system of <c>Mnesia</c> applications it is - useful to be able to interleave <c>Mnesia</c> own events with + <p>When tracing a system of Mnesia applications it is + useful to be able to interleave Mnesia own events with application-related events that give information about the application context.</p> <p>Whenever the application begins a - new and demanding <c>Mnesia</c> task, or if it enters a new + new and demanding Mnesia task, or if it enters a new interesting phase in its execution, it can be a good idea to use <c>mnesia:report_event/1</c>. <c>Event</c> can be any term and generates a <c>{mnesia_user, Event}</c> event - for any processes that subscribe to <c>Mnesia</c> system + for any processes that subscribe to Mnesia system events.</p> </desc> </func> @@ -1748,7 +1748,7 @@ mnesia:create_table(person, <desc> <marker id="restore"></marker> <p>With this function, tables can be restored online from a - backup without restarting <c>Mnesia</c>. + backup without restarting Mnesia. <c>Opaque</c> is forwarded to the backup module. <c>Args</c> is a list of the following tuples:</p> <list type="bulleted"> @@ -1873,7 +1873,7 @@ mnesia:create_table(person, <p>For a complete description of <c>select</c>, see the <seealso marker="erts:index">ERTS</seealso> User's Guide and the <seealso marker="stdlib:ets">ets</seealso> manual page in - <c>STDLIB</c>.</p> + STDLIB.</p> <p>For example, to find the names of all male persons older than 30 in table <c>Tab</c>:</p> <code type="none"> @@ -1920,10 +1920,10 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </func> <func> <name>set_debug_level(Level) -> OldLevel</name> - <fsummary>Changes the internal debug level of <c>Mnesia</c>.</fsummary> + <fsummary>Changes the internal debug level of Mnesia.</fsummary> <desc> <marker id="set_debug_level"></marker> - <p>Changes the internal debug level of <c>Mnesia</c>. + <p>Changes the internal debug level of Mnesia. For details, see <seealso marker="#configuration_parameters">Section Configuration Parameters</seealso>.</p> @@ -1934,7 +1934,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> <fsummary>Sets the master nodes for all tables.</fsummary> <desc> <marker id="set_master_nodes_1"></marker> - <p>For each table <c>Mnesia</c> determines its replica nodes + <p>For each table Mnesia determines its replica nodes (<c>TabNodes</c>) and starts <c>mnesia:set_master_nodes(Tab, TabMasterNodes)</c>. where <c>TabMasterNodes</c> is the intersection of @@ -1952,16 +1952,16 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> that can have caused an inconsistent database, it can use the function <c>mnesia:set_master_nodes(Tab, MasterNodes)</c> to define from which nodes each table is to be loaded. - At startup, the <c>Mnesia</c> normal table load algorithm is + At startup, the Mnesia normal table load algorithm is bypassed and the table is loaded from one of the master nodes - defined for the table, regardless of when and if <c>Mnesia</c> + defined for the table, regardless of when and if Mnesia terminated on other nodes. <c>MasterNodes</c> can only contain nodes where the table has a replica. If the <c>MasterNodes</c> list is empty, the master node recovery mechanism for the particular table is reset, and the normal load mechanism is used at the next restart.</p> <p>The master node setting is always local. It can be - changed regardless if <c>Mnesia</c> is started or not.</p> + changed regardless if Mnesia is started or not.</p> <p>The database can also become inconsistent if configuration parameter <c>max_wait_for_decision</c> is used or if <c>mnesia:force_load_table/1</c> is used.</p> @@ -1976,7 +1976,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </func> <func> <name>snmp_get_mnesia_key(Tab, RowIndex) -> {ok, Key} | undefined</name> - <fsummary>Gets the corresponding <c>Mnesia</c> key from an SNMP index.</fsummary> + <fsummary>Gets the corresponding Mnesia key from an SNMP index.</fsummary> <type> <v>Tab ::= atom()</v> <v>RowIndex ::= [integer()]</v> @@ -1984,7 +1984,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> <v>key() ::= integer() | string() | [integer()]</v> </type> <desc> - <p>Transforms an SNMP index to the corresponding <c>Mnesia</c> + <p>Transforms an SNMP index to the corresponding Mnesia key. If the SNMP table has multiple keys, the key is a tuple of the key columns.</p> </desc> @@ -2020,7 +2020,7 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </func> <func> <name>snmp_open_table(Tab, SnmpStruct) -> {aborted, R} | {atomic, ok}</name> - <fsummary>Organizes a <c>Mnesia</c> table as an SNMP table.</fsummary> + <fsummary>Organizes a Mnesia table as an SNMP table.</fsummary> <type> <v>Tab ::= atom()</v> <v>SnmpStruct ::= [{key, type()}]</v> @@ -2029,14 +2029,14 @@ mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),</code> </type> <desc> <p>A direct one-to-one mapping can be established between - <c>Mnesia</c> tables and SNMP tables. Many telecommunication + Mnesia tables and SNMP tables. Many telecommunication applications are controlled and monitored by the SNMP - protocol. This connection between <c>Mnesia</c> and SNMP + protocol. This connection between Mnesia and SNMP makes it simple and convenient to achieve this mapping.</p> <p>Argument <c>SnmpStruct</c> is a list of SNMP information. Currently, the only information needed is information about the key types in the table. Multiple - keys cannot be handled in <c>Mnesia</c>, but many SNMP + keys cannot be handled in Mnesia, but many SNMP tables have multiple keys. Therefore, the following convention is used: if a table has multiple keys, these must always be stored as a tuple of the keys. Information about @@ -2069,39 +2069,39 @@ mnesia:create_table(employee, <p>When a table is SNMP ordered, modifications are more expensive than usual, O(logN). Also, more memory is used.</p> <p>Notice that only the lexicographical SNMP ordering is - implemented in <c>Mnesia</c>, not the actual SNMP monitoring.</p> + implemented in Mnesia, not the actual SNMP monitoring.</p> </desc> </func> <func> <name>start() -> ok | {error, Reason}</name> - <fsummary>Starts a local <c>Mnesia</c> system.</fsummary> + <fsummary>Starts a local Mnesia system.</fsummary> <desc> <marker id="start"></marker> - <p>The startup procedure for a set of <c>Mnesia</c> nodes is a - fairly complicated operation. A <c>Mnesia</c> system consists - of a set of nodes, with <c>Mnesia</c> started locally on all + <p>The startup procedure for a set of Mnesia nodes is a + fairly complicated operation. A Mnesia system consists + of a set of nodes, with Mnesia started locally on all participating nodes. Normally, each node has a directory where - all the <c>Mnesia</c> files are written. This directory is - referred to as the <c>Mnesia</c> directory. <c>Mnesia</c> can + all the Mnesia files are written. This directory is + referred to as the Mnesia directory. Mnesia can also be started on disc-less nodes. For more information about disc-less nodes, see <c>mnesia:create_schema/1</c> and the User's Guide.</p> - <p>The set of nodes that makes up a <c>Mnesia</c> system is kept - in a schema. <c>Mnesia</c> nodes can be added to or removed + <p>The set of nodes that makes up a Mnesia system is kept + in a schema. Mnesia nodes can be added to or removed from the schema. The initial schema is normally created on disc with the function <c>mnesia:create_schema/1</c>. On disc-less nodes, a tiny default schema is generated each time - <c>Mnesia</c> is started. During the startup procedure, - <c>Mnesia</c> exchanges schema information between the nodes + Mnesia is started. During the startup procedure, + Mnesia exchanges schema information between the nodes to verify that the table definitions are compatible.</p> <p>Each schema has a unique cookie, which can be regarded as a unique schema identifier. The cookie must be the same on all - nodes where <c>Mnesia</c> is supposed to run. For details, + nodes where Mnesia is supposed to run. For details, see the User's Guide.</p> - <p>The schema file and all other files that <c>Mnesia</c> - needs are kept in the <c>Mnesia</c> directory. The + <p>The schema file and all other files that Mnesia + needs are kept in the Mnesia directory. The command-line option <c>-mnesia dir Dir</c> can be used to - specify the location of this directory to the <c>Mnesia</c> + specify the location of this directory to the Mnesia system. If no such command-line option is found, the name of the directory defaults to <c>Mnesia.Node</c>.</p> <p><c>application:start(mnesia)</c> can also be used.</p> @@ -2109,10 +2109,10 @@ mnesia:create_table(employee, </func> <func> <name>stop() -> stopped</name> - <fsummary>Stops <c>Mnesia</c> locally.</fsummary> + <fsummary>Stops Mnesia locally.</fsummary> <desc> <marker id="stop"></marker> - <p>Stops <c>Mnesia</c> locally on the current node.</p> + <p>Stops Mnesia locally on the current node.</p> <p><c>application:stop(mnesia)</c> can also be used.</p> </desc> </func> @@ -2132,7 +2132,7 @@ mnesia:create_table(employee, <desc> <marker id="sync_dirty"></marker> <p>Calls the <c>Fun</c> in a context that is not protected by - a transaction. The <c>Mnesia</c> function calls performed in + a transaction. The Mnesia function calls performed in the <c>Fun</c> are mapped to the corresponding dirty functions. It is performed in almost the same context as <c>mnesia:async_dirty/1,2</c>. The difference is that the @@ -2167,10 +2167,10 @@ mnesia:create_table(employee, </func> <func> <name>system_info(InfoKey) -> Info | exit({aborted, Reason})</name> - <fsummary>Returns information about the <c>Mnesia</c> system.</fsummary> + <fsummary>Returns information about the Mnesia system.</fsummary> <desc> <marker id="system_info"></marker> - <p>Returns information about the <c>Mnesia</c> system, such as + <p>Returns information about the Mnesia system, such as transaction statistics, <c>db_nodes</c>, and configuration parameters. The valid keys are as follows:</p> <list type="bulleted"> @@ -2188,7 +2188,7 @@ mnesia:create_table(employee, </item> <item> <p><c>auto_repair</c>. Returns <c>true</c> or <c>false</c> - to indicate if <c>Mnesia</c> is configured to start the + to indicate if Mnesia is configured to start the auto-repair facility on corrupted disc files.</p> </item> <item> @@ -2209,20 +2209,20 @@ mnesia:create_table(employee, in the list of nodes if they explicitly have been added to the schema, for example, with <c>mnesia:add_table_copy/3</c>. The function can be - started even if <c>Mnesia</c> is not yet running.</p> + started even if Mnesia is not yet running.</p> </item> <item> <p><c>debug</c>. Returns the current debug level of - <c>Mnesia</c>.</p> + Mnesia.</p> </item> <item> - <p><c>directory</c>. Returns the name of the <c>Mnesia</c> - directory. It can be called even if <c>Mnesia</c> is + <p><c>directory</c>. Returns the name of the Mnesia + directory. It can be called even if Mnesia is not yet running.</p> </item> <item> <p><c>dump_log_load_regulation</c>. Returns a boolean that - tells if <c>Mnesia</c> is configured to regulate the + tells if Mnesia is configured to regulate the dumper process load.</p> <p>This feature is temporary and will be removed in future releases.</p> @@ -2233,9 +2233,9 @@ mnesia:create_table(employee, </item> <item> <p><c>dump_log_update_in_place</c>. Returns a boolean that - tells if <c>Mnesia</c> is configured to perform the - updates in the <c>dets</c> files directly, or if the - updates are to be performed in a copy of the <c>dets</c> + tells if Mnesia is configured to perform the + updates in the Dets files directly, or if the + updates are to be performed in a copy of the Dets files.</p> </item> <item> @@ -2253,13 +2253,13 @@ mnesia:create_table(employee, </item> <item> <p><c>held_locks</c>. Returns a list of all - locks held by the local <c>Mnesia</c> lock manager.</p> + locks held by the local Mnesia lock manager.</p> </item> <item> <p><c>is_running</c>. Returns <c>yes</c> or <c>no</c> to - indicate if <c>Mnesia</c> is running. It can + indicate if Mnesia is running. It can also return <c>starting</c> or <c>stopping</c>. Can be - called even if <c>Mnesia</c> is not yet running.</p> + called even if Mnesia is not yet running.</p> </item> <item> <p><c>local_tables</c>. Returns a list @@ -2272,7 +2272,7 @@ mnesia:create_table(employee, </item> <item> <p><c>log_version</c>. Returns the version - number of the <c>Mnesia</c> transaction log format.</p> + number of the Mnesia transaction log format.</p> </item> <item> <p><c>master_node_tables</c>. Returns a @@ -2280,25 +2280,25 @@ mnesia:create_table(employee, </item> <item> <p><c>protocol_version</c>. Returns the version number of - the <c>Mnesia</c> inter-process communication protocol.</p> + the Mnesia inter-process communication protocol.</p> </item> <item> <p><c>running_db_nodes</c>. Returns a list of nodes where - <c>Mnesia</c> currently is running. This function can be - called even if <c>Mnesia</c> is not yet running, but it + Mnesia currently is running. This function can be + called even if Mnesia is not yet running, but it then has slightly different semantics.</p> - <p>If <c>Mnesia</c> is down on the local node, the function + <p>If Mnesia is down on the local node, the function returns those other <c>db_nodes</c> and <c>extra_db_nodes</c> that for the moment are operational.</p> - <p>If <c>Mnesia</c> is started, the function returns - those nodes that <c>Mnesia</c> on the local node is fully - connected to. Only those nodes that <c>Mnesia</c> has + <p>If Mnesia is started, the function returns + those nodes that Mnesia on the local node is fully + connected to. Only those nodes that Mnesia has exchanged schema information with are included as <c>running_db_nodes</c>. After the merge of schemas, the - local <c>Mnesia</c> system is fully operable and + local Mnesia system is fully operable and applications can perform access of remote replicas. - Before the schema merge, <c>Mnesia</c> only operates + Before the schema merge, Mnesia only operates locally. Sometimes there are more nodes included in the <c>running_db_nodes</c> list than all <c>db_nodes</c> and <c>extra_db_nodes</c> together.</p> @@ -2322,17 +2322,17 @@ mnesia:create_table(employee, <item> <p><c>transaction_failures</c>. Returns a number that indicates how many transactions have - failed since <c>Mnesia</c> was started.</p> + failed since Mnesia was started.</p> </item> <item> <p><c>transaction_commits</c>. Returns a number that indicates how many transactions have - terminated successfully since <c>Mnesia</c> was started.</p> + terminated successfully since Mnesia was started.</p> </item> <item> <p><c>transaction_restarts</c>. Returns a number that indicates how many transactions have been - restarted since <c>Mnesia</c> was started.</p> + restarted since Mnesia was started.</p> </item> <item> <p><c>transaction_log_writes</c>. @@ -2342,12 +2342,12 @@ mnesia:create_table(employee, </item> <item> <p><c>use_dir</c>. Returns a boolean that indicates if - the <c>Mnesia</c> directory is used or not. Can be - started even if <c>Mnesia</c> is not yet running.</p> + the Mnesia directory is used or not. Can be + started even if Mnesia is not yet running.</p> </item> <item> <p><c>version</c>. Returns the current - version number of <c>Mnesia</c>.</p> + version number of Mnesia.</p> </item> </list> </desc> @@ -2356,17 +2356,16 @@ mnesia:create_table(employee, <name>table(Tab [,[Option]]) -> QueryHandle</name> <fsummary>Return a QLC query handle.</fsummary> <desc> - <marker id="qlc_table"></marker> <marker id="table"></marker> <p>Returns a Query List Comprehension (QLC) query handle, see the <seealso marker="stdlib:qlc">qlc(3)</seealso> - manual page in <c>STDLIB</c>. The module <c>qlc</c> - implements a query language that can use <c>Mnesia</c> + manual page in STDLIB. The module <c>qlc</c> + implements a query language that can use Mnesia tables as sources of data. Calling <c>mnesia:table/1,2</c> is the means to make the <c>mnesia</c> table <c>Tab</c> usable to QLC.</p> - <p><c>Option</c> can contain <c>Mnesia</c> - options or QLC options. <c>Mnesia</c> recognizes the + <p><c>Option</c> can contain Mnesia + options or QLC options. Mnesia recognizes the following options (any other option is forwarded to QLC).</p> <list type="bulleted"> @@ -2375,7 +2374,7 @@ mnesia:create_table(employee, </item> <item><c>{n_objects,Number}</c>, where <c>n_objects</c> specifies (roughly) the number of objects returned - from <c>Mnesia</c> to QLC. Queries to remote tables + from Mnesia to QLC. Queries to remote tables can need a larger chunk to reduce network overhead. By default, <c>100</c> objects at a time are returned. </item> @@ -2414,7 +2413,7 @@ mnesia:create_table(employee, <desc> <marker id="table_info"></marker> <p>The <c>table_info/2</c> function takes two arguments. - The first is the name of a <c>Mnesia</c> table. + The first is the name of a Mnesia table. The second is one of the following keys:</p> <list type="bulleted"> <item> @@ -2466,7 +2465,7 @@ mnesia:create_table(employee, </item> <item> <p><c>load_node</c>. Returns the name of - the node that <c>Mnesia</c> loaded the table from. The + the node that Mnesia loaded the table from. The structure of the returned value is unspecified, but can be useful for debugging purposes.</p> </item> @@ -2477,7 +2476,7 @@ mnesia:create_table(employee, </item> <item> <p><c>load_reason</c>. Returns the - reason of why <c>Mnesia</c> decided to load the table. + reason of why Mnesia decided to load the table. The structure of the returned value is unspecified, but can be useful for debugging purposes.</p> </item> @@ -2621,7 +2620,7 @@ raise(Name, Amount) -> several processes running on different nodes can concurrently execute the function <c>raise/2</c> without interfering with each other.</p> - <p>Since <c>Mnesia</c> detects deadlocks, a transaction can be + <p>Since Mnesia detects deadlocks, a transaction can be restarted any number of times. This function attempts a restart as specified in <c>Retries</c>. <c>Retries</c> must be an integer greater than 0 or the atom <c>infinity</c>. @@ -2644,7 +2643,7 @@ raise(Name, Amount) -> <p><c>NewAttributeList</c> and <c>NewRecordName</c> specify the attributes and the new record type of the converted table. Table name always remains unchanged. If - <c>record_name</c> is changed, only the <c>Mnesia</c> + <c>record_name</c> is changed, only the Mnesia functions that use table identifiers work, for example, <c>mnesia:write/3</c> works, but not <c>mnesia:write/1</c>.</p> </desc> @@ -2707,7 +2706,7 @@ raise(Name, Amount) -> distributed operation that is either performed on all nodes with disc resident schema, or none. Uninstallation of fallbacks requires Erlang to be operational on all - involved nodes, but it does not matter if <c>Mnesia</c> is + involved nodes, but it does not matter if Mnesia is running or not. Which nodes that are considered as disc-resident nodes is determined from the schema information in the local fallback.</p> @@ -2793,28 +2792,28 @@ raise(Name, Amount) -> <section> <title>Configuration Parameters</title> <marker id="configuration_parameters"></marker> - <p><c>Mnesia</c> reads the following application configuration + <p>Mnesia reads the following application configuration parameters:</p> <list type="bulleted"> <item> <p><c>-mnesia access_module Module</c>. The name of the - <c>Mnesia</c> activity access callback module. Default is + Mnesia activity access callback module. Default is <c>mnesia</c>.</p> </item> <item> <p><c>-mnesia auto_repair true | false</c>. This flag - controls if <c>Mnesia</c> automatically tries to repair + controls if Mnesia automatically tries to repair files that have not been properly closed. Default is <c>true</c>.</p> </item> <item> <p><c>-mnesia backup_module Module</c>. The name of the - <c>Mnesia</c> backup callback module. Default is + Mnesia backup callback module. Default is <c>mnesia_backup</c>.</p> </item> <item> <p><c>-mnesia debug Level</c>. Controls the debug level - of <c>Mnesia</c>. The possible values are as follows:</p> + of Mnesia. The possible values are as follows:</p> <taglist> <tag><c>none</c></tag> <item> @@ -2826,7 +2825,7 @@ raise(Name, Amount) -> events generate <c>{mnesia_info, Format, Args}</c> system events. Processes can subscribe to these events with <c>mnesia:subscribe/1</c>. The events are always sent to - the <c>Mnesia</c> event handler.</p> + the Mnesia event handler.</p> </item> <tag><c>debug</c></tag> <item> @@ -2835,15 +2834,15 @@ raise(Name, Amount) -> <c>{mnesia_info, Format, Args}</c> system events. Processes can subscribe to these events with <c>mnesia:subscribe/1</c>. The events are always sent to - the <c>Mnesia</c> event handler. On this debug level, - the <c>Mnesia</c> event handler starts subscribing to + the Mnesia event handler. On this debug level, + the Mnesia event handler starts subscribing to updates in the schema table.</p> </item> <tag><c>trace</c></tag> <item> <p>Activates all events at the debug level. On this - level, the <c>Mnesia</c> event handler starts subscribing - to updates on all <c>Mnesia</c> tables. This level is + level, the Mnesia event handler starts subscribing + to updates on all Mnesia tables. This level is intended only for debugging small toy systems, as many large events can be generated.</p> </item> @@ -2857,7 +2856,7 @@ raise(Name, Amount) -> </item> <item> <p><c>-mnesia core_dir Directory</c>. The name of the - directory where <c>Mnesia</c> core files is stored, or + directory where Mnesia core files is stored, or false. Setting it implies that also RAM-only nodes generate a core file if a crash occurs.</p> </item> @@ -2871,9 +2870,9 @@ raise(Name, Amount) -> </item> <item> <p><c>-mnesia dir Directory</c>. The name of the directory - where all <c>Mnesia</c> data is stored. The directory name + where all Mnesia data is stored. The directory name must be unique for the current node. Two nodes must never - share the the same <c>Mnesia</c> directory. The results + share the the same Mnesia directory. The results are unpredictable.</p> </item> <item> @@ -2916,44 +2915,44 @@ raise(Name, Amount) -> </item> <item> <p><c>-mnesia event_module Module</c>. The name of the - <c>Mnesia</c> event handler callback module. Default is + Mnesia event handler callback module. Default is <c>mnesia_event</c>.</p> </item> <item> <p><c>-mnesia extra_db_nodes Nodes</c> specifies a list of nodes, in addition to the ones found in the schema, with - which <c>Mnesia</c> is also to establish contact. Default + which Mnesia is also to establish contact. Default is <c>[]</c> (empty list).</p> </item> <item> <p><c>-mnesia fallback_error_function {UserModule, UserFunc}</c>. Specifies a user-supplied callback function, which is - called if a fallback is installed and <c>Mnesia</c> goes - down on another node. <c>Mnesia</c> calls the function + called if a fallback is installed and Mnesia goes + down on another node. Mnesia calls the function with one argument, the name of the dying node, for example, - <c>UserModule:UserFunc(DyingNode)</c>. <c>Mnesia</c> must + <c>UserModule:UserFunc(DyingNode)</c>. Mnesia must be restarted, otherwise the database can be inconsistent. - The default behavior is to terminate <c>Mnesia</c>.</p> + The default behavior is to terminate Mnesia.</p> </item> <item> <p><c>-mnesia max_wait_for_decision Timeout</c>. Specifies - how long <c>Mnesia</c> waits for other nodes to share their + how long Mnesia waits for other nodes to share their knowledge about the outcome of an unclear transaction. By default, <c>Timeout</c> is set to the atom <c>infinity</c>. - This implies that if <c>Mnesia</c> upon startup detects + This implies that if Mnesia upon startup detects a "heavyweight transaction" whose outcome is unclear, the - local <c>Mnesia</c> waits until <c>Mnesia</c> is started + local Mnesia waits until Mnesia is started on some (in the worst case all) of the other nodes that were involved in the interrupted transaction. This is a rare - situation, but if it occurs, <c>Mnesia</c> does not guess if + situation, but if it occurs, Mnesia does not guess if the transaction on the other nodes was committed or - terminated. <c>Mnesia</c> waits until it knows the outcome + terminated. Mnesia waits until it knows the outcome and then acts accordingly.</p> <p>If <c>Timeout</c> is set to an integer value in - milliseconds, <c>Mnesia</c> forces "heavyweight transactions" + milliseconds, Mnesia forces "heavyweight transactions" to be finished, even if the outcome of the transaction for the moment is unclear. After <c>Timeout</c> milliseconds, - <c>Mnesia</c> commits or terminates the transaction and + Mnesia commits or terminates the transaction and continues with the startup. This can lead to a situation where the transaction is committed on some nodes and terminated on other nodes. If the transaction is a @@ -2977,14 +2976,14 @@ raise(Name, Amount) -> </item> <item> <p><c>-mnesia schema_location Loc</c>. Controls where - <c>Mnesia</c> looks for its schema. Parameter + Mnesia looks for its schema. Parameter <c>Loc</c> can be one of the following atoms:</p> <taglist> <tag><c>disc</c></tag> <item> <p>Mandatory disc. The schema is assumed to be located - in the <c>Mnesia</c> directory. If the schema cannot - be found, <c>Mnesia</c> refuses to start. This is the + in the Mnesia directory. If the schema cannot + be found, Mnesia refuses to start. This is the old behavior.</p> </item> <tag><c>ram</c></tag> @@ -3002,10 +3001,10 @@ raise(Name, Amount) -> <tag><c>opt_disc</c></tag> <item> <p>Optional disc. The schema can reside on disc or in - RAM. If the schema is found on disc, <c>Mnesia</c> + RAM. If the schema is found on disc, Mnesia starts as a disc-based node and the storage type of the schema table is <c>disc_copies</c>. If no schema is - found on disc, <c>Mnesia</c> starts as a disc-less node + found on disc, Mnesia starts as a disc-less node and the storage type of the schema table is <c>ram_copies</c>. Default value for the application parameter is <c>opt_disc</c>.</p> @@ -3013,7 +3012,7 @@ raise(Name, Amount) -> </taglist> </item> </list> - <p>First, the <c>SASL</c> application parameters are checked, + <p>First, the SASL application parameters are checked, then the command-line flags are checked, and finally, the default value is chosen.</p> </section> diff --git a/lib/mnesia/doc/src/mnesia_frag_hash.xml b/lib/mnesia/doc/src/mnesia_frag_hash.xml index 95f5f8aa07..51b32129b6 100644 --- a/lib/mnesia/doc/src/mnesia_frag_hash.xml +++ b/lib/mnesia/doc/src/mnesia_frag_hash.xml @@ -87,13 +87,13 @@ the new one.</p> <p><c>NewState</c> is stored as <c>hash_state</c> among the other <c>frag_properties</c>.</p> - <p>As a part of the <c>add_frag</c> procedure, <c>Mnesia</c> iterates + <p>As a part of the <c>add_frag</c> procedure, Mnesia iterates over all fragments corresponding to the <c>IterFrags</c> numbers and starts <c>key_to_frag_number(NewState,RecordKey)</c> for each record. If the new fragment differs from the old fragment, the record is moved to the new fragment.</p> <p>As the <c>add_frag</c> procedure is a part of a schema - transaction, <c>Mnesia</c> acquires write locks on the + transaction, Mnesia acquires write locks on the affected tables. That is, both the fragments corresponding to <c>IterFrags</c> and those corresponding to <c>AdditionalLockFrags</c>.</p> @@ -112,7 +112,7 @@ <desc> <p><c>NewState</c> is stored as <c>hash_state</c> among the other <c>frag_properties</c>.</p> - <p>As a part of the <c>del_frag</c> procedure, <c>Mnesia</c> iterates + <p>As a part of the <c>del_frag</c> procedure, Mnesia iterates over all fragments corresponding to the <c>IterFrags</c> numbers and starts <c>key_to_frag_number(NewState,RecordKey)</c> for each record. If the new fragment differs from the old @@ -120,7 +120,7 @@ <p>Notice that all records in the last fragment must be moved to another fragment, as the entire fragment is deleted.</p> <p>As the <c>del_frag</c> procedure is a part of a schema - transaction, <c>Mnesia</c> acquires write locks on the + transaction, Mnesia acquires write locks on the affected tables. That is, both the fragments corresponding to <c>IterFrags</c> and those corresponding to <c>AdditionalLockFrags</c>.</p> @@ -134,7 +134,7 @@ <v>Reason = term()</v> </type> <desc> - <p>Starts whenever <c>Mnesia</c> needs to determine + <p>Starts whenever Mnesia needs to determine which fragment a certain record belongs to. It is typically started at <c>read</c>, <c>write</c>, and <c>delete</c>.</p> </desc> @@ -149,7 +149,7 @@ <v>Reason = term()</v> </type> <desc> - <p>This function is called whenever <c>Mnesia</c> needs to determine + <p>This function is called whenever Mnesia needs to determine which fragments that need to be searched for a <c>MatchSpec</c>. It is typically called by <c>select</c> and <c>match_object</c>.</p> diff --git a/lib/mnesia/doc/src/mnesia_registry.xml b/lib/mnesia/doc/src/mnesia_registry.xml index cd778ae072..a76f716981 100644 --- a/lib/mnesia/doc/src/mnesia_registry.xml +++ b/lib/mnesia/doc/src/mnesia_registry.xml @@ -38,20 +38,20 @@ <modulesummary>Dump support for registries in erl_interface.</modulesummary> <description> <p>This module is usually part of the <c>erl_interface</c> - application, but is currently part of the <c>Mnesia</c> + application, but is currently part of the Mnesia application.</p> <p>This module is mainly intended for internal use within OTP, but it has two functions that are exported for public use.</p> <p>On C-nodes, <c>erl_interface</c> has support for registry tables. These tables reside in RAM on the C-node, but can also - be dumped into <c>Mnesia</c> tables. By default, the dumping + be dumped into Mnesia tables. By default, the dumping of registry tables through <c>erl_interface</c> causes a - corresponding <c>Mnesia</c> table to be created with + corresponding Mnesia table to be created with <c>mnesia_registry:create_table/1</c>, if necessary.</p> <p>Tables that are created with these functions can be - administered as all other <c>Mnesia</c> tables. They can be + administered as all other Mnesia tables. They can be included in backups, replicas can be added, and so on. - The tables are normal <c>Mnesia</c> tables owned by the user + The tables are normal Mnesia tables owned by the user of the corresponding <c>erl_interface</c> registries.</p> </description> @@ -68,7 +68,7 @@ that is, <c>{ram_copies,[node()]}</c> or <c>{disc_copies,[node()]}</c>.</p> <p>This function is used by <c>erl_interface</c> to - create the <c>Mnesia</c> table if it does not already + create the Mnesia table if it does not already exist.</p> </desc> </func> diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml index 4a68e76d50..9f59759cb6 100644 --- a/lib/mnesia/doc/src/notes.xml +++ b/lib/mnesia/doc/src/notes.xml @@ -39,7 +39,90 @@ thus constitutes one section in this document. The title of each section is the version number of Mnesia.</p> - <section><title>Mnesia 4.13.4</title> + <section><title>Mnesia 4.14.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed crash in checkpoint handling when table was deleted + during backup.</p> + <p> + Own Id: OTP-14167</p> + </item> + </list> + </section> + +</section> + +<section><title>Mnesia 4.14.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A continuation returned by mnesia:select/[14] should be + reusable in different, non-transactional activities.</p> + <p> + Own Id: OTP-13944 Aux Id: PR-1184 </p> + </item> + <item> + <p> + Fixed crash when calling block_table multiple times. + Could happen when having locks for a long time and + restarting mnesia.</p> + <p> + Own Id: OTP-13970 Aux Id: Seq-13198 </p> + </item> + <item> + <p> + Change mnesia_tm process to have off-heap messages since + mnesia_tm can be the receiver of many non-synchronized + message from other nodes.</p> + <p> + Own Id: OTP-14074</p> + </item> + </list> + </section> + +</section> + +<section><title>Mnesia 4.14.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> Correct some minor documentation issues. </p> + <p> + Own Id: OTP-13891</p> + </item> + </list> + </section> + +</section> + +<section><title>Mnesia 4.14</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Added experimental external backend plugin api. This adds + the possibility for the user to write other storage + backends for data, for example by using shared memory or + ram-cached disk storage.</p> + <p> + The plugin api may change in future versions after being + battle tested.</p> + <p> + Own Id: OTP-13058</p> + </item> + </list> + </section> + +</section> + +<section><title>Mnesia 4.13.4</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/mnesia/doc/src/part.xml b/lib/mnesia/doc/src/part.xml index 101bdb29d4..d3ffe93937 100644 --- a/lib/mnesia/doc/src/part.xml +++ b/lib/mnesia/doc/src/part.xml @@ -30,7 +30,7 @@ <file>part.sgml</file> </header> <description> - <p>The <c>Mnesia</c> application is a distributed Database Management + <p>The Mnesia application is a distributed Database Management System (DBMS), appropriate for telecommunications applications and other Erlang applications, which require continuous operation and exhibit soft real-time properties.</p> diff --git a/lib/mnesia/doc/src/ref_man.xml b/lib/mnesia/doc/src/ref_man.xml index 662f0d61d6..7fb71b9c45 100644 --- a/lib/mnesia/doc/src/ref_man.xml +++ b/lib/mnesia/doc/src/ref_man.xml @@ -33,7 +33,7 @@ <file>refman.sgml</file> </header> <description> - <p>The <c>Mnesia</c> application is a distributed Database Management + <p>The Mnesia application is a distributed Database Management System (DBMS), appropriate for telecommunications applications and other Erlang applications, which require continuous operation and exhibit soft real-time properties.</p> diff --git a/lib/mnesia/src/Makefile b/lib/mnesia/src/Makefile index 08a00e6aba..b68fc7d3d0 100644 --- a/lib/mnesia/src/Makefile +++ b/lib/mnesia/src/Makefile @@ -43,6 +43,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/mnesia-$(VSN) # ---------------------------------------------------- MODULES= \ mnesia \ + mnesia_app \ mnesia_backend_type \ mnesia_backup \ mnesia_bup \ @@ -54,7 +55,6 @@ MODULES= \ mnesia_ext_sup \ mnesia_frag \ mnesia_frag_hash \ - mnesia_frag_old_hash \ mnesia_index \ mnesia_kernel_sup \ mnesia_late_loader \ diff --git a/lib/mnesia/src/mnesia.app.src b/lib/mnesia/src/mnesia.app.src index 006ad4bac1..a5d74d2d36 100644 --- a/lib/mnesia/src/mnesia.app.src +++ b/lib/mnesia/src/mnesia.app.src @@ -3,6 +3,7 @@ {vsn, "%VSN%"}, {modules, [ mnesia, + mnesia_app, mnesia_backend_type, mnesia_backup, mnesia_bup, @@ -14,7 +15,6 @@ mnesia_ext_sup, mnesia_frag, mnesia_frag_hash, - mnesia_frag_old_hash, mnesia_index, mnesia_kernel_sup, mnesia_late_loader, @@ -49,7 +49,5 @@ mnesia_tm ]}, {applications, [kernel, stdlib]}, - {mod, {mnesia_sup, []}}, + {mod, {mnesia_app, []}}, {runtime_dependencies, ["stdlib-2.0","kernel-3.0","erts-7.0"]}]}. - - diff --git a/lib/mnesia/src/mnesia.erl b/lib/mnesia/src/mnesia.erl index 9586adbf93..dece995d39 100644 --- a/lib/mnesia/src/mnesia.erl +++ b/lib/mnesia/src/mnesia.erl @@ -138,6 +138,36 @@ -include("mnesia.hrl"). -import(mnesia_lib, [verbose/2]). +-type create_option() :: + {'access_mode', 'read_write' | 'read_only'} | + {'attributes', [atom()]} | + {'disc_copies', [node()]} | + {'disc_only_copies', [node]} | + {'index', [index_attr()]} | + {'load_order', non_neg_integer()} | + {'majority', boolean()} | + {'ram_copies', [node()]} | + {'record_name', atom()} | + {'snmp', SnmpStruct::term()} | + {'storage_properties', [{Backend::module(), [BackendProp::_]}]} | + {'type', 'set' | 'ordered_set' | 'bag'} | + {'local_content', boolean()}. + +-type t_result(Res) :: {'atomic', Res} | {'aborted', Reason::term()}. +-type activity() :: 'ets' | 'async_dirty' | 'sync_dirty' | 'transaction' | 'sync_transaction' | + {'transaction', Retries::non_neg_integer()} | + {'sync_transaction', Retries::non_neg_integer()}. +-type table() :: atom(). +-type storage_type() :: 'ram_copies' | 'disc_copies' | 'disc_only_copies'. +-type index_attr() :: atom() | non_neg_integer(). +-type write_locks() :: 'write' | 'sticky_write'. +-type read_locks() :: 'read'. +-type lock_kind() :: write_locks() | read_locks(). +-type select_continuation() :: term(). +-type snmp_struct() :: [{atom(), snmp_type() | tuple_of(snmp_type())}]. +-type snmp_type() :: 'fix_string' | 'string' | 'integer'. +-type tuple_of(_) :: tuple(). + -define(DEFAULT_ACCESS, ?MODULE). %% Select @@ -196,7 +226,7 @@ e_has_var(X, Pos) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Start and stop - +-spec start() -> 'ok' | {'error', term()}. start() -> start([]). @@ -216,6 +246,7 @@ start_() -> {error, R} end. +-spec start([{Option::atom(), Value::_}]) -> 'ok' | {'error', term()}. start(ExtraEnv) when is_list(ExtraEnv) -> case mnesia_lib:ensure_loaded(?APPLICATION) of ok -> @@ -238,6 +269,7 @@ patched_start([Head | _]) -> patched_start([]) -> start_(). +-spec stop() -> 'stopped' | {'error', term()}. stop() -> case application:stop(?APPLICATION) of ok -> stopped; @@ -245,6 +277,7 @@ stop() -> Other -> Other end. +-spec change_config(Config::atom(), Value::_) -> ok | {error, term()}. change_config(extra_db_nodes, Ns) when is_list(Ns) -> mnesia_controller:connect_nodes(Ns); change_config(dc_dump_limit, N) when is_number(N), N > 0 -> @@ -273,6 +306,7 @@ kill() -> ms() -> [ mnesia, + mnesia_app, mnesia_backup, mnesia_bup, mnesia_checkpoint, @@ -282,7 +316,6 @@ ms() -> mnesia_loader, mnesia_frag, mnesia_frag_hash, - mnesia_frag_old_hash, mnesia_index, mnesia_kernel_sup, mnesia_late_loader, @@ -311,12 +344,12 @@ ms() -> %% Activity mgt -spec abort(_) -> no_return(). - abort(Reason = {aborted, _}) -> exit(Reason); abort(Reason) -> exit({aborted, Reason}). +-spec is_transaction() -> boolean(). is_transaction() -> case get(mnesia_activity_state) of {_, Tid, _Ts} when element(1,Tid) == tid -> @@ -325,29 +358,52 @@ is_transaction() -> false end. +-spec transaction(Fun) -> t_result(Res) when + Fun :: fun(() -> Res). transaction(Fun) -> transaction(get(mnesia_activity_state), Fun, [], infinity, ?DEFAULT_ACCESS, async). + +-spec transaction(Fun, Retries) -> t_result(Res) when + Fun :: fun(() -> Res), + Retries :: non_neg_integer() | 'infinity'; + (Fun, [Arg::_]) -> t_result(Res) when + Fun :: fun((...) -> Res). transaction(Fun, Retries) when is_integer(Retries), Retries >= 0 -> transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, async); transaction(Fun, Retries) when Retries == infinity -> transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, async); transaction(Fun, Args) -> transaction(get(mnesia_activity_state), Fun, Args, infinity, ?DEFAULT_ACCESS, async). + +-spec transaction(Fun, [Arg::_], Retries) -> t_result(Res) when + Fun :: fun((...) -> Res), + Retries :: non_neg_integer() | 'infinity'. transaction(Fun, Args, Retries) -> transaction(get(mnesia_activity_state), Fun, Args, Retries, ?DEFAULT_ACCESS, async). +-spec sync_transaction(Fun) -> t_result(Res) when + Fun :: fun(() -> Res). sync_transaction(Fun) -> transaction(get(mnesia_activity_state), Fun, [], infinity, ?DEFAULT_ACCESS, sync). + +-spec sync_transaction(Fun, Retries) -> t_result(Res) when + Fun :: fun(() -> Res), + Retries :: non_neg_integer() | 'infinity'; + (Fun, [Arg::_]) -> t_result(Res) when + Fun :: fun((...) -> Res). sync_transaction(Fun, Retries) when is_integer(Retries), Retries >= 0 -> transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, sync); sync_transaction(Fun, Retries) when Retries == infinity -> transaction(get(mnesia_activity_state), Fun, [], Retries, ?DEFAULT_ACCESS, sync); sync_transaction(Fun, Args) -> transaction(get(mnesia_activity_state), Fun, Args, infinity, ?DEFAULT_ACCESS, sync). + +-spec sync_transaction(Fun, [Arg::_], Retries) -> t_result(Res) when + Fun :: fun((...) -> Res), + Retries :: non_neg_integer() | 'infinity'. sync_transaction(Fun, Args, Retries) -> transaction(get(mnesia_activity_state), Fun, Args, Retries, ?DEFAULT_ACCESS, sync). - transaction(State, Fun, Args, Retries, Mod, Kind) when is_function(Fun), is_list(Args), Retries == infinity, is_atom(Mod) -> mnesia_tm:transaction(State, Fun, Args, Retries, Mod, Kind); @@ -363,28 +419,60 @@ non_transaction(State, Fun, Args, ActivityKind, Mod) non_transaction(_State, Fun, Args, _ActivityKind, _Mod) -> {aborted, {badarg, Fun, Args}}. +-spec async_dirty(Fun) -> Res | no_return() when + Fun :: fun(() -> Res). async_dirty(Fun) -> async_dirty(Fun, []). + +-spec async_dirty(Fun, [Arg::_]) -> Res | no_return() when + Fun :: fun((...) -> Res). async_dirty(Fun, Args) -> non_transaction(get(mnesia_activity_state), Fun, Args, async_dirty, ?DEFAULT_ACCESS). +-spec sync_dirty(Fun) -> Res | no_return() when + Fun :: fun(() -> Res). sync_dirty(Fun) -> sync_dirty(Fun, []). + +-spec sync_dirty(Fun, [Arg::_]) -> Res | no_return() when + Fun :: fun((...) -> Res). sync_dirty(Fun, Args) -> non_transaction(get(mnesia_activity_state), Fun, Args, sync_dirty, ?DEFAULT_ACCESS). +-spec ets(Fun) -> Res | no_return() when + Fun :: fun(() -> Res). ets(Fun) -> ets(Fun, []). + +-spec ets(Fun, [Arg::_]) -> Res | no_return() when + Fun :: fun((...) -> Res). ets(Fun, Args) -> non_transaction(get(mnesia_activity_state), Fun, Args, ets, ?DEFAULT_ACCESS). +-spec activity(Kind, Fun) -> t_result(Res) | Res when + Kind :: activity(), + Fun :: fun(() -> Res). activity(Kind, Fun) -> activity(Kind, Fun, []). + +-spec activity(Kind, Fun, [Arg::_]) -> t_result(Res) | Res when + Kind :: activity(), + Fun :: fun((...) -> Res); + (Kind, Fun, Mod) -> t_result(Res) | Res when + Kind :: activity(), + Fun :: fun(() -> Res), + Mod :: atom(). + activity(Kind, Fun, Args) when is_list(Args) -> activity(Kind, Fun, Args, mnesia_monitor:get_env(access_module)); activity(Kind, Fun, Mod) -> activity(Kind, Fun, [], Mod). +-spec activity(Kind, Fun, [Arg::_], Mod) -> t_result(Res) | Res when + Kind :: activity(), + Fun :: fun((...) -> Res), + Mod :: atom(). + activity(Kind, Fun, Args, Mod) -> State = get(mnesia_activity_state), case Kind of @@ -414,7 +502,11 @@ wrap_trans(State, Fun, Args, Retries, Mod, Kind) -> %% Nodes may either be a list of nodes or one node as an atom %% Mnesia on all Nodes must be connected to each other, but %% it is not neccessary that they are up and running. - +-spec lock(LockItem, LockKind) -> list() | tuple() | no_return() when + LockItem :: {'record', table(), Key::term()} | + {'table', table()} | + {'global', Key::term(), MnesiaNodes::[node()]}, + LockKind :: lock_kind() | 'load'. lock(LockItem, LockKind) -> case get(mnesia_activity_state) of {?DEFAULT_ACCESS, Tid, Ts} -> @@ -425,6 +517,9 @@ lock(LockItem, LockKind) -> abort(no_transaction) end. +-spec lock_table(Tab::table(), LockKind) -> [MnesiaNode] | no_return() when + MnesiaNode :: node(), + LockKind :: lock_kind() | load. lock_table(Tab, LockKind) -> lock({table, Tab}, LockKind). @@ -446,11 +541,13 @@ lock(Tid, Ts, LockItem, LockKind) -> end. %% Grab a read lock on a whole table +-spec read_lock_table(Tab::table()) -> ok. read_lock_table(Tab) -> lock({table, Tab}, read), ok. %% Grab a write lock on a whole table +-spec write_lock_table(Tab::table()) -> ok. write_lock_table(Tab) -> lock({table, Tab}, write), ok. @@ -515,17 +612,19 @@ good_global_nodes(Nodes) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Access within an activity - updates - +-spec write(Record::tuple()) -> 'ok'. write(Val) when is_tuple(Val), tuple_size(Val) > 2 -> Tab = element(1, Val), write(Tab, Val, write); write(Val) -> abort({bad_type, Val}). +-spec s_write(Record::tuple()) -> 'ok'. s_write(Val) when is_tuple(Val), tuple_size(Val) > 2 -> Tab = element(1, Val), write(Tab, Val, sticky_write). +-spec write(Tab::table(), Record::tuple(), LockKind::write_locks()) -> 'ok'. write(Tab, Val, LockKind) -> case get(mnesia_activity_state) of {?DEFAULT_ACCESS, Tid, Ts} -> @@ -572,16 +671,19 @@ write_to_store(Tab, Store, Oid, Val) -> end, ok. +-spec delete({Tab::table(), Key::_}) -> 'ok'. delete({Tab, Key}) -> delete(Tab, Key, write); delete(Oid) -> abort({bad_type, Oid}). +-spec s_delete({Tab::table(), Key::_}) -> 'ok'. s_delete({Tab, Key}) -> delete(Tab, Key, sticky_write); s_delete(Oid) -> abort({bad_type, Oid}). +-spec delete(Tab::table(), Key::_, LockKind::write_locks()) -> 'ok'. delete(Tab, Key, LockKind) -> case get(mnesia_activity_state) of {?DEFAULT_ACCESS, Tid, Ts} -> @@ -618,18 +720,21 @@ delete(Tid, Ts, Tab, Key, LockKind) delete(_Tid, _Ts, Tab, _Key, _LockKind) -> abort({bad_type, Tab}). +-spec delete_object(Rec::tuple()) -> 'ok'. delete_object(Val) when is_tuple(Val), tuple_size(Val) > 2 -> Tab = element(1, Val), delete_object(Tab, Val, write); delete_object(Val) -> abort({bad_type, Val}). +-spec s_delete_object(Rec::tuple()) -> 'ok'. s_delete_object(Val) when is_tuple(Val), tuple_size(Val) > 2 -> Tab = element(1, Val), delete_object(Tab, Val, sticky_write); s_delete_object(Val) -> abort({bad_type, Val}). +-spec delete_object(Tab::table(), Rec::tuple(), LockKind::write_locks()) -> 'ok'. delete_object(Tab, Val, LockKind) -> case get(mnesia_activity_state) of {?DEFAULT_ACCESS, Tid, Ts} -> @@ -689,19 +794,23 @@ do_delete_object(Tid, Ts, Tab, Val, LockKind) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Access within an activity - read +-spec read(Tab::table(), Key::_) -> [tuple()]. read(Tab, Key) -> read(Tab, Key, read). +-spec read({Tab::table(), Key::_}) -> [tuple()]. read({Tab, Key}) -> read(Tab, Key, read); read(Oid) -> abort({bad_type, Oid}). +-spec wread({Tab::table(), Key::_}) -> [tuple()]. wread({Tab, Key}) -> read(Tab, Key, write); wread(Oid) -> abort({bad_type, Oid}). +-spec read(Tab::table(), Key::_, LockKind::lock_kind()) -> [tuple()]. read(Tab, Key, LockKind) -> case get(mnesia_activity_state) of {?DEFAULT_ACCESS, Tid, Ts} -> @@ -738,6 +847,7 @@ read(Tid, Ts, Tab, Key, LockKind) read(_Tid, _Ts, Tab, _Key, _LockKind) -> abort({bad_type, Tab}). +-spec first(Tab::table()) -> Key::term(). first(Tab) -> case get(mnesia_activity_state) of {?DEFAULT_ACCESS, Tid, Ts} -> @@ -765,6 +875,7 @@ first(Tid, Ts, Tab) first(_Tid, _Ts,Tab) -> abort({bad_type, Tab}). +-spec last(Tab::table()) -> Key::term(). last(Tab) -> case get(mnesia_activity_state) of {?DEFAULT_ACCESS, Tid, Ts} -> @@ -792,6 +903,7 @@ last(Tid, Ts, Tab) last(_Tid, _Ts,Tab) -> abort({bad_type, Tab}). +-spec next(Tab::table(), Key::term()) -> NextKey::term(). next(Tab,Key) -> case get(mnesia_activity_state) of {?DEFAULT_ACCESS,Tid,Ts} -> @@ -818,6 +930,7 @@ next(Tid,Ts,Tab,Key) next(_Tid, _Ts,Tab,_) -> abort({bad_type, Tab}). +-spec prev(Tab::table(), Key::term()) -> PrevKey::term(). prev(Tab,Key) -> case get(mnesia_activity_state) of {?DEFAULT_ACCESS,Tid,Ts} -> @@ -952,6 +1065,8 @@ ts_keys_1([], Acc) -> %%%%%%%%%%%%%%%%%%%%% %% Iterators +-spec foldl(Fun, Acc0, Tab::table()) -> Acc when + Fun::fun((Record::tuple(), Acc0) -> Acc). foldl(Fun, Acc, Tab) -> foldl(Fun, Acc, Tab, read). @@ -992,6 +1107,8 @@ do_foldl(A, O, Tab, Key, Fun, Acc, Type, Stored) -> %% Type is set or bag {_, Tid, Ts} = get(mnesia_activity_state), do_foldl(Tid, Ts, Tab, dirty_next(Tab, Key), Fun, NewAcc, Type, NewStored). +-spec foldr(Fun, Acc0, Tab::table()) -> Acc when + Fun::fun((Record::tuple(), Acc0) -> Acc). foldr(Fun, Acc, Tab) -> foldr(Fun, Acc, Tab, read). foldr(Fun, Acc, Tab, LockKind) when is_function(Fun) -> @@ -1105,12 +1222,15 @@ add_written_to_bag([{_, _ , delete} | Tail], _Objs, _Ack) -> add_written_to_bag([{_, Val, delete_object} | Tail], Objs, Ack) -> add_written_to_bag(Tail, lists:delete(Val, Objs), lists:delete(Val, Ack)). +-spec match_object(Pattern::tuple()) -> [Record::tuple()]. match_object(Pat) when is_tuple(Pat), tuple_size(Pat) > 2 -> Tab = element(1, Pat), match_object(Tab, Pat, read); match_object(Pat) -> abort({bad_type, Pat}). +-spec match_object(Tab,Pattern,LockKind) -> [Record] when + Tab::table(),Pattern::tuple(),LockKind::lock_kind(),Record::tuple(). match_object(Tab, Pat, LockKind) -> case get(mnesia_activity_state) of {?DEFAULT_ACCESS, Tid, Ts} -> @@ -1270,9 +1390,13 @@ deloid(Oid, [H | T]) -> %%%%%%%%%%%%%%%%%% % select - +-spec select(Tab, Spec) -> [Match] when + Tab::table(), Spec::ets:match_spec(), Match::term(). select(Tab, Pat) -> select(Tab, Pat, read). +-spec select(Tab, Spec, LockKind) -> [Match] when + Tab::table(), Spec::ets:match_spec(), + Match::term(),LockKind::lock_kind(). select(Tab, Pat, LockKind) when is_atom(Tab), Tab /= schema, is_list(Pat) -> case get(mnesia_activity_state) of @@ -1331,6 +1455,11 @@ select_lock(Tid,Ts,LockKind,Spec,Tab) -> end. %% Breakable Select +-spec select(Tab, Spec, N, LockKind) -> {[Match], Cont} | '$end_of_table' when + Tab::table(), Spec::ets:match_spec(), + Match::term(), N::non_neg_integer(), + LockKind::lock_kind(), + Cont::select_continuation(). select(Tab, Pat, NObjects, LockKind) when is_atom(Tab), Tab /= schema, is_list(Pat), is_integer(NObjects) -> case get(mnesia_activity_state) of @@ -1387,6 +1516,9 @@ fun_select(Tid, Ts, Tab, Spec, LockKind, TabPat, Init, NObjects, Node, Storage) select_state(Init(Spec),Def) end. +-spec select(Cont) -> {[Match], Cont} | '$end_of_table' when + Match::term(), + Cont::select_continuation(). select(Cont) -> case get(mnesia_activity_state) of {?DEFAULT_ACCESS, Tid, Ts} -> @@ -1409,8 +1541,14 @@ select_cont(Tid,_,State=#mnesia_select{tid=Tid,written=[]}) -> select_state(dirty_sel_cont(State),State); select_cont(Tid,_Ts,State=#mnesia_select{tid=Tid}) -> trans_select(dirty_sel_cont(State), State); -select_cont(_Tid2,_,#mnesia_select{tid=_Tid1}) -> % Missmatching tids +select_cont(Tid2,_,#mnesia_select{tid=_Tid1}) + when element(1,Tid2) == tid -> % Mismatching tids abort(wrong_transaction); +select_cont(Tid,Ts,State=#mnesia_select{}) -> + % Repair mismatching tids in non-transactional contexts + RepairedState = State#mnesia_select{tid = Tid, written = [], + spec = undefined, type = undefined}, + select_cont(Tid,Ts,RepairedState); select_cont(_,_,Cont) -> abort({badarg, Cont}). @@ -1430,6 +1568,7 @@ get_record_pattern([]) -> []; get_record_pattern([{M,C,_B}|R]) -> [{M,C,['$_']} | get_record_pattern(R)]. +-spec all_keys(Tab::table()) -> [Key::term()]. all_keys(Tab) -> case get(mnesia_activity_state) of {?DEFAULT_ACCESS, Tid, Ts} -> @@ -1454,12 +1593,20 @@ all_keys(Tid, Ts, Tab, LockKind) all_keys(_Tid, _Ts, Tab, _LockKind) -> abort({bad_type, Tab}). +-spec index_match_object(Pattern, Attr) -> [Record] when + Pattern::tuple(), Attr::index_attr(), Record::tuple(). index_match_object(Pat, Attr) when is_tuple(Pat), tuple_size(Pat) > 2 -> Tab = element(1, Pat), index_match_object(Tab, Pat, Attr, read); index_match_object(Pat, _Attr) -> abort({bad_type, Pat}). +-spec index_match_object(Tab, Pattern, Attr, LockKind) -> [Record] when + Tab::table(), + Pattern::tuple(), + Attr::index_attr(), + LockKind::lock_kind(), + Record::tuple(). index_match_object(Tab, Pat, Attr, LockKind) -> case get(mnesia_activity_state) of {?DEFAULT_ACCESS, Tid, Ts} -> @@ -1496,6 +1643,11 @@ index_match_object(Tid, Ts, Tab, Pat, Attr, LockKind) index_match_object(_Tid, _Ts, Tab, Pat, _Attr, _LockKind) -> abort({bad_type, Tab, Pat}). +-spec index_read(Tab, Key, Attr) -> [Record] when + Tab::table(), + Key::term(), + Attr::index_attr(), + Record::tuple(). index_read(Tab, Key, Attr) -> case get(mnesia_activity_state) of {?DEFAULT_ACCESS, Tid, Ts} -> @@ -1535,13 +1687,14 @@ index_read(_Tid, _Ts, Tab, _Key, _Attr, _LockKind) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Dirty access regardless of activities - updates - +-spec dirty_write(Record::tuple()) -> 'ok'. dirty_write(Val) when is_tuple(Val), tuple_size(Val) > 2 -> Tab = element(1, Val), dirty_write(Tab, Val); dirty_write(Val) -> abort({bad_type, Val}). +-spec dirty_write(Tab::table(), Record::tuple()) -> 'ok'. dirty_write(Tab, Val) -> do_dirty_write(async_dirty, Tab, Val). @@ -1553,11 +1706,13 @@ do_dirty_write(SyncMode, Tab, Val) do_dirty_write(_SyncMode, Tab, Val) -> abort({bad_type, Tab, Val}). +-spec dirty_delete({Tab::table(), Key::_}) -> 'ok'. dirty_delete({Tab, Key}) -> dirty_delete(Tab, Key); dirty_delete(Oid) -> abort({bad_type, Oid}). +-spec dirty_delete(Tab::table(), Key::_) -> 'ok'. dirty_delete(Tab, Key) -> do_dirty_delete(async_dirty, Tab, Key). @@ -1567,12 +1722,14 @@ do_dirty_delete(SyncMode, Tab, Key) when is_atom(Tab), Tab /= schema -> do_dirty_delete(_SyncMode, Tab, _Key) -> abort({bad_type, Tab}). +-spec dirty_delete_object(Record::tuple()) -> 'ok'. dirty_delete_object(Val) when is_tuple(Val), tuple_size(Val) > 2 -> Tab = element(1, Val), dirty_delete_object(Tab, Val); dirty_delete_object(Val) -> abort({bad_type, Val}). +-spec dirty_delete_object(Tab::table(), Record::tuple()) -> 'ok'. dirty_delete_object(Tab, Val) -> do_dirty_delete_object(async_dirty, Tab, Val). @@ -1590,12 +1747,15 @@ do_dirty_delete_object(_SyncMode, Tab, Val) -> abort({bad_type, Tab, Val}). %% A Counter is an Oid being {CounterTab, CounterName} - +-spec dirty_update_counter({Tab::table(), Key::_}, Incr::integer()) -> + NewVal::integer(). dirty_update_counter({Tab, Key}, Incr) -> dirty_update_counter(Tab, Key, Incr); dirty_update_counter(Counter, _Incr) -> abort({bad_type, Counter}). +-spec dirty_update_counter(Tab::table(), Key::_, Incr::integer()) -> + NewVal::integer(). dirty_update_counter(Tab, Key, Incr) -> do_dirty_update_counter(async_dirty, Tab, Key, Incr). @@ -1614,23 +1774,28 @@ do_dirty_update_counter(_SyncMode, Tab, _Key, Incr) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Dirty access regardless of activities - read +-spec dirty_read({Tab::table(), Key::_}) -> [tuple()]. dirty_read({Tab, Key}) -> dirty_read(Tab, Key); dirty_read(Oid) -> abort({bad_type, Oid}). +-spec dirty_read(Tab::table(), Key::_) -> [tuple()]. dirty_read(Tab, Key) when is_atom(Tab), Tab /= schema -> dirty_rpc(Tab, mnesia_lib, db_get, [Tab, Key]); dirty_read(Tab, _Key) -> abort({bad_type, Tab}). +-spec dirty_match_object(Pattern::tuple()) -> [Record::tuple()]. dirty_match_object(Pat) when is_tuple(Pat), tuple_size(Pat) > 2 -> Tab = element(1, Pat), dirty_match_object(Tab, Pat); dirty_match_object(Pat) -> abort({bad_type, Pat}). +-spec dirty_match_object(Tab,Pattern) -> [Record] when + Tab::table(), Pattern::tuple(), Record::tuple(). dirty_match_object(Tab, Pat) when is_atom(Tab), Tab /= schema, is_tuple(Pat), tuple_size(Pat) > 2 -> dirty_rpc(Tab, ?MODULE, remote_dirty_match_object, [Tab, Pat]); @@ -1660,6 +1825,8 @@ remote_dirty_match_object(Tab, Pat, []) -> remote_dirty_match_object(Tab, Pat, _PosList) -> abort({bad_type, Tab, Pat}). +-spec dirty_select(Tab, Spec) -> [Match] when + Tab::table(), Spec::ets:match_spec(), Match::term(). dirty_select(Tab, Spec) when is_atom(Tab), Tab /= schema, is_list(Spec) -> dirty_rpc(Tab, ?MODULE, remote_dirty_select, [Tab, Spec]); dirty_select(Tab, Spec) -> @@ -1708,6 +1875,7 @@ dirty_sel_cont(#mnesia_select{cont='$end_of_table'}) -> '$end_of_table'; dirty_sel_cont(#mnesia_select{node=Node,tab=Tab,storage=Type,cont=Cont,orig=Ms}) -> do_dirty_rpc(Tab,Node,mnesia_lib,db_select_cont,[Type,Cont,Ms]). +-spec dirty_all_keys(Tab::table()) -> [Key::term()]. dirty_all_keys(Tab) when is_atom(Tab), Tab /= schema -> case ?catch_val({Tab, wild_pattern}) of {'EXIT', _} -> @@ -1723,12 +1891,19 @@ dirty_all_keys(Tab) when is_atom(Tab), Tab /= schema -> dirty_all_keys(Tab) -> abort({bad_type, Tab}). +-spec dirty_index_match_object(Pattern, Attr) -> [Record] when + Pattern::tuple(), Attr::index_attr(), Record::tuple(). dirty_index_match_object(Pat, Attr) when is_tuple(Pat), tuple_size(Pat) > 2 -> Tab = element(1, Pat), dirty_index_match_object(Tab, Pat, Attr); dirty_index_match_object(Pat, _Attr) -> abort({bad_type, Pat}). +-spec dirty_index_match_object(Tab, Pattern, Attr) -> [Record] when + Tab::table(), + Pattern::tuple(), + Attr::index_attr(), + Record::tuple(). dirty_index_match_object(Tab, Pat, Attr) when is_atom(Tab), Tab /= schema, is_tuple(Pat), tuple_size(Pat) > 2 -> case mnesia_schema:attr_tab_to_pos(Tab, Attr) of @@ -1752,6 +1927,11 @@ dirty_index_match_object(Tab, Pat, Attr) dirty_index_match_object(Tab, Pat, _Attr) -> abort({bad_type, Tab, Pat}). +-spec dirty_index_read(Tab, Key, Attr) -> [Record] when + Tab::table(), + Key::term(), + Attr::index_attr(), + Record::tuple(). dirty_index_read(Tab, Key, Attr) when is_atom(Tab), Tab /= schema -> Pos = mnesia_schema:attr_tab_to_pos(Tab, Attr), case has_var(Key) of @@ -1763,26 +1943,31 @@ dirty_index_read(Tab, Key, Attr) when is_atom(Tab), Tab /= schema -> dirty_index_read(Tab, _Key, _Attr) -> abort({bad_type, Tab}). +%% do not use only for backwards compatibility dirty_slot(Tab, Slot) when is_atom(Tab), Tab /= schema, is_integer(Slot) -> dirty_rpc(Tab, mnesia_lib, db_slot, [Tab, Slot]); dirty_slot(Tab, Slot) -> abort({bad_type, Tab, Slot}). +-spec dirty_first(Tab::table()) -> Key::term(). dirty_first(Tab) when is_atom(Tab), Tab /= schema -> dirty_rpc(Tab, mnesia_lib, db_first, [Tab]); dirty_first(Tab) -> abort({bad_type, Tab}). +-spec dirty_last(Tab::table()) -> Key::term(). dirty_last(Tab) when is_atom(Tab), Tab /= schema -> dirty_rpc(Tab, mnesia_lib, db_last, [Tab]); dirty_last(Tab) -> abort({bad_type, Tab}). +-spec dirty_next(Tab::table(), Key::_) -> NextKey::term(). dirty_next(Tab, Key) when is_atom(Tab), Tab /= schema -> dirty_rpc(Tab, mnesia_lib, db_next_key, [Tab, Key]); dirty_next(Tab, _Key) -> abort({bad_type, Tab}). +-spec dirty_prev(Tab::table(), Key::_) -> PrevKey::term(). dirty_prev(Tab, Key) when is_atom(Tab), Tab /= schema -> dirty_rpc(Tab, mnesia_lib, db_prev_key, [Tab, Key]); dirty_prev(Tab, _Key) -> @@ -1833,7 +2018,7 @@ do_dirty_rpc(Tab, Node, M, F, Args) -> %% Info %% Info about one table --spec table_info(atom(), any()) -> any(). +-spec table_info(Tab::table(), Item::term()) -> Info::term(). table_info(Tab, Item) -> case get(mnesia_activity_state) of undefined -> @@ -1876,9 +2061,10 @@ any_table_info(Tab, Item) when is_atom(Tab) -> [] -> abort({no_exists, Tab, Item}); Props -> - lists:map(fun({setorbag, Type}) -> {type, Type}; - (Prop) -> Prop end, - Props) + Rename = fun ({setorbag, Type}) -> {type, Type}; + (Prop) -> Prop + end, + lists:sort(lists:map(Rename, Props)) end; name -> Tab; @@ -1921,16 +2107,20 @@ bad_info_reply(_Tab, memory) -> 0; bad_info_reply(Tab, Item) -> abort({no_exists, Tab, Item}). %% Raw info about all tables +-spec schema() -> ok. schema() -> mnesia_schema:info(). %% Raw info about one tables +-spec schema(Tab::table()) -> ok. schema(Tab) -> mnesia_schema:info(Tab). +-spec error_description(Error::term()) -> string(). error_description(Err) -> mnesia_lib:error_desc(Err). +-spec info() -> ok. info() -> case mnesia_lib:is_running() of yes -> @@ -2056,6 +2246,7 @@ display_tab_info() -> Rdisp = fun({Rpat, Rtabs}) -> io:format("~p = ~p~n", [Rpat, Rtabs]) end, lists:foreach(Rdisp, lists:sort(Repl)). +-spec get_backend_types() -> [BackendType::term()]. get_backend_types() -> case ?catch_val({schema, user_property, mnesia_backend_types}) of {'EXIT', _} -> @@ -2064,6 +2255,7 @@ get_backend_types() -> lists:sort(Ts) end. +-spec get_index_plugins() -> [IndexPlugins::term()]. get_index_plugins() -> case ?catch_val({schema, user_property, mnesia_index_plugins}) of {'EXIT', _} -> @@ -2112,6 +2304,7 @@ storage_count(T, {U, R, D, DO, Ext}) -> {ext, A, _} -> {U, R, D, DO, orddict:append(A, T, Ext)} end. +-spec system_info(Iterm::term()) -> Info::term(). system_info(Item) -> try system_info2(Item) catch _:Error -> abort(Error) @@ -2361,83 +2554,134 @@ load_mnesia_or_abort() -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Database mgt +-spec create_schema(Ns::[node()]) -> 'ok' | {'error', Reason::term()}. create_schema(Ns) -> create_schema(Ns, []). +-spec create_schema(Ns::[node()], [Prop]) -> 'ok' | {'error', Reason::term()} when + Prop :: BackendType | IndexPlugin, + BackendType :: {backend_types, [{Name::atom(), Module::module()}]}, + IndexPlugin :: {index_plugins, [{{Name::atom()}, Module::module(), Function::atom()}]}. create_schema(Ns, Properties) -> mnesia_bup:create_schema(Ns, Properties). +-spec delete_schema(Ns::[node()]) -> 'ok' | {'error', Reason::term()}. delete_schema(Ns) -> mnesia_schema:delete_schema(Ns). +-spec add_backend_type(Name::atom(), Module::module()) -> t_result('ok'). add_backend_type(Alias, Module) -> mnesia_schema:add_backend_type(Alias, Module). +-spec backup(Dest::term()) -> 'ok' | {'error', Reason::term()}. backup(Opaque) -> mnesia_log:backup(Opaque). +-spec backup(Dest::term(), Mod::module()) -> + 'ok' | {'error', Reason::term()}. backup(Opaque, Mod) -> mnesia_log:backup(Opaque, Mod). +-spec traverse_backup(Src::term(), Dest::term(), Fun, Acc) -> + {'ok', Acc} | {'error', Reason::term()} when + Fun :: fun((Items, Acc) -> {Items,Acc}). traverse_backup(S, T, Fun, Acc) -> mnesia_bup:traverse_backup(S, T, Fun, Acc). +-spec traverse_backup(Src::term(), SrcMod::module(), + Dest::term(), DestMod::module(), + Fun, Acc) -> + {'ok', Acc} | {'error', Reason::term()} when + Fun :: fun((Items, Acc) -> {Items,Acc}). traverse_backup(S, SM, T, TM, F, A) -> mnesia_bup:traverse_backup(S, SM, T, TM, F, A). +-spec install_fallback(Src::term()) -> 'ok' | {'error', Reason::term()}. install_fallback(Opaque) -> mnesia_bup:install_fallback(Opaque). +-spec install_fallback(Src::term(), Mod::module()|[Opt]) -> + 'ok' | {'error', Reason::term()} when + Opt :: Module | Scope | Dir, + Module :: {'module', Mod::module()}, + Scope :: {'scope', 'global' | 'local'}, + Dir :: {'mnesia_dir', Dir::string()}. install_fallback(Opaque, Mod) -> mnesia_bup:install_fallback(Opaque, Mod). +-spec uninstall_fallback() -> 'ok' | {'error', Reason::term()}. uninstall_fallback() -> mnesia_bup:uninstall_fallback(). +-spec uninstall_fallback(Args) -> 'ok' | {'error', Reason::term()} when + Args :: [{'mnesia_dir', Dir::string()}]. uninstall_fallback(Args) -> mnesia_bup:uninstall_fallback(Args). +-spec activate_checkpoint([Arg]) -> {'ok', Name, [node()]} | {'error', Reason::term()} when + Arg :: {'name', Name} | {'max', [table()]} | {'min', [table()]} | + {'allow_remote', boolean()} | {'ram_overrides_dump', boolean()}. activate_checkpoint(Args) -> mnesia_checkpoint:activate(Args). +-spec deactivate_checkpoint(Name::_) -> 'ok' | {'error', Reason::term()}. deactivate_checkpoint(Name) -> mnesia_checkpoint:deactivate(Name). +-spec backup_checkpoint(Name::_, Dest::_) -> 'ok' | {'error', Reason::term()}. backup_checkpoint(Name, Opaque) -> mnesia_log:backup_checkpoint(Name, Opaque). +-spec backup_checkpoint(Name::_, Dest::_, Mod::module()) -> + 'ok' | {'error', Reason::term()}. backup_checkpoint(Name, Opaque, Mod) -> mnesia_log:backup_checkpoint(Name, Opaque, Mod). +-spec restore(Src::_, [Arg]) -> t_result([table()]) when + Op :: 'skip_tables' | 'clear_tables' | 'keep_tables' | 'restore_tables', + Arg :: {'module', module()} | {Op, [table()]} | {'default_op', Op}. restore(Opaque, Args) -> mnesia_schema:restore(Opaque, Args). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Table mgt - +-spec create_table([Arg]) -> t_result('ok') when + Arg :: {'name', table()} | create_option(). create_table(Arg) -> mnesia_schema:create_table(Arg). + +-spec create_table(Name::table(), [create_option()]) -> t_result('ok'). create_table(Name, Arg) when is_list(Arg) -> mnesia_schema:create_table([{name, Name}| Arg]); create_table(Name, Arg) -> {aborted, badarg, Name, Arg}. +-spec delete_table(Tab::table()) -> t_result('ok'). delete_table(Tab) -> mnesia_schema:delete_table(Tab). +-spec add_table_copy(Tab::table(), N::node(), ST::storage_type()) -> t_result(ok). add_table_copy(Tab, N, S) -> mnesia_schema:add_table_copy(Tab, N, S). + +-spec del_table_copy(Tab::table(), N::node()) -> t_result(ok). del_table_copy(Tab, N) -> mnesia_schema:del_table_copy(Tab, N). +-spec move_table_copy(Tab::table(), From::node(), To::node()) -> t_result(ok). move_table_copy(Tab, From, To) -> mnesia_schema:move_table(Tab, From, To). +-spec add_table_index(Tab::table(), I::index_attr()) -> t_result(ok). add_table_index(Tab, Ix) -> mnesia_schema:add_table_index(Tab, Ix). +-spec del_table_index(Tab::table(), I::index_attr()) -> t_result(ok). del_table_index(Tab, Ix) -> mnesia_schema:del_table_index(Tab, Ix). +-spec transform_table(Tab::table(), Fun, [Attr]) -> t_result(ok) when + Attr :: atom(), + Fun:: fun((Record::tuple()) -> Transformed::tuple()). transform_table(Tab, Fun, NewA) -> try val({Tab, record_name}) of OldRN -> mnesia_schema:transform_table(Tab, Fun, NewA, OldRN) @@ -2445,12 +2689,18 @@ transform_table(Tab, Fun, NewA) -> mnesia:abort(Reason) end. +-spec transform_table(Tab::table(), Fun, [Attr], RecName) -> t_result(ok) when + RecName :: atom(), + Attr :: atom(), + Fun:: fun((Record::tuple()) -> Transformed::tuple()). transform_table(Tab, Fun, NewA, NewRN) -> mnesia_schema:transform_table(Tab, Fun, NewA, NewRN). +-spec change_table_copy_type(Tab::table(), Node::node(), To::storage_type()) -> t_result(ok). change_table_copy_type(T, N, S) -> mnesia_schema:change_table_copy_type(T, N, S). +-spec clear_table(Tab::table()) -> t_result(ok). clear_table(Tab) -> case get(mnesia_activity_state) of State = {Mod, Tid, _Ts} when element(1, Tid) =/= tid -> @@ -2480,19 +2730,22 @@ clear_table(Tid, Ts, Tab, Obj) when element(1, Tid) =:= tid -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Table mgt - user properties - +-spec read_table_property(Tab::table(), PropKey::term()) -> Res::tuple(). read_table_property(Tab, PropKey) -> val({Tab, user_property, PropKey}). +-spec write_table_property(Tab::table(), Prop::tuple()) -> t_result(ok). write_table_property(Tab, Prop) -> mnesia_schema:write_table_property(Tab, Prop). +-spec delete_table_property(Tab::table(), PropKey::term()) -> t_result(ok). delete_table_property(Tab, PropKey) -> mnesia_schema:delete_table_property(Tab, PropKey). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Table mgt - user properties +-spec change_table_frag(Tab::table(), FP::term()) -> t_result(ok). change_table_frag(Tab, FragProp) -> mnesia_schema:change_table_frag(Tab, FragProp). @@ -2500,28 +2753,38 @@ change_table_frag(Tab, FragProp) -> %% Table mgt - table load %% Dump a ram table to disc +-spec dump_tables([Tab::table()]) -> t_result(ok). dump_tables(Tabs) -> mnesia_schema:dump_tables(Tabs). %% allow the user to wait for some tables to be loaded +-spec wait_for_tables([Tab::table()], TMO::timeout()) -> + 'ok' | {'timeout', [table()]} | {'error', Reason::term()}. wait_for_tables(Tabs, Timeout) -> mnesia_controller:wait_for_tables(Tabs, Timeout). +-spec force_load_table(Tab::table()) -> 'yes' | {'error', Reason::term()}. force_load_table(Tab) -> case mnesia_controller:force_load_table(Tab) of ok -> yes; % Backwards compatibility Other -> Other end. +-spec change_table_access_mode(Tab::table(), Mode) -> t_result(ok) when + Mode :: 'read_only'|'read_write'. change_table_access_mode(T, Access) -> mnesia_schema:change_table_access_mode(T, Access). +-spec change_table_load_order(Tab::table(), Order) -> t_result(ok) when + Order :: non_neg_integer(). change_table_load_order(T, O) -> mnesia_schema:change_table_load_order(T, O). +-spec change_table_majority(Tab::table(), M::boolean()) -> t_result(ok). change_table_majority(T, M) -> mnesia_schema:change_table_majority(T, M). +-spec set_master_nodes(Ns::[node()]) -> 'ok' | {'error', Reason::term()}. set_master_nodes(Nodes) when is_list(Nodes) -> UseDir = system_info(use_dir), IsRunning = system_info(is_running), @@ -2560,6 +2823,8 @@ log_valid_master_nodes(Cstructs, Nodes, UseDir, IsRunning) -> Args = lists:map(Fun, Cstructs), mnesia_recover:log_master_nodes(Args, UseDir, IsRunning). +-spec set_master_nodes(Tab::table(), Ns::[node()]) -> + 'ok' | {'error', Reason::term()}. set_master_nodes(Tab, Nodes) when is_list(Nodes) -> UseDir = system_info(use_dir), IsRunning = system_info(is_running), @@ -2610,31 +2875,39 @@ set_master_nodes(Tab, Nodes) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Misc admin - +-spec dump_log() -> 'dumped'. dump_log() -> mnesia_controller:sync_dump_log(user). +-spec sync_log() -> 'ok' | {'error', Reason::term()}. sync_log() -> mnesia_monitor:sync_log(latest_log). +-spec subscribe(What) -> {'ok', node()} | {'error', Reason::term()} when + What :: 'system' | 'activity' | {'table', table(), 'simple' | 'detailed'}. subscribe(What) -> mnesia_subscr:subscribe(self(), What). +-spec unsubscribe(What) -> {'ok', node()} | {'error', Reason::term()} when + What :: 'system' | 'activity' | {'table', table(), 'simple' | 'detailed'}. unsubscribe(What) -> mnesia_subscr:unsubscribe(self(), What). +-spec report_event(Event::_) -> 'ok'. report_event(Event) -> mnesia_lib:report_system_event({mnesia_user, Event}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Snmp - +-spec snmp_open_table(Tab::table(), Snmp::snmp_struct()) -> ok. snmp_open_table(Tab, Us) -> mnesia_schema:add_snmp(Tab, Us). +-spec snmp_close_table(Tab::table()) -> ok. snmp_close_table(Tab) -> mnesia_schema:del_snmp(Tab). +-spec snmp_get_row(Tab::table(), [integer()]) -> {'ok', Row::tuple()} | 'undefined'. snmp_get_row(Tab, RowIndex) when is_atom(Tab), Tab /= schema, is_list(RowIndex) -> case get(mnesia_activity_state) of {Mod, Tid, Ts=#tidstore{store=Store}} when element(1, Tid) =:= tid -> @@ -2670,7 +2943,7 @@ snmp_get_row(Tab, _RowIndex) -> abort({bad_type, Tab}). %%%%%%%%%%%%% - +-spec snmp_get_next_index(Tab::table(), [integer()]) -> {'ok', [integer()]} | 'endOfTable'. snmp_get_next_index(Tab, RowIndex) when is_atom(Tab), Tab /= schema, is_list(RowIndex) -> {Next,OrigKey} = dirty_rpc(Tab, mnesia_snmp_hook, get_next_index, [Tab, RowIndex]), case get(mnesia_activity_state) of @@ -2712,7 +2985,7 @@ get_ordered_snmp_key(_, []) -> endOfTable. %%%%%%%%%% - +-spec snmp_get_mnesia_key(Tab::table(), [integer()]) -> {'ok', Key::term()} | 'undefined'. snmp_get_mnesia_key(Tab, RowIndex) when is_atom(Tab), Tab /= schema, is_list(RowIndex) -> case get(mnesia_activity_state) of {_Mod, Tid, Ts} when element(1, Tid) =:= tid -> @@ -2776,17 +3049,27 @@ snmp_filter_key(undefined, RowIndex, Tab, Store) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Textfile access - +-spec load_textfile(File::file:filename()) -> t_result(ok) | {'error', term()}. load_textfile(F) -> mnesia_text:load_textfile(F). + +-spec dump_to_textfile(File :: file:filename()) -> 'ok' | 'error' | {'error', term()}. dump_to_textfile(F) -> mnesia_text:dump_to_textfile(F). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% QLC Handles %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-spec table(Tab::table()) -> qlc:query_handle(). table(Tab) -> table(Tab, []). + +-spec table(Tab::table(), Options) -> qlc:query_handle() when + Options :: Option | [Option], + Option :: MnesiaOpt | QlcOption, + MnesiaOpt :: {'traverse', SelectOp} | {lock, lock_kind()} | {n_objects, non_neg_integer()}, + SelectOp :: 'select' | {'select', ets:match_spec()}, + QlcOption :: {'key_equality', '==' | '=:='}. table(Tab,Opts) -> {[Trav,Lock,NObjects],QlcOptions0} = qlc_opts(Opts,[{traverse,select},{lock,read},{n_objects,100}]), diff --git a/lib/mnesia/src/mnesia.hrl b/lib/mnesia/src/mnesia.hrl index 0716dd87c8..da7e662288 100644 --- a/lib/mnesia/src/mnesia.hrl +++ b/lib/mnesia/src/mnesia.hrl @@ -49,12 +49,12 @@ %% It's important that counter is first, since we compare tid's --record(tid, +-record(tid, {counter, %% serial no for tid pid}). %% owner of tid --record(tidstore, +-record(tidstore, {store, %% current ets table for tid up_stores = [], %% list of upper layer stores for nested trans level = 1}). %% transaction level @@ -128,5 +128,4 @@ mnesia_lib:eval_debug_fun(I, C, ?FILE, ?LINE)). -else. -define(eval_debug_fun(I, C), ok). --endif. - +-endif. diff --git a/lib/mnesia/src/mnesia_app.erl b/lib/mnesia/src/mnesia_app.erl new file mode 100644 index 0000000000..4d89011db2 --- /dev/null +++ b/lib/mnesia/src/mnesia_app.erl @@ -0,0 +1,41 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +-module(mnesia_app). + +-behaviour(application). + +-export([start/2, stop/1]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% application callback functions + +start(normal, Args) -> + case mnesia_sup:start_link(Args) of + {ok, Pid} -> + {ok, Pid, {normal, Args}}; + Error -> + Error + end; +start(_, _) -> + {error, badarg}. + +stop(_StartArgs) -> + ok. diff --git a/lib/mnesia/src/mnesia_checkpoint.erl b/lib/mnesia/src/mnesia_checkpoint.erl index 9eb939e8d3..fc626940b4 100644 --- a/lib/mnesia/src/mnesia_checkpoint.erl +++ b/lib/mnesia/src/mnesia_checkpoint.erl @@ -909,7 +909,7 @@ retainer_loop(Cp = #checkpoint_args{name=Name}) -> retainer_loop(Cp2); {From, {iter_end, Iter}} -> - retainer_fixtable(Iter#iter.oid_tab, false), + ?SAFE(retainer_fixtable(Iter#iter.oid_tab, false)), Iters = Cp#checkpoint_args.iterators -- [Iter], reply(From, Name, ok), retainer_loop(Cp#checkpoint_args{iterators = Iters}); @@ -971,7 +971,8 @@ do_stop(Cp) -> unset({checkpoint, Name}), lists:foreach(fun deactivate_tab/1, Cp#checkpoint_args.retainers), Iters = Cp#checkpoint_args.iterators, - lists:foreach(fun(I) -> retainer_fixtable(I#iter.oid_tab, false) end, Iters). + [?SAFE(retainer_fixtable(Tab, false)) || #iter{main_tab=Tab} <- Iters], + ok. deactivate_tab(R) -> Name = R#retainer.cp_name, @@ -1151,7 +1152,7 @@ do_change_copy(Cp, Tab, FromType, ToType) -> Cp#checkpoint_args{retainers = Rs, nodes = writers(Rs)}. check_iter(From, Iter) when Iter#iter.pid == From -> - retainer_fixtable(Iter#iter.oid_tab, false), + ?SAFE(retainer_fixtable(Iter#iter.oid_tab, false)), false; check_iter(_From, _Iter) -> true. diff --git a/lib/mnesia/src/mnesia_controller.erl b/lib/mnesia/src/mnesia_controller.erl index 4791e2e290..17b47c059e 100644 --- a/lib/mnesia/src/mnesia_controller.erl +++ b/lib/mnesia/src/mnesia_controller.erl @@ -1703,9 +1703,10 @@ add_active_replica(Tab, Node, Cs = #cstruct{}) -> block_table(Tab) -> Var = {Tab, where_to_commit}, - Old = val(Var), - New = {blocked, Old}, - set(Var, New). % where_to_commit + case is_tab_blocked(val(Var)) of + {true, _} -> ok; + {false, W2C} -> set(Var, mark_blocked_tab(true, W2C)) + end. unblock_table(Tab) -> call({unblock_table, Tab}). diff --git a/lib/mnesia/src/mnesia_event.erl b/lib/mnesia/src/mnesia_event.erl index 7320d381ea..6f7531245f 100644 --- a/lib/mnesia/src/mnesia_event.erl +++ b/lib/mnesia/src/mnesia_event.erl @@ -114,7 +114,8 @@ handle_table_event({Oper, Record, TransId}, State) -> handle_system_event({mnesia_checkpoint_activated, _Checkpoint}, State) -> {ok, State}; -handle_system_event({mnesia_checkpoint_deactivated, _Checkpoint}, State) -> +handle_system_event({mnesia_checkpoint_deactivated, Checkpoint}, State) -> + report_error("Checkpoint '~p' has been deactivated, last table copy deleted.\n",[Checkpoint]), {ok, State}; handle_system_event({mnesia_up, Node}, State) -> diff --git a/lib/mnesia/src/mnesia_frag.erl b/lib/mnesia/src/mnesia_frag.erl index c6e812b36d..c39f30e140 100644 --- a/lib/mnesia/src/mnesia_frag.erl +++ b/lib/mnesia/src/mnesia_frag.erl @@ -58,9 +58,7 @@ -include("mnesia.hrl"). --define(OLD_HASH_MOD, mnesia_frag_old_hash). -define(DEFAULT_HASH_MOD, mnesia_frag_hash). -%%-define(DEFAULT_HASH_MOD, ?OLD_HASH_MOD). %% BUGBUG: New should be default -record(frag_state, {foreign_key, @@ -80,7 +78,7 @@ lock(ActivityId, Opaque, {table , Tab}, LockKind) -> case frag_names(Tab) of [Tab] -> - mnesia:lock(ActivityId, Opaque, {table, Tab}, LockKind); + mnesia:lock(ActivityId, Opaque, {table, Tab}, LockKind); Frags -> DeepNs = [mnesia:lock(ActivityId, Opaque, {table, F}, LockKind) || F <- Frags], @@ -321,7 +319,7 @@ init_select(Tid,Opaque,Tab,Pat,Limit,LockKind) -> {'EXIT', _} -> mnesia:select(Tid, Opaque, Tab, Pat, Limit,LockKind); FH -> - FragNumbers = verify_numbers(FH,Pat), + FragNumbers = verify_numbers(FH,Pat), Fun = fun(Num) -> Name = n_to_frag_name(Tab, Num), Node = val({Name, where_to_read}), @@ -336,19 +334,19 @@ init_select(Tid,Opaque,Tab,Pat,Limit,LockKind) -> end. select_cont(_Tid,_,{frag_cont, '$end_of_table', [],_}) -> '$end_of_table'; -select_cont(Tid,Ts,{frag_cont, '$end_of_table', [{Tab,Node,Type}|Rest],Args}) -> +select_cont(Tid,Ts,{frag_cont, '$end_of_table', [{Tab,Node,Type}|Rest],Args}) -> {Spec,LockKind,Limit} = Args, InitFun = fun(FixedSpec) -> mnesia:dirty_sel_init(Node,Tab,FixedSpec,Limit,Type) end, Res = mnesia:fun_select(Tid,Ts,Tab,Spec,LockKind,Tab,InitFun,Limit,Node,Type), frag_sel_cont(Res, Rest, Args); -select_cont(Tid,Ts,{frag_cont, Cont, TabL, Args}) -> +select_cont(Tid,Ts,{frag_cont, Cont, TabL, Args}) -> frag_sel_cont(mnesia:select_cont(Tid,Ts,Cont),TabL,Args); select_cont(Tid,Ts,Else) -> %% Not a fragmented table mnesia:select_cont(Tid,Ts,Else). frag_sel_cont('$end_of_table', [],_) -> '$end_of_table'; -frag_sel_cont('$end_of_table', TabL,Args) -> +frag_sel_cont('$end_of_table', TabL,Args) -> {[], {frag_cont, '$end_of_table', TabL,Args}}; frag_sel_cont({Recs,Cont}, TabL,Args) -> {Recs, {frag_cont, Cont, TabL,Args}}. @@ -358,9 +356,9 @@ do_select(ActivityId, Opaque, Tab, MatchSpec, LockKind) -> {'EXIT', _} -> mnesia:select(ActivityId, Opaque, Tab, MatchSpec, LockKind); FH -> - FragNumbers = verify_numbers(FH,MatchSpec), + FragNumbers = verify_numbers(FH,MatchSpec), Fun = fun(Num) -> - Name = n_to_frag_name(Tab, Num), + Name = n_to_frag_name(Tab, Num), Node = val({Name, where_to_read}), mnesia:lock(ActivityId, Opaque, {table, Name}, LockKind), {Name, Node} @@ -398,7 +396,7 @@ do_select(ActivityId, Opaque, Tab, MatchSpec, LockKind) -> verify_numbers(FH,MatchSpec) -> HashState = FH#frag_state.hash_state, - FragNumbers = + FragNumbers = case FH#frag_state.hash_module of HashMod when HashMod == ?DEFAULT_HASH_MOD -> ?DEFAULT_HASH_MOD:match_spec_to_frag_numbers(HashState, MatchSpec); @@ -434,7 +432,7 @@ local_select(ReplyTo, Ref, RemoteNameNodes, MatchSpec) -> end, unlink(ReplyTo), exit(normal). - + remote_select(ReplyTo, Ref, NameNodes, MatchSpec) -> do_remote_select(ReplyTo, Ref, NameNodes, MatchSpec). @@ -805,22 +803,22 @@ make_deactivate(Tab) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Add a fragment to a fragmented table and fill it with half of %% the records from one of the old fragments - + make_multi_add_frag(Tab, SortedNs) when is_list(SortedNs) -> verify_multi(Tab), Ops = make_add_frag(Tab, SortedNs), %% Propagate to foreigners MoreOps = [make_add_frag(T, SortedNs) || T <- lookup_foreigners(Tab)], - [Ops | MoreOps]; + [Ops | MoreOps]; make_multi_add_frag(Tab, SortedNs) -> mnesia:abort({bad_type, Tab, SortedNs}). verify_multi(Tab) -> FH = lookup_frag_hash(Tab), ForeignKey = FH#frag_state.foreign_key, - mnesia_schema:verify(undefined, ForeignKey, - {combine_error, Tab, + mnesia_schema:verify(undefined, ForeignKey, + {combine_error, Tab, "Op only allowed via foreign table", {foreign_key, ForeignKey}}). @@ -839,7 +837,7 @@ make_frag_names_and_acquire_locks(Tab, N, FragIndecies, DoNotLockN) -> end, FragNames = erlang:make_tuple(N, undefined), lists:foldl(Fun, FragNames, FragIndecies). - + make_add_frag(Tab, SortedNs) -> Cs = mnesia_schema:incr_version(val({Tab, cstruct})), mnesia_schema:ensure_active(Cs), @@ -849,8 +847,8 @@ make_add_frag(Tab, SortedNs) -> FragNames = make_frag_names_and_acquire_locks(Tab, N, WriteIndecies, true), NewFrag = element(N, FragNames), - NR = length(Cs#cstruct.ram_copies), - ND = length(Cs#cstruct.disc_copies), + NR = length(Cs#cstruct.ram_copies), + ND = length(Cs#cstruct.disc_copies), NDO = length(Cs#cstruct.disc_only_copies), NExt = length(Cs#cstruct.external_copies), NewCs = Cs#cstruct{name = NewFrag, @@ -859,7 +857,7 @@ make_add_frag(Tab, SortedNs) -> disc_copies = [], disc_only_copies = [], external_copies = []}, - + {NewCs2, _, _} = set_frag_nodes(NR, ND, NDO, NExt, NewCs, SortedNs, []), [NewOp] = mnesia_schema:make_create_table(NewCs2), @@ -944,7 +942,7 @@ do_split(FH, OldN, FragNames, [Rec | Recs], Ops) -> Key = element(2, Rec), NewOid = {NewFrag, Key}, OldOid = {OldFrag, Key}, - Ops2 = [{op, rec, unknown, {NewOid, [Rec], write}}, + Ops2 = [{op, rec, unknown, {NewOid, [Rec], write}}, {op, rec, unknown, {OldOid, [OldOid], delete}} | Ops], do_split(FH, OldN, FragNames, Recs, Ops2); _NewFrag -> @@ -958,7 +956,7 @@ do_split(_FH, _OldN, _FragNames, [], Ops) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Delete a fragment from a fragmented table %% and merge its records with another fragment - + make_multi_del_frag(Tab) -> verify_multi(Tab), Ops = make_del_frag(Tab), @@ -1064,7 +1062,7 @@ do_merge(FH, OldN, FragNames, [Rec | Recs], Ops) -> Key = element(2, Rec), NewOid = {NewFrag, Key}, OldOid = {OldFrag, Key}, - Ops2 = [{op, rec, unknown, {NewOid, [Rec], write}}, + Ops2 = [{op, rec, unknown, {NewOid, [Rec], write}}, {op, rec, unknown, {OldOid, [OldOid], delete}} | Ops], do_merge(FH, OldN, FragNames, Recs, Ops2); _NewFrag -> @@ -1077,7 +1075,7 @@ do_merge(FH, OldN, FragNames, [Rec | Recs], Ops) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Add a node to the node pool of a fragmented table - + make_multi_add_node(Tab, Node) -> verify_multi(Tab), Ops = make_add_node(Tab, Node), @@ -1085,7 +1083,7 @@ make_multi_add_node(Tab, Node) -> %% Propagate to foreigners MoreOps = [make_add_node(T, Node) || T <- lookup_foreigners(Tab)], [Ops | MoreOps]. - + make_add_node(Tab, Node) when is_atom(Node) -> Pool = lookup_prop(Tab, node_pool), case lists:member(Node, Pool) of @@ -1114,7 +1112,7 @@ make_multi_del_node(Tab, Node) -> %% Propagate to foreigners MoreOps = [make_del_node(T, Node) || T <- lookup_foreigners(Tab)], [Ops | MoreOps]. - + make_del_node(Tab, Node) when is_atom(Node) -> Cs = mnesia_schema:incr_version(val({Tab, cstruct})), mnesia_schema:ensure_active(Cs), @@ -1147,8 +1145,8 @@ remove_node(Node, Cs) -> case lists:member(Node, Pool) of true -> Pool2 = Pool -- [Node], - Props = lists:keyreplace(node_pool, 1, - Cs#cstruct.frag_properties, + Props = lists:keyreplace(node_pool, 1, + Cs#cstruct.frag_properties, {node_pool, Pool2}), {Cs#cstruct{frag_properties = Props}, true}; false -> @@ -1180,18 +1178,10 @@ props_to_frag_hash(Tab, Props) -> T when T == Tab -> Foreign = mnesia_schema:pick(Tab, foreign_key, Props, must), N = mnesia_schema:pick(Tab, n_fragments, Props, must), - case mnesia_schema:pick(Tab, hash_module, Props, undefined) of undefined -> - Split = mnesia_schema:pick(Tab, next_n_to_split, Props, must), - Doubles = mnesia_schema:pick(Tab, n_doubles, Props, must), - FH = {frag_hash, Foreign, N, Split, Doubles}, - HashState = ?OLD_HASH_MOD:init_state(Tab, FH), - #frag_state{foreign_key = Foreign, - n_fragments = N, - hash_module = ?OLD_HASH_MOD, - hash_state = HashState}; - HashMod -> + no_hash; + HashMod -> HashState = mnesia_schema:pick(Tab, hash_state, Props, must), #frag_state{foreign_key = Foreign, n_fragments = N, @@ -1216,13 +1206,9 @@ lookup_frag_hash(Tab) -> case ?catch_val({Tab, frag_hash}) of FH when is_record(FH, frag_state) -> FH; - {frag_hash, K, N, _S, _D} = FH -> + {frag_hash, _K, _N, _S, _D} -> %% Old style. Kept for backwards compatibility. - HashState = ?OLD_HASH_MOD:init_state(Tab, FH), - #frag_state{foreign_key = K, - n_fragments = N, - hash_module = ?OLD_HASH_MOD, - hash_state = HashState}; + mnesia:abort({no_hash, Tab, frag_properties, frag_hash}); {'EXIT', _} -> mnesia:abort({no_exists, Tab, frag_properties, frag_hash}) end. @@ -1249,10 +1235,10 @@ key_pos(FH) -> case FH#frag_state.foreign_key of undefined -> 2; - {_ForeignTab, Pos} -> + {_ForeignTab, Pos} -> Pos end. - + %% Returns name of fragment table key_to_frag_name({BaseTab, _} = Tab, Key) -> N = key_to_frag_number(Tab, Key), diff --git a/lib/mnesia/src/mnesia_frag_old_hash.erl b/lib/mnesia/src/mnesia_frag_old_hash.erl deleted file mode 100644 index b246c76236..0000000000 --- a/lib/mnesia/src/mnesia_frag_old_hash.erl +++ /dev/null @@ -1,133 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2002-2016. All Rights Reserved. -%% -%% Licensed under the Apache License, Version 2.0 (the "License"); -%% you may not use this file except in compliance with the License. -%% You may obtain a copy of the License at -%% -%% http://www.apache.org/licenses/LICENSE-2.0 -%% -%% Unless required by applicable law or agreed to in writing, software -%% distributed under the License is distributed on an "AS IS" BASIS, -%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -%% See the License for the specific language governing permissions and -%% limitations under the License. -%% -%% %CopyrightEnd% -%% - -%% -%%%---------------------------------------------------------------------- -%%% Purpose : Implements hashing functionality for fragmented tables -%%%---------------------------------------------------------------------- - --module(mnesia_frag_old_hash). -%%-behaviour(mnesia_frag_hash). - --compile({nowarn_deprecated_function, {erlang,hash,2}}). - -%% Hashing callback functions --export([ - init_state/2, - add_frag/1, - del_frag/1, - key_to_frag_number/2, - match_spec_to_frag_numbers/2 - ]). - --record(old_hash_state, - {n_fragments, - next_n_to_split, - n_doubles}). - -%% Old style. Kept for backwards compatibility. --record(frag_hash, - {foreign_key, - n_fragments, - next_n_to_split, - n_doubles}). - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -init_state(_Tab, InitialState) when InitialState == undefined -> - #old_hash_state{n_fragments = 1, - next_n_to_split = 1, - n_doubles = 0}; -init_state(_Tab, FH) when is_record(FH, frag_hash) -> - %% Old style. Kept for backwards compatibility. - #old_hash_state{n_fragments = FH#frag_hash.n_fragments, - next_n_to_split = FH#frag_hash.next_n_to_split, - n_doubles = FH#frag_hash.n_doubles}. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -add_frag(State) when is_record(State, old_hash_state) -> - SplitN = State#old_hash_state.next_n_to_split, - P = SplitN + 1, - L = State#old_hash_state.n_doubles, - NewN = State#old_hash_state.n_fragments + 1, - State2 = case trunc(math:pow(2, L)) + 1 of - P2 when P2 == P -> - State#old_hash_state{n_fragments = NewN, - next_n_to_split = 1, - n_doubles = L + 1}; - _ -> - State#old_hash_state{n_fragments = NewN, - next_n_to_split = P} - end, - {State2, [SplitN], [NewN]}. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -del_frag(State) when is_record(State, old_hash_state) -> - P = State#old_hash_state.next_n_to_split - 1, - L = State#old_hash_state.n_doubles, - N = State#old_hash_state.n_fragments, - if - P < 1 -> - L2 = L - 1, - MergeN = trunc(math:pow(2, L2)), - State2 = State#old_hash_state{n_fragments = N - 1, - next_n_to_split = MergeN, - n_doubles = L2}, - {State2, [N], [MergeN]}; - true -> - MergeN = P, - State2 = State#old_hash_state{n_fragments = N - 1, - next_n_to_split = MergeN}, - {State2, [N], [MergeN]} - end. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -key_to_frag_number(State, Key) when is_record(State, old_hash_state) -> - L = State#old_hash_state.n_doubles, - A = erlang:hash(Key, trunc(math:pow(2, L))), - P = State#old_hash_state.next_n_to_split, - if - A < P -> - erlang:hash(Key, trunc(math:pow(2, L + 1))); - true -> - A - end. - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -match_spec_to_frag_numbers(State, MatchSpec) when is_record(State, old_hash_state) -> - case MatchSpec of - [{HeadPat, _, _}] when is_tuple(HeadPat), tuple_size(HeadPat) > 2 -> - KeyPat = element(2, HeadPat), - case has_var(KeyPat) of - false -> - [key_to_frag_number(State, KeyPat)]; - true -> - lists:seq(1, State#old_hash_state.n_fragments) - end; - _ -> - lists:seq(1, State#old_hash_state.n_fragments) - end. - -has_var(Pat) -> - mnesia:has_var(Pat). diff --git a/lib/mnesia/src/mnesia_lib.erl b/lib/mnesia/src/mnesia_lib.erl index 10e232c800..1fdc656600 100644 --- a/lib/mnesia/src/mnesia_lib.erl +++ b/lib/mnesia/src/mnesia_lib.erl @@ -1192,25 +1192,15 @@ db_select(Storage, Tab, Pat) -> end. db_select_init({ext, Alias, Mod}, Tab, Pat, Limit) -> - case Mod:select(Alias, Tab, Pat, Limit) of - {Matches, Continuation} when is_list(Matches) -> - {Matches, {Alias, Continuation}}; - R -> - R - end; + Mod:select(Alias, Tab, Pat, Limit); db_select_init(disc_only_copies, Tab, Pat, Limit) -> dets:select(Tab, Pat, Limit); db_select_init(_, Tab, Pat, Limit) -> ets:select(Tab, Pat, Limit). -db_select_cont({ext, Alias, Mod}, Cont0, Ms) -> +db_select_cont({ext, _Alias, Mod}, Cont0, Ms) -> Cont = Mod:repair_continuation(Cont0, Ms), - case Mod:select(Cont) of - {Matches, Continuation} when is_list(Matches) -> - {Matches, {Alias, Continuation}}; - R -> - R - end; + Mod:select(Cont); db_select_cont(disc_only_copies, Cont0, Ms) -> Cont = dets:repair_continuation(Cont0, Ms), dets:select(Cont); diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl index 71e5829c87..c710470a2c 100644 --- a/lib/mnesia/src/mnesia_loader.erl +++ b/lib/mnesia/src/mnesia_loader.erl @@ -342,9 +342,12 @@ spawned_receiver(ReplyTo,Tab,Storage,Cs, SenderPid,TabSize,DetsData, Init) -> Done = do_init_table(Tab,Storage,Cs, SenderPid,TabSize,DetsData, ReplyTo, Init), - ReplyTo ! {self(),Done}, - unlink(ReplyTo), - unlink(whereis(mnesia_controller)), + try + ReplyTo ! {self(),Done}, + unlink(ReplyTo), + unlink(whereis(mnesia_controller)) + catch _:_ -> ok %% avoid error reports when stopping down mnesia + end, exit(normal). wait_on_load_complete(Pid) -> @@ -916,9 +919,15 @@ send_packet(_N, _Pid, _Chunk, DataState) -> finish_copy(Pid, Tab, Storage, RemoteS, NeedLock) -> RecNode = node(Pid), DatBin = dat2bin(Tab, Storage, RemoteS), + Node = node(Pid), Trans = fun() -> NeedLock andalso mnesia:read_lock_table(Tab), + %% Check that receiver is still alive + receive {copier_done, Node} -> + throw(receiver_died) + after 0 -> ok + end, A = val({Tab, access_mode}), mnesia_controller:sync_and_block_table_whereabouts(Tab, RecNode, RemoteS, A), cleanup_tab_copier(Pid, Storage, Tab), @@ -927,7 +936,7 @@ finish_copy(Pid, Tab, Storage, RemoteS, NeedLock) -> receive {Pid, no_more} -> % Dont bother about the spurious 'more' message no_more; - {copier_done, Node} when Node == node(Pid)-> + {copier_done, Node} -> verbose("Tab receiver ~p crashed (more): ~p~n", [Tab, Node]), receiver_died end diff --git a/lib/mnesia/src/mnesia_monitor.erl b/lib/mnesia/src/mnesia_monitor.erl index ab78c9b13e..ff58974aba 100644 --- a/lib/mnesia/src/mnesia_monitor.erl +++ b/lib/mnesia/src/mnesia_monitor.erl @@ -169,7 +169,7 @@ check_protocol([{Node, {accept, Mon, Version, Protocol}} | Tail], Protocols) -> verbose("Failed to connect with ~p. ~p protocols rejected. " "expected version = ~p, expected protocol = ~p~n", [Node, Protocols, Version, Protocol]), - unlink(Mon), % Get rid of unneccessary link + unlink(Mon), % Get rid of unnecessary link check_protocol(Tail, Protocols) end; check_protocol([{Node, {reject, _Mon, Version, Protocol}} | Tail], Protocols) -> diff --git a/lib/mnesia/src/mnesia_schema.erl b/lib/mnesia/src/mnesia_schema.erl index 0e4017e4c3..b0d7965886 100644 --- a/lib/mnesia/src/mnesia_schema.erl +++ b/lib/mnesia/src/mnesia_schema.erl @@ -1941,7 +1941,7 @@ make_change_table_copy_type(Tab, Node, ToS) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% change index functions .... -%% Pos is allready added by 1 in both of these functions +%% Pos is already added by 1 in both of these functions add_table_index(Tab, Pos) -> schema_transaction(fun() -> do_add_table_index(Tab, Pos) end). diff --git a/lib/mnesia/src/mnesia_sup.erl b/lib/mnesia/src/mnesia_sup.erl index 4aece81308..3e5792900b 100644 --- a/lib/mnesia/src/mnesia_sup.erl +++ b/lib/mnesia/src/mnesia_sup.erl @@ -23,39 +23,21 @@ -module(mnesia_sup). --behaviour(application). -behaviour(supervisor). --export([start/0, start/2, init/1, stop/1, start_event/0, kill/0]). +-export([start_link/1, init/1, start_event/0, kill/0]). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% application and suprvisor callback functions - -start(normal, Args) -> - SupName = {local,?MODULE}, - case supervisor:start_link(SupName, ?MODULE, [Args]) of - {ok, Pid} -> - {ok, Pid, {normal, Args}}; - Error -> - Error - end; -start(_, _) -> - {error, badarg}. - -start() -> - SupName = {local,?MODULE}, - supervisor:start_link(SupName, ?MODULE, []). +start_link(Args) -> + supervisor:start_link({local,?MODULE}, ?MODULE, [Args]). -stop(_StartArgs) -> - ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% supervisor callback functions -init([]) -> % Supervisor - init(); -init([[]]) -> % Application +init([[]]) -> init(); init(BadArg) -> {error, {badarg, BadArg}}. - + init() -> Flags = {one_for_all, 0, 3600}, % Should be rest_for_one policy @@ -124,4 +106,3 @@ ensure_dead(Name) -> timer:sleep(10), ensure_dead(Name) end. - diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl index b116b48312..305bf14bcf 100644 --- a/lib/mnesia/src/mnesia_tm.erl +++ b/lib/mnesia/src/mnesia_tm.erl @@ -80,6 +80,7 @@ start() -> init(Parent) -> register(?MODULE, self()), process_flag(trap_exit, true), + process_flag(message_queue_data, off_heap), %% Initialize the schema IgnoreFallback = mnesia_monitor:get_env(ignore_fallback_at_startup), @@ -950,7 +951,7 @@ return_abort(Fun, Args, Reason) -> if Level == 1 -> mnesia_locker:async_release_tid(Nodes, Tid), - ?MODULE ! {delete_transaction, Tid}, + ?SAFE(?MODULE ! {delete_transaction, Tid}), erase(mnesia_activity_state), flush_downs(), ?SAFE(unlink(whereis(?MODULE))), diff --git a/lib/mnesia/test/ext_test.erl b/lib/mnesia/test/ext_test.erl index 45ddb148bc..ad32245a11 100644 --- a/lib/mnesia/test/ext_test.erl +++ b/lib/mnesia/test/ext_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2014. All Rights Reserved. +%% Copyright Ericsson AB 1996-2016. All Rights Reserved. %% %% Licensed under the Apache License, Version 2.0 (the "License"); %% you may not use this file except in compliance with the License. @@ -233,5 +233,5 @@ select_1({Acc, C}) -> select(ext_ets, Tab, Ms, Limit) when is_integer(Limit); Limit =:= infinity -> ets:select(mnesia_lib:val({?MODULE,Tab}), Ms, Limit). -repair_continuation({Alias, Cont}, Ms) -> - {Alias, ets:repair_continuation(Cont, Ms)}. +repair_continuation(Cont, Ms) -> + ets:repair_continuation(Cont, Ms). diff --git a/lib/mnesia/test/mnesia_atomicity_test.erl b/lib/mnesia/test/mnesia_atomicity_test.erl index 612c4ad368..cc32ba3826 100644 --- a/lib/mnesia/test/mnesia_atomicity_test.erl +++ b/lib/mnesia/test/mnesia_atomicity_test.erl @@ -700,11 +700,19 @@ start_restart_check(RestartOp, ReplicaNeed, Config) -> %% mnesia shall be killed at that node, where A is reading %% the information from - kill_where_to_read(TabName, N1, [N2, N3]), + Read = kill_where_to_read(TabName, N1, [N2, N3]), %% wait some time to let mnesia go down and spread those news around %% fun A shall be able to finish its job before being restarted - wait(500), + Wait = fun(Loop) -> + wait(300), + sys:get_status(mnesia_monitor), + case lists:member(Read, mnesia_lib:val({current, db_nodes})) of + true -> Loop(Loop); + false -> ok + end + end, + Wait(Wait), A ! go_ahead, %% the sticky write doesnt work on remote nodes !!! @@ -772,10 +780,12 @@ kill_where_to_read(TabName, N1, Nodes) -> Read = rpc:call(N1,mnesia,table_info, [TabName, where_to_read]), case lists:member(Read, Nodes) of true -> - mnesia_test_lib:kill_mnesia([Read]); + mnesia_test_lib:kill_mnesia([Read]), + Read; false -> ?error("Fault while killing Mnesia: ~p~n", [Read]), - mnesia_test_lib:kill_mnesia(Nodes) + mnesia_test_lib:kill_mnesia(Nodes), + Read end. sync_tid_release() -> diff --git a/lib/mnesia/test/mnesia_consistency_test.erl b/lib/mnesia/test/mnesia_consistency_test.erl index 9cc84de87b..2fe1bd34e6 100644 --- a/lib/mnesia/test/mnesia_consistency_test.erl +++ b/lib/mnesia/test/mnesia_consistency_test.erl @@ -665,10 +665,10 @@ consistency_after_restore(ReplicaType, Op, Config) -> [lists:foreach(fun(E) -> ok = mnesia:dirty_write({Tab, E, 2}) end, NList) || Tab <- Tabs], - Pids1 = [{'EXIT', spawn_link(?MODULE, change_tab, [self(), carA, Op]), ok} || _ <- lists:seq(1, 5)], - Pids2 = [{'EXIT', spawn_link(?MODULE, change_tab, [self(), carB, Op]), ok} || _ <- lists:seq(1, 5)], - Pids3 = [{'EXIT', spawn_link(?MODULE, change_tab, [self(), carC, Op]), ok} || _ <- lists:seq(1, 5)], - Pids4 = [{'EXIT', spawn_link(?MODULE, change_tab, [self(), carD, Op]), ok} || _ <- lists:seq(1, 5)], + Pids1 = [{'EXIT', spawn_link(?MODULE, change_tab, [self(), carA, Op]), carA} || _ <- lists:seq(1, 5)], + Pids2 = [{'EXIT', spawn_link(?MODULE, change_tab, [self(), carB, Op]), carB} || _ <- lists:seq(1, 5)], + Pids3 = [{'EXIT', spawn_link(?MODULE, change_tab, [self(), carC, Op]), carC} || _ <- lists:seq(1, 5)], + Pids4 = [{'EXIT', spawn_link(?MODULE, change_tab, [self(), carD, Op]), carD} || _ <- lists:seq(1, 5)], AllPids = Pids1 ++ Pids2 ++ Pids3 ++ Pids4, @@ -678,19 +678,38 @@ consistency_after_restore(ReplicaType, Op, Config) -> Else -> Else end end, - + timer:sleep(timer:seconds(Delay)), %% Let changers grab locks ?verbose("Doing restore~n", []), ?match(Tabs, Restore(File, [{default_op, Op}])), - timer:sleep(timer:seconds(Delay)), %% Let em die + Collect = fun(Msg, Acc) -> + receive Msg -> Acc + after 10000 -> [Msg|Acc] + end + end, - ?match_multi_receive(AllPids), + Failed1 = lists:foldl(Collect, [], AllPids), + Failed = lists:foldl(Collect, [], Failed1), + + case Failed of + [] -> ok; + _ -> + ?match([], Failed), + io:format("TIME: ~p sec~n", [erlang:system_time(seconds) band 16#FF]), + Dbg = fun({_, Pid, Tab}) -> + io:format("Tab ~p: ~p~n",[Tab, process_info(Pid, current_stacktrace)]), + [io:format(" ~p~n", [Rec]) || Rec <- mnesia:dirty_match_object({Tab, '_', '_'})] + end, + [Dbg(Msg) || Msg <- Failed], + io:format(" Held: ~p~n", [mnesia_locker:get_held_locks()]), + io:format("Queue: ~p~n", [mnesia_locker:get_lock_queue()]) + end, - case ?match(ok, restore_verify_tabs(Tabs)) of - {success, ok} -> + case ?match(ok, restore_verify_tabs(Tabs)) of + {success, ok} -> file:delete(File); - _ -> + _ -> {T, M, S} = time(), File2 = ?flat_format("consistency_error~w~w~w.BUP", [T, M, S]), file:rename(File, File2) @@ -700,17 +719,20 @@ consistency_after_restore(ReplicaType, Op, Config) -> change_tab(Father, Tab, Test) -> Key = rand:uniform(20), Update = fun() -> + Time = erlang:system_time(seconds) band 16#FF, + case put(time, Time) of + Time -> ok; + _ -> io:format("~p ~p ~p sec~n", [self(), Tab, Time]) + end, case mnesia:read({Tab, Key}) of - [{Tab, Key, 1}] -> - quit; - [{Tab, Key, _N}] -> - mnesia:write({Tab, Key, 3}) + [{Tab, Key, 1}] -> quit; + [{Tab, Key, _N}] -> mnesia:write({Tab, Key, 3}) end end, case mnesia:transaction(Update) of {atomic, quit} -> - exit(ok); - {aborted, {no_exists, Tab}} when Test == recreate_tables ->%% I'll allow this + exit(Tab); + {aborted, {no_exists, Tab}} when Test == recreate_tables -> %% I'll allow this change_tab(Father, Tab, Test); {atomic, ok} -> change_tab(Father, Tab, Test) diff --git a/lib/mnesia/test/mnesia_evil_backup.erl b/lib/mnesia/test/mnesia_evil_backup.erl index b71348f144..044cf501fd 100644 --- a/lib/mnesia/test/mnesia_evil_backup.erl +++ b/lib/mnesia/test/mnesia_evil_backup.erl @@ -325,7 +325,7 @@ restore(Config, Op) -> end, ?match(ok, file:delete(File1)), ?match(ok, file:delete(File2)), - ?match([], Check() -- Before), + ?match([], Check() -- (Before ++ [{ok, latest_log}, {ok, previous_log}])), ?verify_mnesia(Nodes, []). @@ -723,18 +723,18 @@ bup_records(File, Mod) -> exit(Reason) end. -sops_with_checkpoint(doc) -> +sops_with_checkpoint(doc) -> ["Test schema operations during a checkpoint"]; sops_with_checkpoint(suite) -> []; sops_with_checkpoint(Config) when is_list(Config) -> - Ns = ?acquire_nodes(2, Config), - + Ns = [N1,N2] = ?acquire_nodes(2, Config), + ?match({ok, cp1, Ns}, mnesia:activate_checkpoint([{name, cp1},{max,mnesia:system_info(tables)}])), - Tab = tab, + Tab = tab, ?match({atomic, ok}, mnesia:create_table(Tab, [{disc_copies,Ns}])), OldRecs = [{Tab, K, -K} || K <- lists:seq(1, 5)], [mnesia:dirty_write(R) || R <- OldRecs], - + ?match({ok, cp2, Ns}, mnesia:activate_checkpoint([{name, cp2},{max,mnesia:system_info(tables)}])), File1 = "cp1_delete_me.BUP", ?match(ok, mnesia:dirty_write({Tab,6,-6})), @@ -742,16 +742,16 @@ sops_with_checkpoint(Config) when is_list(Config) -> ?match(ok, mnesia:dirty_write({Tab,7,-7})), File2 = "cp2_delete_me.BUP", ?match(ok, mnesia:backup_checkpoint(cp2, File2)), - + ?match(ok, mnesia:deactivate_checkpoint(cp1)), ?match(ok, mnesia:backup_checkpoint(cp2, File1)), ?match(ok, mnesia:dirty_write({Tab,8,-8})), - + ?match({atomic,ok}, mnesia:delete_table(Tab)), ?match({error,_}, mnesia:backup_checkpoint(cp2, File2)), ?match({'EXIT',_}, mnesia:dirty_write({Tab,9,-9})), - ?match({atomic,_}, mnesia:restore(File1, [{default_op, recreate_tables}])), + ?match({atomic,_}, mnesia:restore(File1, [{default_op, recreate_tables}])), Test = fun(N) when N > 5 -> ?error("To many records in backup ~p ~n", [N]); (N) -> case mnesia:dirty_read(Tab,N) of [{Tab,N,B}] when -B =:= N -> ok; @@ -759,8 +759,29 @@ sops_with_checkpoint(Config) when is_list(Config) -> end end, [Test(N) || N <- mnesia:dirty_all_keys(Tab)], - ?match({aborted,enoent}, mnesia:restore(File2, [{default_op, recreate_tables}])), - + ?match({aborted,enoent}, mnesia:restore(File2, [{default_op, recreate_tables}])), + + %% Mnesia crashes when deleting a table during backup + ?match([], mnesia_test_lib:stop_mnesia([N2])), + Tab2 = ram, + ?match({atomic, ok}, mnesia:create_table(Tab2, [{ram_copies,[N1]}])), + ?match({ok, cp3, _}, mnesia:activate_checkpoint([{name, cp3}, + {ram_overrides_dump,true}, + {min,[Tab2]}])), + Write = fun Loop (N) -> + case N > 0 of + true -> + mnesia:dirty_write({Tab2, N+100, N+100}), + Loop(N-1); + false -> + ok + end + end, + ok = Write(100000), + spawn_link(fun() -> ?match({atomic, ok},mnesia:delete_table(Tab2)) end), + + %% We don't check result here, depends on timing of above call + mnesia:backup_checkpoint(cp3, File2), file:delete(File1), file:delete(File2), - ?verify_mnesia(Ns, []). + ?verify_mnesia([N1], [N2]). diff --git a/lib/mnesia/test/mnesia_recovery_test.erl b/lib/mnesia/test/mnesia_recovery_test.erl index 2388b595d0..130b87346f 100644 --- a/lib/mnesia/test/mnesia_recovery_test.erl +++ b/lib/mnesia/test/mnesia_recovery_test.erl @@ -504,12 +504,21 @@ with_checkpoint(Config, Type) when is_list(Config) -> ?match(ok, mnesia:deactivate_checkpoint(sune)), ?match([], check_chkp(Nodes)), + Wait = fun(Loop) -> + timer:sleep(300), + sys:get_status(mnesia_monitor), + case lists:member(Kill, mnesia_lib:val({current, db_nodes})) of + true -> Loop(Loop); + false -> ok + end + end, + case Kill of Node1 -> ignore; Node2 -> mnesia_test_lib:kill_mnesia([Kill]), - timer:sleep(500), %% Just to help debugging + Wait(Wait), ?match({ok, sune, _}, mnesia:activate_checkpoint([{name, sune}, {max, mnesia:system_info(tables)}, {ram_overrides_dump, true}])), diff --git a/lib/mnesia/test/mnesia_test_lib.erl b/lib/mnesia/test/mnesia_test_lib.erl index 6e84a27ec9..0fabdc7929 100644 --- a/lib/mnesia/test/mnesia_test_lib.erl +++ b/lib/mnesia/test/mnesia_test_lib.erl @@ -263,6 +263,7 @@ slave_start_link(Host, Name, Retries) -> Path = code:get_path(), ok = rpc:call(NewNode, file, set_cwd, [Cwd]), true = rpc:call(NewNode, code, set_path, [Path]), + ok = rpc:call(NewNode, error_logger, tty, [false]), spawn_link(NewNode, ?MODULE, slave_sup, []), rpc:multicall([node() | nodes()], global, sync, []), {ok, NewNode}; diff --git a/lib/mnesia/test/mnesia_trans_access_test.erl b/lib/mnesia/test/mnesia_trans_access_test.erl index aa50ee4cb1..4ed73ea859 100644 --- a/lib/mnesia/test/mnesia_trans_access_test.erl +++ b/lib/mnesia/test/mnesia_trans_access_test.erl @@ -307,6 +307,7 @@ select14(Config) when is_list(Config) -> %% Some Helpers Trans = fun(Fun) -> mnesia:transaction(Fun) end, + Dirty = fun(Fun) -> mnesia:async_dirty(Fun) end, LoopHelp = fun('$end_of_table',_) -> []; ({Recs,Cont},Fun) -> Sel = mnesia:select(Cont), @@ -334,8 +335,13 @@ select14(Config) when is_list(Config) -> ?match({atomic, [OneRec]}, Trans(fun() -> Loop(Tab, OnePat) end)), ?match({atomic, All}, Trans(fun() -> Loop(Tab, AllPat) end)), - {atomic,{_, Cont}} = Trans(fun() -> mnesia:select(Tab, OnePat, 1, read) end), - ?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select(Cont) end)), + {atomic,{_, ContOne}} = Trans(fun() -> mnesia:select(Tab, OnePat, 1, read) end), + ?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select(ContOne) end)), + ?match('$end_of_table', Dirty(fun() -> mnesia:select(ContOne) end)), + + {atomic,{_, ContAll}} = Trans(fun() -> mnesia:select(Tab, AllPat, 1, read) end), + ?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select(ContAll) end)), + ?match({[_], _}, Dirty(fun() -> mnesia:select(ContAll) end)), ?match({aborted, _}, Trans(fun() -> mnesia:select(Tab, {match, '$1', 2},1,read) end)), ?match({aborted, _}, Trans(fun() -> mnesia:select(Tab, [{'_', [], '$1'}],1,read) end)), diff --git a/lib/mnesia/test/mt.erl b/lib/mnesia/test/mt.erl index 793fb125e6..5a981bf539 100644 --- a/lib/mnesia/test/mt.erl +++ b/lib/mnesia/test/mt.erl @@ -80,6 +80,8 @@ resolve(Suite0) when is_atom(Suite0) -> {Suite, Case} -> {Suite, is_group(Suite,Case)} end; +resolve({Suite0, {group, Case}}) -> + resolve({Suite0, Case}); resolve({Suite0, Case}) when is_atom(Suite0), is_atom(Case) -> case alias(Suite0) of Suite when is_atom(Suite) -> diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk index 194bc439a0..e272a469bb 100644 --- a/lib/mnesia/vsn.mk +++ b/lib/mnesia/vsn.mk @@ -1 +1 @@ -MNESIA_VSN = 4.13.4 +MNESIA_VSN = 4.14.3 |