aboutsummaryrefslogtreecommitdiffstats
path: root/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mnesia/doc/src/Mnesia_chap5.xmlsrc')
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap5.xmlsrc1653
1 files changed, 802 insertions, 851 deletions
diff --git a/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc
index 127c23e0f7..813731e0b8 100644
--- a/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc
+++ b/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc
@@ -31,222 +31,205 @@
<rev></rev>
<file>Mnesia_chap5.xml</file>
</header>
- <p>The earlier chapters of this User Guide described how to get
- started with Mnesia, and how to build a Mnesia database. In this
- chapter, we will describe the more advanced features available
- when building a distributed, fault tolerant Mnesia database. This
- chapter contains the following sections:
- </p>
+
+ <p>The previous sections describe how to get started
+ with <c>Mnesia</c> and how to build a <c>Mnesia</c> database. This
+ section describes the more advanced features available
+ when building a distributed, fault-tolerant <c>Mnesia</c> database.
+ The following topics are included:</p>
<list type="bulleted">
- <item>Indexing
- </item>
- <item>Distribution and Fault Tolerance
- </item>
- <item>Table fragmentation.
- </item>
- <item>Local content tables.
- </item>
- <item>Disc-less nodes.
- </item>
- <item>More about schema management
- </item>
- <item>Debugging a Mnesia application
- </item>
- <item>Concurrent Processes in Mnesia
- </item>
- <item>Prototyping
- </item>
- <item>Object Based Programming with Mnesia.
- </item>
+ <item>Indexing</item>
+ <item>Distribution and fault tolerance</item>
+ <item>Table fragmentation</item>
+ <item>Local content tables</item>
+ <item>Disc-less nodes</item>
+ <item>More about schema management</item>
+ <item><c>Mnesia</c> event handling</item>
+ <item>Debugging <c>Mnesia</c> applications</item>
+ <item>Concurrent processes in <c>Mnesia</c></item>
+ <item>Prototyping</item>
+ <item>Object-based programming with <c>Mnesia</c></item>
</list>
<section>
<marker id="indexing"></marker>
<title>Indexing</title>
- <p>Data retrieval and matching can be performed very efficiently
- if we know the key for the record. Conversely, if the key is not
- known, all records in a table must be searched. The larger the
- table the more time consuming it will become. To remedy this
- problem Mnesia's indexing capabilities are used to improve data
- retrieval and matching of records.
- </p>
- <p>The following two functions manipulate indexes on existing tables:
- </p>
+ <p>Data retrieval and matching can be performed efficiently
+ if the key for the record is known. Conversely, if the key is
+ unknown, all records in a table must be searched. The larger the
+ table, the more time consuming it becomes. To remedy this
+ problem, <c>Mnesia</c> indexing capabilities are used to improve
+ data retrieval and matching of records.</p>
+ <p>The following two functions manipulate indexes on existing
+ tables:</p>
<list type="bulleted">
- <item><c>mnesia:add_table_index(Tab, AttributeName) -> {aborted, R} |{atomic, ok}</c></item>
- <item><c>mnesia:del_table_index(Tab, AttributeName) -> {aborted, R} |{atomic, ok}</c></item>
- </list>
- <p>These functions create or delete a table index on field
- defined by <c>AttributeName</c>. To illustrate this, add an
- index to the table definition <c>(employee, {emp_no, name, salary, sex, phone, room_no}</c>, which is the example table
- from the Company database. The function
- which adds an index on the element <c>salary</c> can be expressed in
- the following way:
- </p>
- <list type="ordered">
- <item><c>mnesia:add_table_index(employee, salary)</c></item>
+ <item><seealso marker="mnesia#add_table_index/2">mnesia:add_table_index(Tab, AttributeName)
+ -> {aborted, R} |{atomic, ok}</seealso></item>
+ <item><seealso marker="mnesia#del_table_index/2">mnesia:del_table_index(Tab, AttributeName)
+ -> {aborted, R} |{atomic, ok}</seealso></item>
</list>
- <p>The indexing capabilities of Mnesia are utilized with the
- following three functions, which retrieve and match records on the
- basis of index entries in the database.
- </p>
+ <p>These functions create or delete a table index on a field
+ defined by <c>AttributeName</c>. To illustrate this, add an
+ index to the table definition <c>(employee, {emp_no, name,
+ salary, sex, phone, room_no})</c>, which is the example table
+ from the <c>Company</c> database. The function that
+ adds an index on element <c>salary</c> can be expressed
+ as <c>mnesia:add_table_index(employee, salary)</c>.</p>
+ <p>The indexing capabilities of <c>Mnesia</c> are used with the
+ following three functions, which retrieve and match records
+ based on index entries in the database:</p>
<list type="bulleted">
- <item><c>mnesia:index_read(Tab, SecondaryKey, AttributeName) -> transaction abort | RecordList</c>.
- Avoids an exhaustive search of the entire table, by looking up
- the <c>SecondaryKey</c> in the index to find the primary keys.
+ <item>
+ <seealso marker="mnesia#index_read/3">mnesia:index_read(Tab, SecondaryKey, AttributeName)
+ -> transaction abort | RecordList</seealso>
+ avoids an exhaustive search of the entire table, by looking up
+ <c>SecondaryKey</c> in the index to find the primary keys.
</item>
- <item><c>mnesia:index_match_object(Pattern, AttributeName) -> transaction abort | RecordList</c>
- Avoids an exhaustive search of the entire table, by looking up
+ <item>
+ <seealso marker="mnesia#index_match_object/2">mnesia:index_match_object(Pattern, AttributeName)
+ -> transaction abort | RecordList</seealso>
+ avoids an exhaustive search of the entire table, by looking up
the secondary key in the index to find the primary keys.
- The secondary key is found in the <c>AttributeName</c> field of
- the <c>Pattern</c>. The secondary key must be bound.
+ The secondary key is found in field <c>AttributeName</c> of
+ <c>Pattern</c>. The secondary key must be bound.
</item>
- <item><c>mnesia:match_object(Pattern) -> transaction abort | RecordList</c>
- Uses indices to avoid exhaustive search of the entire table.
- Unlike the other functions above, this function may utilize
+ <item>
+ <seealso marker="mnesia#match_object/1">mnesia:match_object(Pattern)
+ -> transaction abort | RecordList</seealso>
+ uses indexes to avoid exhaustive search of the entire table.
+ Unlike the previous functions, this function can use
any index as long as the secondary key is bound.</item>
</list>
<p>These functions are further described and exemplified in
- Chapter 4: <seealso marker="Mnesia_chap4#matching">Pattern matching</seealso>.
- </p>
+ <seealso marker="Mnesia_chap4#matching">Pattern Matching</seealso>.
+ </p>
</section>
<section>
<title>Distribution and Fault Tolerance</title>
- <p>Mnesia is a distributed, fault tolerant DBMS. It is possible
- to replicate tables on different Erlang nodes in a variety of
- ways. The Mnesia programmer does not have to state
+ <p><c>Mnesia</c> is a distributed, fault-tolerant DBMS. Tables
+ can be replicated on different Erlang nodes in various
+ ways. The <c>Mnesia</c> programmer does not need to state
where the different tables reside, only the names of the
- different tables are specified in the program code. This is
- known as "location transparency" and it is an important
- concept. In particular:
- </p>
+ different tables need to be specified in the program code. This
+ is known as "location transparency" and is an important
+ concept. In particular:</p>
<list type="bulleted">
- <item>A program will work regardless of the
- location of the data. It makes no difference whether the data
- resides on the local node, or on a remote node. <em>Note:</em> The program
- will run slower if the data is located on a remote node.
+ <item><p>A program works regardless of the data
+ location. It makes no difference whether the data
+ resides on the local node or on a remote node.</p>
+ <p>Notice that the program runs slower if the data
+ is located on a remote node.</p>
</item>
<item>The database can be reconfigured, and tables can be
- moved between nodes. These operations do not effect the user
+ moved between nodes. These operations do not affect the user
programs.
</item>
</list>
- <p>We have previously seen that each table has a number of
- system attributes, such as <c>index</c> and
- <c>type</c>.
- </p>
+ <p>It has previously been shown that each table has a number of
+ system attributes, such as <c>index</c> and <c>type</c>.</p>
<p>Table attributes are specified when the table is created. For
- example, the following function will create a new table with two
- RAM replicas:
- </p>
+ example, the following function creates a table with two
+ RAM replicas:</p>
<pre>
mnesia:create_table(foo,
[{ram_copies, [N1, N2]},
- {attributes, record_info(fields, foo)}]).
- </pre>
+ {attributes, record_info(fields, foo)}]).</pre>
<p>Tables can also have the following properties,
- where each attribute has a list of Erlang nodes as its value.
- </p>
+ where each attribute has a list of Erlang nodes as its value:</p>
<list type="bulleted">
<item>
- <p><c>ram_copies</c>. The value of the node list is a list of
- Erlang nodes, and a RAM replica of the table will reside on
- each node in the list. This is a RAM replica, and it is
- important to realize that no disc operations are performed when
- a program executes write operations to these replicas. However,
- should permanent RAM replicas be a requirement, then the
+ <p><c>ram_copies</c>. The value of the node list is a list
+ of Erlang nodes, and a RAM replica of the table resides on
+ each node in the list.</p>
+ <p>Notice that no disc operations are performed when
+ a program executes write operations to these replicas.
+ However, if permanent RAM replicas are required, the
following alternatives are available:</p>
<list type="ordered">
- <item>The <c>mnesia:dump_tables/1</c> function can be used
- to dump RAM table replicas to disc.
+ <item>The function
+ <seealso marker="mnesia#dump_tables/1">mnesia:dump_tables/1</seealso>
+ can be used to dump RAM table replicas to disc.
</item>
- <item>The table replicas can be backed up; either from
- RAM, or from disc if dumped there with the above
- function.
+ <item>The table replicas can be backed up, either from
+ RAM, or from disc if dumped there with this function.
</item>
</list>
</item>
<item><c>disc_copies</c>. The value of the attribute is a list
- of Erlang nodes, and a replica of the table will reside both
+ of Erlang nodes, and a replica of the table resides both
in RAM and on disc on each node in the list. Write operations
- addressed to the table will address both the RAM and the disc
+ addressed to the table address both the RAM and the disc
copy of the table.
</item>
<item><c>disc_only_copies</c>. The value of the attribute is a
- list of Erlang nodes, and a replica of the table will reside
+ list of Erlang nodes, and a replica of the table resides
only as a disc copy on each node in the list. The major
disadvantage of this type of table replica is the access
speed. The major advantage is that the table does not occupy
space in memory.
</item>
</list>
- <p>It is also possible to set and change table properties on
- existing tables. Refer to Chapter 3: <seealso marker="Mnesia_chap3#def_schema">Defining the Schema</seealso> for full
- details.
- </p>
+ <p>In addition, table properties can be set and changed.
+ For details, see
+ <seealso marker="Mnesia_chap3#def_schema">Define a Schema</seealso>.
+ </p>
<p>There are basically two reasons for using more than one table
- replica: fault tolerance, or speed. It is worthwhile to note
+ replica: fault tolerance and speed. Notice
that table replication provides a solution to both of these
- system requirements.
- </p>
- <p>If we have two active table replicas, all information is
- still available if one of the replicas fail. This can be a very
+ system requirements.</p>
+ <p>If there are two active table replicas, all information is
+ still available if one replica fails. This can be an
important property in many applications. Furthermore, if a table
- replica exists at two specific nodes, applications which execute
+ replica exists at two specific nodes, applications that execute
at either of these nodes can read data from the table without
- accessing the network. Network operations are considerably
- slower and consume more resources than local operations.
- </p>
+ accessing the network. Network operations are considerably
+ slower and consume more resources than local operations.</p>
<p>It can be advantageous to create table replicas for a
- distributed application which reads data often, but writes data
- seldom, in order to achieve fast read operations on the local
+ distributed application that reads data often, but writes data
+ seldom, to achieve fast read operations on the local
node. The major disadvantage with replication is the increased
time to write data. If a table has two replicas, every write
operation must access both table replicas. Since one of these
write operations must be a network operation, it is considerably
more expensive to perform a write operation to a replicated
- table than to a non-replicated table.
- </p>
+ table than to a non-replicated table.</p>
</section>
<section>
<title>Table Fragmentation</title>
<section>
- <title>The Concept</title>
- <p>A concept of table fragmentation has been introduced in
- order to cope with very large tables. The idea is to split a
- table into several more manageable fragments. Each fragment
- is implemented as a first class Mnesia table and may be
- replicated, have indices etc. as any other table. But the
- tables may neither have <c>local_content</c> nor have the
- <c>snmp</c> connection activated.
- </p>
- <p>In order to be able to access a record in a fragmented
- table, Mnesia must determine to which fragment the
- actual record belongs. This is done by the
- <c>mnesia_frag</c> module, which implements the
- <c>mnesia_access</c> callback behaviour. Please, read the
- documentation about <c>mnesia:activity/4</c> to see how
- <c>mnesia_frag</c> can be used as a <c>mnesia_access</c>
- callback module.
- </p>
- <p>At each record access <c>mnesia_frag</c> first computes
- a hash value from the record key. Secondly the name of the
- table fragment is determined from the hash value. And
- finally the actual table access is performed by the same
+ <title>Concept</title>
+ <p>A concept of table fragmentation has been introduced
+ to cope with large tables. The idea is to split a
+ table into several manageable fragments. Each fragment is
+ implemented as a first class <c>Mnesia</c> table and can be
+ replicated, have indexes, and so on, as any other table. But
+ the tables cannot have <c>local_content</c> or have the
+ <c>snmp</c> connection activated.</p>
+ <p>To be able to access a record in a fragmented
+ table, <c>Mnesia</c> must determine to which fragment the
+ actual record belongs. This is done by module
+ <c>mnesia_frag</c>, which implements the <c>mnesia_access</c>
+ callback behavior. It is recommended to read the
+ documentation about the function
+ <seealso marker="mnesia#activity/4">mnesia:activity/4</seealso>
+ to see how <c>mnesia_frag</c>
+ can be used as a <c>mnesia_access</c> callback module.</p>
+ <p>At each record access, <c>mnesia_frag</c> first computes
+ a hash value from the record key. Second, the name of the
+ table fragment is determined from the hash value.
+ Finally the actual table access is performed by the same
functions as for non-fragmented tables. When the key is
not known beforehand, all fragments are searched for
- matching records. Note: In <c>ordered_set</c> tables
- the records will be ordered per fragment, and the
- the order is undefined in results returned by select and
- match_object.
- </p>
- <p>The following piece of code illustrates
- how an existing Mnesia table is converted to be a
- fragmented table and how more fragments are added later on.
- </p>
+ matching records.</p>
+ <p>Notice that in <c>ordered_set</c> tables, the records
+ are ordered per fragment, and the the order is undefined in
+ results returned by <c>select</c> and <c>match_object</c>.</p>
+ <p>The following code illustrates how a <c>Mnesia</c> table is
+ converted to be a fragmented table and how more fragments
+ are added later:</p>
<code type="none"><![CDATA[
Eshell V4.7.3.3 (abort with ^G)
(a@sam)1> mnesia:start().
@@ -299,102 +282,96 @@ ok
<section>
<title>Fragmentation Properties</title>
- <p>There is a table property called
- <c>frag_properties</c> and may be read with
- <c>mnesia:table_info(Tab, frag_properties)</c>. The
- fragmentation properties is a list of tagged tuples with
- the arity 2. By default the list is empty, but when it is
- non-empty it triggers Mnesia to regard the table as
- fragmented. The fragmentation properties are:
- </p>
+ <p>The table property <c>frag_properties</c> can be read with
+ the function
+ <seealso marker="mnesia#table_info/2">mnesia:table_info(Tab, frag_properties)</seealso>.
+ The fragmentation properties are a list of tagged tuples with
+ arity 2. By default the list is empty, but when it is
+ non-empty it triggers <c>Mnesia</c> to regard the table as
+ fragmented. The fragmentation properties are as follows:</p>
<taglist>
<tag><c>{n_fragments, Int}</c></tag>
<item>
<p><c>n_fragments</c> regulates how many fragments
- that the table currently has. This property may explicitly
+ that the table currently has. This property can explicitly
be set at table creation and later be changed with
<c>{add_frag, NodesOrDist}</c> or
- <c>del_frag</c>. <c>n_fragment</c>s defaults to <c>1</c>.
- </p>
+ <c>del_frag</c>. <c>n_fragments</c> defaults to <c>1</c>.</p>
</item>
<tag><c>{node_pool, List}</c></tag>
<item>
- <p>The node pool contains a list of nodes and may
+ <p>The node pool contains a list of nodes and can
explicitly be set at table creation and later be changed
- with <c>{add_node, Node}</c> or <c>{del_node, Node}</c>. At table creation Mnesia tries to distribute
+ with <c>{add_node, Node}</c> or <c>{del_node, Node}</c>.
+ At table creation <c>Mnesia</c> tries to distribute
the replicas of each fragment evenly over all the nodes in
- the node pool. Hopefully all nodes will end up with the
+ the node pool. Hopefully all nodes end up with the
same number of replicas. <c>node_pool</c> defaults to the
- return value from <c>mnesia:system_info(db_nodes)</c>.
- </p>
+ return value from the function
+ <seealso marker="mnesia#system_info/1">mnesia:system_info(db_nodes)</seealso>.</p>
</item>
<tag><c>{n_ram_copies, Int}</c></tag>
<item>
<p>Regulates how many <c>ram_copies</c> replicas
- that each fragment should have. This property may
- explicitly be set at table creation. The default is
+ that each fragment is to have. This property can
+ explicitly be set at table creation. Defaults is
<c>0</c>, but if <c>n_disc_copies</c> and
<c>n_disc_only_copies</c> also are <c>0</c>,
- <c>n_ram_copies</c> will default be set to <c>1</c>.
- </p>
+ <c>n_ram_copies</c> defaults to <c>1</c>.</p>
</item>
<tag><c>{n_disc_copies, Int}</c></tag>
<item>
- <p>Regulates how many <c>disc_copies</c> replicas
- that each fragment should have. This property may
- explicitly be set at table creation. The default is <c>0</c>.
- </p>
+ <p>Regulates how many <c>disc_copies</c> replicas that
+ each fragment is to have. This property can explicitly
+ be set at table creation. Default is <c>0</c>.</p>
</item>
<tag><c>{n_disc_only_copies, Int}</c></tag>
<item>
<p>Regulates how many <c>disc_only_copies</c> replicas
- that each fragment should have. This property may
- explicitly be set at table creation. The default is <c>0</c>.
- </p>
+ that each fragment is to have. This property can
+ explicitly be set at table creation. Defaults is
+ <c>0</c>.</p>
</item>
<tag><c>{foreign_key, ForeignKey}</c></tag>
<item>
- <p><c>ForeignKey</c> may either be the atom
+ <p><c>ForeignKey</c> can either be the atom
<c>undefined</c> or the tuple <c>{ForeignTab, Attr}</c>,
- where <c>Attr</c> denotes an attribute which should be
+ where <c>Attr</c> denotes an attribute that is to be
interpreted as a key in another fragmented table named
- <c>ForeignTab</c>. Mnesia will ensure that the number of
+ <c>ForeignTab</c>. <c>Mnesia</c> ensures that the number of
fragments in this table and in the foreign table are
- always the same. When fragments are added or deleted
- Mnesia will automatically propagate the operation to all
- fragmented tables that has a foreign key referring to this
+ always the same.</p>
+ <p>When fragments are added or deleted, <c>Mnesia</c>
+ automatically propagates the operation to all
+ fragmented tables that have a foreign key referring to this
table. Instead of using the record key to determine which
- fragment to access, the value of the <c>Attr</c> field is
- used. This feature makes it possible to automatically
- co-locate records in different tables to the same
- node. <c>foreign_key</c> defaults to
- <c>undefined</c>. However if the foreign key is set to
- something else it will cause the default values of the
+ fragment to access, the value of field <c>Attr</c> is
+ used. This feature makes it possible to colocate records
+ automatically in different tables to the same node.
+ <c>foreign_key</c> defaults to
+ <c>undefined</c>. However, if the foreign key is set to
+ something else, it causes the default values of the
other fragmentation properties to be the same values as
- the actual fragmentation properties of the foreign table.
- </p>
+ the actual fragmentation properties of the foreign table.</p>
</item>
<tag><c>{hash_module, Atom}</c></tag>
<item>
- <p>Enables definition of an alternate hashing scheme.
- The module must implement the <c>mnesia_frag_hash</c>
- callback behaviour (see the reference manual). This
- property may explicitly be set at table creation.
- The default is <c>mnesia_frag_hash</c>.</p>
- <p>Older tables that was created before the concept of
- user defined hash modules was introduced, uses
- the <c>mnesia_frag_old_hash</c> module in order to
- be backwards compatible. The <c>mnesia_frag_old_hash</c>
- is still using the poor deprecated <c>erlang:hash/1</c>
- function.
- </p>
+ <p>Enables definition of an alternative hashing scheme.
+ The module must implement the
+ <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>
- <p>Enables a table specific parameterization
- of a generic hash module. This property may explicitly
- be set at table creation.
- The default is <c>undefined</c>.</p>
+ <p>Enables a table-specific parameterization of a
+ generic hash module. This property can explicitly be set
+ at table creation. Default is <c>undefined</c>.</p>
<code type="none"><![CDATA[
Eshell V4.7.3.3 (abort with ^G)
(a@sam)1> mnesia:start().
@@ -463,177 +440,159 @@ ok
<title>Management of Fragmented Tables</title>
<p>The function <c>mnesia:change_table_frag(Tab, Change)</c>
is intended to be used for reconfiguration of fragmented
- tables. The <c>Change</c> argument should have one of the
- following values:
- </p>
+ tables. Argument <c>Change</c> is to have one of the
+ following values:</p>
<taglist>
<tag><c>{activate, FragProps}</c></tag>
<item>
<p>Activates the fragmentation properties of an
- existing table. <c>FragProps</c> should either contain
- <c>{node_pool, Nodes}</c> or be empty.
- </p>
+ existing table. <c>FragProps</c> is either to contain
+ <c>{node_pool, Nodes}</c> or be empty.</p>
</item>
<tag><c>deactivate</c></tag>
<item>
<p>Deactivates the fragmentation properties of a
- table. The number of fragments must be <c>1</c>. No other
- tables may refer to this table in its foreign key.
- </p>
+ table. The number of fragments must be <c>1</c>. No other
+ table can refer to this table in its foreign key.</p>
</item>
<tag><c>{add_frag, NodesOrDist}</c></tag>
<item>
- <p>Adds one new fragment to a fragmented table. All
- records in one of the old fragments will be rehashed and
- about half of them will be moved to the new (last)
- fragment. All other fragmented tables, which refers to this
- table in their foreign key, will automatically get a new
- fragment, and their records will also be dynamically
- rehashed in the same manner as for the main table.
- </p>
- <p>The <c>NodesOrDist</c> argument may either be a list
- of nodes or the result from <c>mnesia:table_info(Tab, frag_dist)</c>. The <c>NodesOrDist</c> argument is
+ <p>Adds a fragment to a fragmented table. All
+ records in one of the old fragments are rehashed and
+ about half of them are moved to the new (last)
+ fragment. All other fragmented tables, which refer to this
+ table in their foreign key, automatically get a new
+ fragment. Also, their records are dynamically
+ rehashed in the same manner as for the main table.</p>
+ <p>Argument <c>NodesOrDist</c> can either be a list of
+ nodes or the result from the function
+ <seealso marker="mnesia#table_info/2">mnesia:table_info(Tab, frag_dist)</seealso>.
+ Argument <c>NodesOrDist</c> is
assumed to be a sorted list with the best nodes to
host new replicas first in the list. The new fragment
- will get the same number of replicas as the first
- fragment (see <c>n_ram_copies</c>, <c>n_disc_copies</c>
+ gets the same number of replicas as the first
+ fragment (see <c>n_ram_copies</c>, <c>n_disc_copies</c>,
and <c>n_disc_only_copies</c>). The <c>NodesOrDist</c>
list must at least contain one element for each
- replica that needs to be allocated.
- </p>
+ replica that needs to be allocated.</p>
</item>
<tag><c>del_frag</c></tag>
<item>
- <p>Deletes one fragment from a fragmented table. All
- records in the last fragment will be moved to one of the other
- fragments. All other fragmented tables which refers to
- this table in their foreign key, will automatically lose
- their last fragment and their records will also be
+ <p>Deletes a fragment from a fragmented table. All
+ records in the last fragment are moved to one of the other
+ fragments. All other fragmented tables, which refer to
+ this table in their foreign key, automatically lose
+ their last fragment. Also, their records are
dynamically rehashed in the same manner as for the main
- table.
- </p>
+ table.</p>
</item>
<tag><c>{add_node, Node}</c></tag>
<item>
- <p>Adds a new node to the <c>node_pool</c>. The new
- node pool will affect the list returned from
- <c>mnesia:table_info(Tab, frag_dist)</c>.
- </p>
+ <p>Adds a node to <c>node_pool</c>. The new
+ node pool affects the list returned from the function
+ <seealso marker="mnesia#table_info/2">mnesia:table_info(Tab, frag_dist)</seealso>.
+ </p>
</item>
<tag><c>{del_node, Node}</c></tag>
<item>
- <p>Deletes a new node from the <c>node_pool</c>. The
- new node pool will affect the list returned from
- <c>mnesia:table_info(Tab, frag_dist)</c>.</p>
+ <p>Deletes a node from <c>node_pool</c>. The new
+ node pool affects the list returned from the function
+ <seealso marker="mnesia#table_info/2">mnesia:table_info(Tab, frag_dist)</seealso>.
+ </p>
</item>
</taglist>
</section>
<section>
<title>Extensions of Existing Functions</title>
- <p>The function <c>mnesia:create_table/2</c> is used to
- create a brand new fragmented table, by setting the table
- property <c>frag_properties</c> to some proper values.
- </p>
- <p>The function <c>mnesia:delete_table/1</c> is used to
- delete a fragmented table including all its
- fragments. There must however not exist any other
- fragmented tables which refers to this table in their foreign key.
- </p>
- <p>The function <c>mnesia:table_info/2</c> now understands
- the <c>frag_properties</c> item.
- </p>
- <p>If the function <c>mnesia:table_info/2</c> is invoked in
- the activity context of the <c>mnesia_frag</c> module,
- information of several new items may be obtained:
- </p>
+ <p>The function
+ <seealso marker="mnesia#create_table/2">mnesia:create_table/2</seealso>
+ creates a brand new fragmented table, by setting table
+ property <c>frag_properties</c> to some proper values.</p>
+ <p>The function
+ <seealso marker="mnesia#delete_table/1">mnesia:delete_table/1</seealso>
+ deletes a fragmented table including all its
+ fragments. There must however not exist any other fragmented
+ tables that refer to this table in their foreign key.</p>
+ <p>The function
+ <seealso marker="mnesia#table_info/2">mnesia:table_info/2</seealso>
+ now understands item <c>frag_properties</c>.</p>
+ <p>If the function <c>mnesia:table_info/2</c> is started in
+ the activity context of module <c>mnesia_frag</c>,
+ information of several new items can be obtained:</p>
<taglist>
<tag><c>base_table</c></tag>
- <item>
- <p>the name of the fragmented table
- </p>
- </item>
+ <item>The name of the fragmented table</item>
<tag><c>n_fragments</c></tag>
- <item>
- <p>the actual number of fragments
- </p>
- </item>
+ <item>The actual number of fragments</item>
<tag><c>node_pool</c></tag>
- <item>
- <p>the pool of nodes
- </p>
- </item>
+ <item>The pool of nodes</item>
<tag><c>n_ram_copies</c></tag>
<item></item>
<tag><c>n_disc_copies</c></tag>
<item></item>
<tag><c>n_disc_only_copies</c></tag>
<item>
- <p>the number of replicas with storage type
- <c>ram_copies</c>, <c>disc_copies</c> and <c>disc_only_copies</c>
+ <p>The number of replicas with storage type <c>ram_copies</c>,
+ <c>disc_copies</c>, and <c>disc_only_copies</c>,
respectively. The actual values are dynamically derived
from the first fragment. The first fragment serves as a
- pro-type and when the actual values needs to be computed
- (e.g. when adding new fragments) they are simply
- determined by counting the number of each replicas for
- each storage type. This means, when the functions
- <c>mnesia:add_table_copy/3</c>,
- <c>mnesia:del_table_copy/2</c> and<c>mnesia:change_table_copy_type/2</c> are applied on the
- first fragment, it will affect the settings on
+ protype. When the actual values need to be computed
+ (for example, when adding new fragments) they are
+ determined by counting the number of each replica for
+ each storage type. This means that when the functions
+ <seealso marker="mnesia#add_table_copy/3">mnesia:add_table_copy/3</seealso>,
+
+ <seealso marker="mnesia#del_table_copy/2">mnesia:del_table_copy/2</seealso>,
+ and
+ <seealso marker="mnesia#change_table_copy_type/3">mnesia:change_table_copy_type/2</seealso> are applied on the
+ first fragment, it affects the settings on
<c>n_ram_copies</c>, <c>n_disc_copies</c>, and
- <c>n_disc_only_copies</c>.
- </p>
+ <c>n_disc_only_copies</c>.</p>
</item>
<tag><c>foreign_key</c></tag>
<item>
- <p>the foreign key.
- </p>
+ <p>The foreign key</p>
</item>
<tag><c>foreigners</c></tag>
<item>
- <p>all other tables that refers to this table in
- their foreign key.
- </p>
+ <p>All other tables that refer to this table in
+ their foreign key</p>
</item>
<tag><c>frag_names</c></tag>
<item>
- <p>the names of all fragments.
- </p>
+ <p>The names of all fragments</p>
</item>
<tag><c>frag_dist</c></tag>
<item>
- <p>a sorted list of <c>{Node, Count}</c> tuples
- which is sorted in increasing <c>Count</c> order. The
+ <p>A sorted list of <c>{Node, Count}</c> tuples
+ that are sorted in increasing <c>Count</c> order.
<c>Count</c> is the total number of replicas that this
fragmented table hosts on each <c>Node</c>. The list
- always contains at least all nodes in the
- <c>node_pool</c>. The nodes which not belongs to the
- <c>node_pool</c> will be put last in the list even if
- their <c>Count</c> is lower.
- </p>
+ always contains at least all nodes in
+ <c>node_pool</c>. Nodes that do not belong to
+ <c>node_pool</c> are put last in the list even if
+ their <c>Count</c> is lower.</p>
</item>
<tag><c>frag_size</c></tag>
<item>
- <p>a list of <c>{Name, Size}</c> tuples where
- <c>Name</c> is a fragment <c>Name</c> and <c>Size</c> is
- how many records it contains.
- </p>
+ <p>A list of <c>{Name, Size}</c> tuples, where
+ <c>Name</c> is a fragment <c>Name</c>, and <c>Size</c> is
+ how many records it contains</p>
</item>
<tag><c>frag_memory</c></tag>
<item>
- <p>a list of <c>{Name, Memory}</c> tuples where
- <c>Name</c> is a fragment <c>Name</c> and <c>Memory</c> is
- how much memory it occupies.
- </p>
+ <p>A list of <c>{Name, Memory}</c> tuples, where
+ <c>Name</c> is a fragment <c>Name</c>, and <c>Memory</c> is
+ how much memory it occupies</p>
</item>
<tag><c>size</c></tag>
<item>
- <p>total size of all fragments
- </p>
+ <p>Total size of all fragments</p>
</item>
<tag><c>memory</c></tag>
<item>
- <p>the total memory of all fragments</p>
+ <p>Total memory of all fragments</p>
</item>
</taglist>
</section>
@@ -642,42 +601,45 @@ ok
<title>Load Balancing</title>
<p>There are several algorithms for distributing records
in a fragmented table evenly over a
- pool of nodes. No one is best, it simply depends of the
- application needs. Here follows some examples of
- situations which may need some attention:
- </p>
- <p><c>permanent change of nodes</c> when a new permanent
- <c>db_node</c> is introduced or dropped, it may be time to
- change the pool of nodes and re-distribute the replicas
- evenly over the new pool of nodes. It may also be time to
- add or delete a fragment before the replicas are re-distributed.
- </p>
- <p><c>size/memory threshold</c> when the total size or
+ pool of nodes. No one is best, it depends on the
+ application needs. The following examples of
+ situations need some attention:</p>
+ <list type="bulleted">
+ <item><c>permanent change of nodes</c>. When a new permanent
+ <c>db_node</c> is introduced or dropped, it can be time to
+ change the pool of nodes and redistribute the replicas
+ evenly over the new pool of nodes. It can also be time to
+ add or delete a fragment before the replicas are redistributed.
+ </item>
+ <item><c>size/memory threshold</c>. When the total size or
total memory of a fragmented table (or a single
- fragment) exceeds some application specific threshold, it
- may be time to dynamically add a new fragment in order
- obtain a better distribution of records.
- </p>
- <p><c>temporary node down</c> when a node temporarily goes
- down it may be time to compensate some fragments with new
- replicas in order to keep the desired level of
- redundancy. When the node comes up again it may be time to
- remove the superfluous replica.
- </p>
- <p><c>overload threshold</c> when the load on some node is
- exceeds some application specific threshold, it may be time to
- either add or move some fragment replicas to nodes with lesser
- load. Extra care should be taken if the table has a foreign
- key relation to some other table. In order to avoid severe
- performance penalties, the same re-distribution must be
- performed for all of the related tables.
- </p>
- <p>Use <c>mnesia:change_table_frag/2</c> to add new fragments
+ fragment) exceeds some application-specific threshold, it
+ can be time to add a new fragment dynamically to
+ obtain a better distribution of records.
+ </item>
+ <item><c>temporary node down</c>. When a node temporarily goes
+ down, it can be time to compensate some fragments with new
+ replicas to keep the desired level of
+ redundancy. When the node comes up again, it can be time to
+ remove the superfluous replica.
+ </item>
+ <item><c>overload threshold</c>. When the load on some node
+ exceeds some application-specific threshold, it can be time to
+ either add or move some fragment replicas to nodes with lower
+ load. Take extra care if the table has a foreign
+ key relation to some other table. To avoid severe
+ performance penalties, the same redistribution must be
+ performed for all the related tables.
+ </item>
+ </list>
+ <p>Use the function
+ <c>mnesia:change_table_frag/2</c> to add new fragments
and apply the usual schema manipulation functions (such as
- <c>mnesia:add_table_copy/3</c>, <c>mnesia:del_table_copy/2</c>
- and <c>mnesia:change_table_copy_type/2</c>) on each fragment
- to perform the actual re-distribution.
- </p>
+ <seealso marker="mnesia#add_table_copy/3">mnesia:add_table_copy/3</seealso>,
+ <seealso marker="mnesia#del_table_copy/2">mnesia:del_table_copy/2</seealso>,
+ and
+ <seealso marker="mnesia#change_table_copy_type/3">mnesia:change_table_copy_type/2</seealso>)
+ on each fragment to perform the actual redistribution.</p>
</section>
</section>
@@ -685,356 +647,369 @@ ok
<title>Local Content Tables</title>
<p>Replicated tables have the same content on all nodes where
they are replicated. However, it is sometimes advantageous to
- have tables but different content on different nodes.
- </p>
- <p>If we specify the attribute <c>{local_content, true}</c> when
- we create the table, the table will reside on the nodes where
- we specify that the table shall exist, but the write operations on the
- table will only be performed on the local copy.
- </p>
- <p>Furthermore, when the table is initialized at start-up, the
- table will only be initialized locally, and the table
- content will not be copied from another node.
- </p>
+ have tables, but different content on different nodes.</p>
+ <p>If attribute <c>{local_content, true}</c> is specified when
+ you create the table, the table resides on the nodes where you
+ specify the table to exist, but the write operations on the
+ table are only performed on the local copy.</p>
+ <p>Furthermore, when the table is initialized at startup, the
+ table is only initialized locally, and the table
+ content is not copied from another node.</p>
</section>
<section>
- <title>Disc-less Nodes</title>
- <p>It is possible to run Mnesia on nodes that do not have a
- disc. It is of course not possible to have replicas
- of neither <c>disc_copies</c>, nor <c>disc_only_copies</c>
- on such nodes. This especially troublesome for the
- <c>schema</c> table since Mnesia need the schema in order
- to initialize itself.
- </p>
- <p>The schema table may, as other tables, reside on one or
- more nodes. The storage type of the schema table may either
- be <c>disc_copies</c> or <c>ram_copies</c>
- (not <c>disc_only_copies</c>). At
- start-up Mnesia uses its schema to determine with which
- nodes it should try to establish contact. If any
- of the other nodes are already started, the starting node
+ <title>Disc-Less Nodes</title>
+ <p><c>Mnesia</c> can be run on nodes that do not have a disc.
+ Replicas of <c>disc_copies</c> or <c>disc_only_copies</c> are
+ not possible on such nodes. This is especially troublesome for
+ the <c>schema</c> table, as <c>Mnesia</c> needs the schema
+ to initialize itself.</p>
+ <p>The schema table can, as other tables, reside on one or
+ more nodes. The storage type of the schema table can either
+ be <c>disc_copies</c> or <c>ram_copies</c>
+ (but not <c>disc_only_copies</c>). At
+ startup, <c>Mnesia</c> uses its schema to determine with which
+ nodes it is to try to establish contact. If any
+ other node is started already, the starting node
merges its table definitions with the table definitions
brought from the other nodes. This also applies to the
- definition of the schema table itself. The application
- parameter <c>extra_db_nodes</c> contains a list of nodes which
- Mnesia also should establish contact with besides the ones
- found in the schema. The default value is the empty list
- <c>[]</c>.
- </p>
+ definition of the schema table itself. Application
+ parameter <c>extra_db_nodes</c> contains a list of nodes that
+ <c>Mnesia</c> also is to establish contact with besides those
+ found in the schema. Default is <c>[]</c> (empty list).</p>
<p>Hence, when a disc-less node needs to find the schema
- definitions from a remote node on the network, we need to supply
- this information through the application parameter <c>-mnesia extra_db_nodes NodeList</c>. Without this
- configuration parameter set, Mnesia will start as a single node
- system. It is also possible to use <c>mnesia:change_config/2</c>
- to assign a value to 'extra_db_nodes' and force a connection
- after mnesia have been started, i.e.
- mnesia:change_config(extra_db_nodes, NodeList).
- </p>
- <p>The application parameter schema_location controls where
- Mnesia will search for its schema. The parameter may be one of
- the following atoms:
- </p>
+ definitions from a remote node on the network, this
+ information must be supplied through application parameter
+ <c>-mnesia extra_db_nodes NodeList</c>. Without this
+ configuration parameter set, <c>Mnesia</c> starts as a single
+ node system. Also, the function
+ <seealso marker="mnesia#change_config/2">mnesia:change_config/2</seealso>
+ can be used to assign a value to <c>extra_db_nodes</c> and force
+ a connection after <c>Mnesia</c> has been started, that is,
+ <c>mnesia:change_config(extra_db_nodes, NodeList)</c>.</p>
+ <p>Application parameter <c>schema_location</c> controls where
+ <c>Mnesia</c> searches for its schema. The parameter 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
- on the Mnesia directory. And if the schema cannot be found,
- Mnesia refuses to start.
- </p>
+ in the <c>Mnesia</c> directory. If the schema cannot be found,
+ <c>Mnesia</c> refuses to start.</p>
</item>
<tag><c>ram</c></tag>
<item>
- <p>Mandatory ram. The schema resides in ram
- only. At start-up a tiny new schema is generated. This
- default schema contains just the definition of the schema
- table and only resides on the local node. Since no other
- nodes are found in the default schema, the configuration
- parameter <c>extra_db_nodes</c> must be used in order to let the
- node share its table definitions with other nodes. (The
- <c>extra_db_nodes</c> parameter may also be used on disc-full nodes.)
- </p>
+ <p>Mandatory RAM. The schema resides in RAM
+ only. At startup, a tiny new schema is generated. This
+ default schema contains only the definition of the schema
+ table and resides on the local node only. Since no other
+ nodes are found in the default schema, configuration
+ parameter <c>extra_db_nodes</c> must be used to let the
+ node share its table definitions with other nodes. (Parameter
+ <c>extra_db_nodes</c> can also be used on disc-full nodes.)</p>
</item>
<tag><c>opt_disc</c></tag>
<item>
- <p>Optional disc. The schema may reside on either disc
- or ram. If the schema is found on disc, Mnesia starts as a
- disc-full node (the storage type of the schema table is
- disc_copies). If no schema is found on disc, Mnesia starts
- as a disc-less node (the storage type of the schema table is
- ram_copies). The default value for the application parameter
- is
- <c>opt_disc</c>. </p>
+ <p>Optional disc. The schema can reside on either disc or
+ RAM. If the schema is found on disc, <c>Mnesia</c> starts as
+ a disc-full node (the storage type of the schema table is
+ disc_copies). If no schema is found on disc, <c>Mnesia</c>
+ starts as a disc-less node (the storage type of the schema
+ table is <c>ram_copies</c>). The default for the
+ application parameter is <c>opt_disc</c>.</p>
</item>
</taglist>
- <p>When the <c>schema_location</c> is set to opt_disc the
- function <c>mnesia:change_table_copy_type/3</c> may be used to
- change the storage type of the schema.
- This is illustrated below:
- </p>
+ <p>When <c>schema_location</c> is set to <c>opt_disc</c>, the
+ function
+ <seealso marker="mnesia#change_table_copy_type/3">mnesia:change_table_copy_type/3</seealso>
+ can be used to change the storage type of the schema.
+ This is illustrated as follows:</p>
<pre>
1> mnesia:start().
ok
2> mnesia:change_table_copy_type(schema, node(), disc_copies).
- {atomic, ok}
- </pre>
- <p>Assuming that the call to <c>mnesia:start</c> did not
- find any schema to read on the disc, then Mnesia has started
- as a disc-less node, and then changed it to a node that
- utilizes the disc to locally store the schema.
- </p>
+ {atomic, ok}</pre>
+ <p>Assuming that the call to
+ <seealso marker="mnesia#start/0">mnesia:start/0</seealso> does not
+ find any schema to read on the disc, <c>Mnesia</c> starts
+ as a disc-less node, and then change it to a node that
+ use the disc to store the schema locally.</p>
</section>
<section>
- <title>More Schema Management</title>
- <p>It is possible to add and remove nodes from a Mnesia system.
- This can be done by adding a copy of the schema to those nodes.
- </p>
- <p>The functions <c>mnesia:add_table_copy/3</c> and
- <c>mnesia:del_table_copy/2</c> may be used to add and delete
- replicas of the schema table. Adding a node to the list
- of nodes where the schema is replicated will affect two
- things. First it allows other tables to be replicated to
- this node. Secondly it will cause Mnesia to try to contact
- the node at start-up of disc-full nodes.
- </p>
- <p>The function call <c>mnesia:del_table_copy(schema, mynode@host)</c> deletes the node 'mynode@host' from the
- Mnesia system. The call fails if mnesia is running on
- 'mynode@host'. The other mnesia nodes will never try to connect
- to that node again. Note, if there is a disc
- resident schema on the node 'mynode@host', the entire mnesia
- directory should be deleted. This can be done with
- <c>mnesia:delete_schema/1</c>. If
- mnesia is started again on the the node 'mynode@host' and the
- directory has not been cleared, mnesia's behaviour is undefined.
- </p>
- <p>If the storage type of the schema is ram_copies, i.e, we
- have disc-less node, Mnesia
- will not use the disc on that particular node. The disc
- usage is enabled by changing the storage type of the table
- <c>schema</c> to disc_copies.
- </p>
- <p>New schemas are
- created explicitly with <c>mnesia:create_schema/1</c> or implicitly
- by starting Mnesia without a disc resident schema. Whenever
- a table (including the schema table) is created it is
- assigned its own unique cookie. The schema table is not created with
- <c>mnesia:create_table/2</c> as normal tables.
- </p>
- <p>At start-up Mnesia connects different nodes to each other,
- then they exchange table definitions with each other and the
- table definitions are merged. During the merge procedure Mnesia
+ <title>More about Schema Management</title>
+ <p>Nodes can be added to and removed from a <c>Mnesia</c> system.
+ This can be done by adding a copy of the schema to those nodes.</p>
+ <p>The functions
+ <seealso marker="mnesia#add_table_copy/3">mnesia:add_table_copy/3</seealso>
+ and
+ <seealso marker="mnesia#del_table_copy/2">mnesia:del_table_copy/2</seealso>
+ can be used to add and delete
+ replicas of the schema table. Adding a node to the list of
+ nodes where the schema is replicated affects the following:</p>
+ <list type="bulleted">
+ <item>It allows other tables to be replicated to this node.
+ </item>
+ <item>It causes <c>Mnesia</c> to try to contact the node at
+ startup of disc-full nodes.
+ </item>
+ </list>
+ <p>The function call <c>mnesia:del_table_copy(schema,
+ mynode@host)</c> deletes node <c>mynode@host</c> from the
+ <c>Mnesia</c> system. The call fails if <c>Mnesia</c> is running
+ on <c>mynode@host</c>. The other <c>Mnesia</c> nodes never try to
+ connect to that node again. Notice that if there is a disc resident
+ schema on node <c>mynode@host</c>, the entire <c>Mnesia</c>
+ directory is to be deleted. This is done with the function
+ <seealso marker="mnesia#delete_schema/1">mnesia:delete_schema/1</seealso>.
+ If <c>Mnesia</c> is started again
+ on node <c>mynode@host</c> and the directory has not been
+ cleared, the behavior of <c>Mnesia</c> is undefined.</p>
+ <p>If the storage type of the schema is <c>ram_copies</c>,
+ that is, a disc-less node, <c>Mnesia</c>
+ does not use the disc on that particular node. The disc
+ use is enabled by changing the storage type of table
+ <c>schema</c> to <c>disc_copies</c>.</p>
+ <p>New schemas are created explicitly with the function
+ <seealso marker="mnesia#create_schema/1">mnesia:create_schema/1</seealso>
+ or implicitly by starting
+ <c>Mnesia</c> without a disc resident schema. Whenever
+ a table (including the schema table) is created, it is
+ assigned its own unique cookie. The schema table is not created
+ with the function
+ <seealso marker="mnesia#create_table/2">mnesia:create_table/2</seealso>
+ as normal tables.</p>
+ <p>At startup, <c>Mnesia</c> connects different nodes to each other,
+ then they exchange table definitions with each other, and the table
+ definitions are merged. During the merge procedure, <c>Mnesia</c>
performs a sanity test to ensure that the table definitions are
- compatible with each other. If a table exists on several nodes
- the cookie must be the same, otherwise Mnesia will shutdown one
- of the nodes. This unfortunate situation will occur if a table
+ compatible with each other. If a table exists on several nodes,
+ the cookie must be the same, otherwise <c>Mnesia</c> shut down one
+ of the nodes. This unfortunate situation occurs if a table
has been created on two nodes independently of each other while
- they were disconnected. To solve the problem, one of the tables
- must be deleted (as the cookies differ we regard it to be two
- different tables even if they happen to have the same name).
- </p>
- <p>Merging different versions of the schema table, does not
+ they were disconnected. To solve this, one of the tables
+ must be deleted (as the cookies differ, it is regarded to be two
+ different tables even if they have the same name).</p>
+ <p>Merging different versions of the schema table does not
always require the cookies to be the same. If the storage
- type of the schema table is disc_copies, the cookie is
- immutable, and all other db_nodes must have the same
- cookie. When the schema is stored as type ram_copies,
+ type of the schema table is <c>disc_copies</c>, the cookie is
+ immutable, and all other <c>db_nodes</c> must have the same
+ cookie. When the schema is stored as type <c>ram_copies</c>,
its cookie can be replaced with a cookie from another node
- (ram_copies or disc_copies). The cookie replacement (during
- merge of the schema table definition) is performed each time
- a RAM node connects to another node.
- </p>
- <p><c>mnesia:system_info(schema_location)</c> and
- <c>mnesia:system_info(extra_db_nodes)</c> may be used to determine
- the actual values of schema_location and extra_db_nodes
- respectively. <c>mnesia:system_info(use_dir)</c> may be used to
- determine whether Mnesia is actually using the Mnesia
- directory. <c>use_dir</c> may be determined even before
- Mnesia is started. The function <c>mnesia:info/0</c> may now be
- used to printout some system information even before Mnesia
- is started. When Mnesia is started the function prints out
- more information.
- </p>
- <p>Transactions which update the definition of a table,
- requires that Mnesia is started on all nodes where the
- storage type of the schema is disc_copies. All replicas of
+ (<c>ram_copies</c> or <c>disc_copies</c>). The cookie replacement
+ (during merge of the schema table definition) is performed each
+ time a RAM node connects to another node.</p>
+ <p>Further, the following applies:</p>
+ <list type ="bulleted">
+ <item><seealso marker="mnesia#system_info/1">mnesia:system_info(schema_location)</seealso>
+ and
+ <seealso marker="mnesia#system_info/1">mnesia:system_info(extra_db_nodes)</seealso>
+ can be used to determine the actual values of <c>schema_location</c>
+ and <c>extra_db_nodes</c>, respectively.
+ </item>
+ <item><seealso marker="mnesia#system_info/1">mnesia:system_info(use_dir)</seealso>
+ can be used to determine whether <c>Mnesia</c> is actually
+ using the <c>Mnesia</c> directory.
+ </item>
+ <item><c>use_dir</c> can be determined even before
+ <c>Mnesia</c> is started.
+ </item>
+ </list>
+ <p>The function <seealso marker="mnesia#info/0">mnesia:info/0</seealso>
+ can now be used to print
+ some system information even before <c>Mnesia</c> is started.
+ When <c>Mnesia</c> is started, the function prints more
+ information.</p>
+ <p>Transactions that update the definition of a table
+ requires that <c>Mnesia</c> is started on all nodes where the
+ storage type of the schema is <c>disc_copies</c>. All replicas of
the table on these nodes must also be loaded. There are a
- few exceptions to these availability rules. Tables may be
- created and new replicas may be added without starting all
- of the disc-full nodes. New replicas may be added before all
- other replicas of the table have been loaded, it will suffice
- when one other replica is active.
- </p>
+ few exceptions to these availability rules:</p>
+ <list type="bulleted">
+ <item>Tables can be created and new replicas can be added
+ without starting all the disc-full nodes.
+ </item>
+ <item>New replicas can be added before all other replicas of
+ the table have been loaded, provided that at least one other
+ replica is active.
+ </item>
+ </list>
</section>
<section>
<marker id="event_handling"></marker>
<title>Mnesia Event Handling</title>
- <p>System events and table events are the two categories of events
- that Mnesia will generate in various situations.
- </p>
- <p>It is possible for user process to subscribe on the
- events generated by Mnesia.
- We have the following two functions:</p>
+ <p>System events and table events are the two event categories
+ that <c>Mnesia</c> generates in various situations.</p>
+ <p>A user process can subscribe on the events generated by
+ <c>Mnesia</c>. The following two functions are provided:</p>
<taglist>
- <tag><c>mnesia:subscribe(Event-Category)</c></tag>
- <item>
- <p>Ensures that a copy of all events of type
- <c>Event-Category</c> are sent to the calling process.
- </p>
- </item>
- <tag><c>mnesia:unsubscribe(Event-Category)</c></tag>
+ <tag><seealso marker="mnesia#subscribe/1">mnesia:subscribe(Event-Category)</seealso>
+ </tag>
+ <item>Ensures that a copy of all events of type
+ <c>Event-Category</c> are sent to the calling process</item>
+ <tag><seealso marker="mnesia#unsubscribe/1">mnesia:unsubscribe(Event-Category)</seealso>
+ </tag>
<item>Removes the subscription on events of type
- <c>Event-Category</c></item>
+ <c>Event-Category</c>
+ </item>
</taglist>
- <p><c>Event-Category</c> may either be the atom <c>system</c>, the atom <c>activity</c>, or
- one of the tuples <c>{table, Tab, simple}</c>, <c>{table, Tab, detailed}</c>. The old event-category <c>{table, Tab}</c> is the same
- event-category as <c>{table, Tab, simple}</c>.
- The subscribe functions activate a subscription
+ <p><c>Event-Category</c> can be either of the following:</p>
+ <list type="bulleted">
+ <item>The atom <c>system</c>
+ </item>
+ <item>The atom <c>activity</c>
+ </item>
+ <item>The tuple <c>{table, Tab, simple}</c>
+ </item>
+ <item>The tuple <c>{table, Tab, detailed}</c>
+ </item>
+ </list>
+ <p>The old event category <c>{table, Tab}</c> is the same
+ event category as <c>{table, Tab, simple}</c>.</p>
+ <p>The subscribe functions activate a subscription
of events. The events are delivered as messages to the process
- evaluating the <c>mnesia:subscribe/1</c> function. The syntax of
- system events is <c>{mnesia_system_event, Event}</c>,
- <c>{mnesia_activity_event, Event}</c> for activity events, and
- <c>{mnesia_table_event, Event}</c> for table events. What the various
- event types mean is described below.</p>
- <p>All system events are subscribed by Mnesia's
- gen_event handler. The default gen_event handler is
- <c>mnesia_event</c>. But it may be changed by using the application
- parameter <c>event_module</c>. The value of this parameter must be
- the name of a module implementing a complete handler
- as specified by the <c>gen_event</c> module in
- STDLIB. <c>mnesia:system_info(subscribers)</c> and
- <c>mnesia:table_info(Tab, subscribers)</c> may be used to determine
- which processes are subscribed to various
- events.
- </p>
+ evaluating the function
+ <seealso marker="mnesia#subscribe/1">mnesia:subscribe/1</seealso>
+ The syntax is as follows:</p>
+ <list type="bulleted">
+ <item><c>{mnesia_system_event, Event}</c> for system events
+ </item>
+ <item><c>{mnesia_activity_event, Event}</c> for activity events
+ </item>
+ <item><c>{mnesia_table_event, Event}</c> for table events
+ </item>
+ </list>
+ <p>The event types are described in the next sections.</p>
+ <p>All system events are subscribed by the <c>Mnesia</c>
+ <c>gen_event</c> handler. The default <c>gen_event</c> handler
+ is <c>mnesia_event</c>, but it can be changed by using
+ application parameter <c>event_module</c>. The value of this
+ parameter must be the name of a module implementing a complete
+ handler, as specified by the
+ <seealso marker="stdlib:gen_event">gen_event</seealso> module
+ in <c>STDLIB</c>.</p>
+ <p><seealso marker="mnesia#system_info/1">mnesia:system_info(subscribers)</seealso>
+ and
+ <seealso marker="mnesia#table_info/2">mnesia:table_info(Tab, subscribers)</seealso>
+ can be used to determine which processes are subscribed to
+ various events.</p>
<section>
<title>System Events</title>
- <p>The system events are detailed below:</p>
+ <p>The system events are as follows:</p>
<taglist>
<tag><c>{mnesia_up, Node}</c></tag>
- <item>
- <p>Mnesia has been started on a node.
- Node is the name of the node. By default this event is ignored.
- </p>
+ <item>Mnesia is started on a node. <c>Node</c> is the node
+ name. By default this event is ignored.
</item>
<tag><c>{mnesia_down, Node}</c></tag>
- <item>
- <p>Mnesia has been stopped on a node.
- Node is the name of the node. By default this event is
- ignored.
- </p>
+ <item>Mnesia is stopped on a node. <c>Node</c> is the node
+ name. By default this event is ignored.
</item>
<tag><c>{mnesia_checkpoint_activated, Checkpoint}</c></tag>
- <item>
- <p>a checkpoint with the name
- <c>Checkpoint</c> has been activated and that the current node is
- involved in the checkpoint. Checkpoints may be activated
- explicitly with <c>mnesia:activate_checkpoint/1</c> or implicitly
- at backup, adding table replicas, internal transfer of data
- between nodes etc. By default this event is ignored.
- </p>
+ <item>A checkpoint with the name <c>Checkpoint</c> is
+ activated and the current node is involved in the
+ checkpoint. Checkpoints can be activated explicitly with
+ the function
+ <seealso marker="mnesia#activate_checkpoint/1">mnesia:activate_checkpoint/1</seealso>
+ or implicitly at
+ backup, when adding table replicas, at internal transfer of
+ data between nodes, and so on. By default this event is
+ ignored.
</item>
<tag><c>{mnesia_checkpoint_deactivated, Checkpoint}</c></tag>
- <item>
- <p>A checkpoint with the name
- <c>Checkpoint</c> has been deactivated and that the current node was
- involved in the checkpoint. Checkpoints may explicitly be
- deactivated with <c>mnesia:deactivate/1</c> or implicitly when the
- last replica of a table (involved in the checkpoint)
- becomes unavailable, e.g. at node down. By default this
- event is ignored.
- </p>
+ <item>A checkpoint with the name <c>Checkpoint</c> is
+ deactivated and the current node is involved in the
+ checkpoint. Checkpoints can be deactivated explicitly with
+ the function
+ <seealso marker="mnesia#deactivate_checkpoint/1">mnesia:deactivate/1</seealso>
+ or implicitly when the last
+ replica of a table (involved in the checkpoint) becomes
+ unavailable, for example, at node-down. By default this
+ event is ignored.
</item>
<tag><c>{mnesia_overload, Details}</c></tag>
- <item>
- <p>Mnesia on the current node is
- overloaded and the subscriber should take action.
- </p>
+ <item><p><c>Mnesia</c> on the current node is
+ overloaded and the subscriber is to take action.</p>
<p>A typical overload situation occurs when the
- applications are performing more updates on disc
- resident tables than Mnesia is able to handle. Ignoring
- this kind of overload may lead into a situation where
+ applications perform more updates on disc resident
+ tables than <c>Mnesia</c> can handle. Ignoring
+ this kind of overload can lead to a situation where
the disc space is exhausted (regardless of the size of
- the tables stored on disc).
- <br></br>
- Each update is appended to
- the transaction log and occasionally(depending of how it
+ the tables stored on disc).</p>
+ <p>Each update is appended to the transaction log and
+ occasionally (depending on how it
is configured) dumped to the tables files. The
table file storage is more compact than the transaction
log storage, especially if the same record is updated
- over and over again. If the thresholds for dumping the
- transaction log have been reached before the previous
- dump was finished an overload event is triggered.
- </p>
+ repeatedly. If the thresholds for dumping the
+ transaction log are reached before the previous
+ dump is finished, an overload event is triggered.</p>
<p>Another typical overload situation is when the
transaction manager cannot commit transactions at the
- same pace as the applications are performing updates of
- disc resident tables. When this happens the message
- queue of the transaction manager will continue to grow
+ same pace as the applications perform updates of
+ disc resident tables. When this occurs, the message
+ queue of the transaction manager continues to grow
until the memory is exhausted or the load
- decreases.
- </p>
- <p>The same problem may occur for dirty updates. The overload
- is detected locally on the current node, but its cause may
- be on another node. Application processes may cause heavy
- loads if any table are residing on other nodes (replicated or not). By default this event
- is reported to the error_logger.
- </p>
+ decreases.</p>
+ <p>The same problem can occur for dirty updates. The overload
+ is detected locally on the current node, but its cause can
+ be on another node. Application processes can cause high
+ load if any table resides on another node (replicated
+ or not). By default this event
+ is reported to <c>error_logger.</c></p>
</item>
<tag><c>{inconsistent_database, Context, Node}</c></tag>
- <item>
- <p>Mnesia regards the database as
- potential inconsistent and gives its applications a chance
- to recover from the inconsistency, e.g. by installing a
- consistent backup as fallback and then restart the system
- or pick a <c>MasterNode</c> from <c>mnesia:system_info(db_nodes)</c>)
- and invoke <c>mnesia:set_master_node([MasterNode])</c>. By default an
- error is reported to the error logger.
- </p>
+ <item><c>Mnesia</c> regards the database as potential
+ inconsistent and gives its applications a chance to
+ recover from the inconsistency. For example, by installing a
+ consistent backup as fallback and then restart the system.
+ An alternative is to pick a <c>MasterNode</c> from
+ <seealso marker="mnesia#system_info/1">mnesia:system_info(db_nodes)</seealso>
+ and invoke
+ <seealso marker="mnesia#set_master_nodes/1">mnesia:set_master_node([MasterNode])</seealso>.
+ By default an error is reported to <c>error_logger</c>.
</item>
<tag><c>{mnesia_fatal, Format, Args, BinaryCore}</c></tag>
<item>
- <p>Mnesia has encountered a fatal error
- and will (in a short period of time) be terminated. The reason for
- the fatal error is explained in Format and Args which may
- be given as input to <c>io:format/2</c> or sent to the
- error_logger. By default it will be sent to the
- error_logger. <c>BinaryCore</c> is a binary containing a summary of
- Mnesia's internal state at the time the when the fatal error was
- encountered. By default the binary is written to a
- unique file name on current directory. On RAM nodes the
- core is ignored.
- </p>
+ <p><c>Mnesia</c> detected a fatal error and
+ terminates soon. The fault reason is explained in
+ <c>Format</c> and <c>Args</c>, which can be given as input
+ to <c>io:format/2</c> or sent to <c>error_logger</c>. By
+ default it is sent to <c>error_logger</c>.</p>
+ <p><c>BinaryCore</c> is a binary containing a summary of the
+ <c>Mnesia</c> internal state at the time when the fatal
+ error was detected. By default the binary is written to a
+ unique filename on the current directory. On RAM nodes, the
+ core is ignored.</p>
</item>
<tag><c>{mnesia_info, Format, Args}</c></tag>
- <item>
- <p>Mnesia has detected something that
- may be of interest when debugging the system. This is explained
- in <c>Format</c> and <c>Args</c> which may appear
- as input to <c>io:format/2</c> or sent to the error_logger. By
- default this event is printed with <c>io:format/2</c>.
- </p>
+ <item><c>Mnesia</c> detected something that can be of
+ interest when debugging the system. This is explained in
+ <c>Format</c> and <c>Args</c>, which can appear as input
+ to <c>io:format/2</c> or sent to <c>error_logger</c>. By
+ default this event is printed with <c>io:format/2</c>.
</item>
<tag><c>{mnesia_error, Format, Args}</c></tag>
- <item>
- <p>Mnesia has encountered an error. The
- reason for the error is explained i <c>Format</c> and <c>Args</c>
- which may be given as input to <c>io:format/2</c> or sent to the
- error_logger. By default this event is reported to the error_logger.
- </p>
+ <item><c>Mnesia</c> has detected an error. The fault reason is
+ explained in <c>Format</c> and <c>Args</c>, which can be
+ given as input to <c>io:format/2</c> or sent to
+ <c>error_logger</c>. By default this event is reported to
+ <c>error_logger</c>.
</item>
<tag><c>{mnesia_user, Event}</c></tag>
- <item>
- <p>An application has invoked the
- function <c>mnesia:report_event(Event)</c>. <c>Event</c> may be any Erlang
- data structure. When tracing a system of Mnesia applications
- it is useful to be able to interleave Mnesia's own events with
- application related events that give information about the
- application context. Whenever the application starts with
- a new and demanding Mnesia activity or enters a
- new and interesting phase in its execution it may be a good idea
- to use <c>mnesia:report_event/1</c>. </p>
+ <item>An application started the function
+ <seealso marker="mnesia#report_event/1">mnesia:report_event(Event)</seealso>.
+ <c>Event</c> can be
+ any Erlang data structure. When tracing a system of
+ <c>Mnesia</c> applications, it is useful to be able to
+ interleave own events of <c>Mnesia</c> with application-related
+ events that give information about the application context.
+ Whenever the application starts with a new and demanding
+ <c>Mnesia</c> activity, or enters a new and interesting
+ phase in its execution, it can be a good idea to use
+ <c>mnesia:report_event/1</c>.
</item>
</taglist>
</section>
@@ -1045,80 +1020,86 @@ ok
<taglist>
<tag><c>{complete, ActivityID}</c></tag>
<item>
- <p>This event occurs when a transaction that caused a modification to the database
- has completed. It is useful for determining when a set of table events
- (see below) caused by a given activity have all been sent. Once the this event
- has been received, it is guaranteed that no further table events with the same
- ActivityID will be received. Note that this event may still be received even
- if no table events with a corresponding ActivityID were received, depending on
+ <p>This event occurs when a transaction that caused a modification
+ to the database is completed. It is useful for determining when
+ a set of table events (see the next section), caused by a given
+ activity, have been sent. Once this event is received, it is
+ guaranteed that no further table events with the same
+ <c>ActivityID</c> will be received. Notice that this event can
+ still be received even if no table events with a corresponding
+ <c>ActivityID</c> were received, depending on
the tables to which the receiving process is subscribed.</p>
- <p>Dirty operations always only contain one update and thus no activity event is sent.</p>
+ <p>Dirty operations always contain only one update and thus no
+ activity event is sent.</p>
</item>
</taglist>
</section>
<section>
<title>Table Events</title>
- <p>The final category of events are table events, which are
- events related to table updates. There are two types of table
- events simple and detailed.
- </p>
- <p>The simple table events are tuples looking like this:
- <c>{Oper, Record, ActivityId}</c>. Where <c>Oper</c> is the
- operation performed. <c>Record</c> is the record involved in the
- operation and <c>ActivityId</c> is the identity of the
- transaction performing the operation. Note that the name of the
- record is the table name even when the <c>record_name</c> has
- another setting. The various table related events that may
- occur are:
- </p>
+ <p>Table events are events related to table updates. There are
+ two types of table events, simple and detailed.</p>
+ <p>The <em>simple table events</em> are tuples like
+ <c>{Oper, Record, ActivityId}</c>, where:</p>
+ <list type="bulleted">
+ <item><c>Oper</c> is the operation performed.
+ </item>
+ <item><c>Record</c> is the record involved in the operation.
+ </item>
+ <item><c>ActivityId</c> is the identity of the transaction
+ performing the operation.
+ </item>
+ </list>
+ <p>Notice that the record name is the table name even when
+ <c>record_name</c> has another setting.</p>
+ <p>The table-related events that can occur are as follows:</p>
<taglist>
<tag><c>{write, NewRecord, ActivityId}</c></tag>
- <item>
- <p>a new record has been written.
- NewRecord contains the new value of the record.
- </p>
+ <item>A new record has been written. <c>NewRecord</c> contains
+ the new record value.
</item>
<tag><c>{delete_object, OldRecord, ActivityId}</c></tag>
- <item>
- <p>a record has possibly been deleted
- with <c>mnesia:delete_object/1</c>. <c>OldRecord</c>
- contains the value of the old record as stated as argument
- by the application. Note that, other records with the same
- key may be remaining in the table if it is a bag.
- </p>
+ <item>A record has possibly been deleted with
+ <seealso marker="mnesia#delete_object/1">mnesia:delete_object/1</seealso>.
+ <c>OldRecord</c>
+ contains the value of the old record, as stated as argument
+ by the application. Notice that other records with the same
+ key can remain in the table if it is of type <c>bag</c>.
</item>
<tag><c>{delete, {Tab, Key}, ActivityId}</c></tag>
- <item>
- <p>one or more records possibly has
- been deleted. All records with the key Key in the table
- <c>Tab</c> have been deleted. </p>
+ <item>One or more records have possibly been deleted.
+ All records with the key <c>Key</c> in the table
+ <c>Tab</c> have been deleted.
</item>
</taglist>
- <p>The detailed table events are tuples looking like
- this: <c>{Oper, Table, Data, [OldRecs], ActivityId}</c>.
- Where <c>Oper</c> is the operation
- performed. <c>Table</c> is the table involved in the operation,
- <c>Data</c> is the record/oid written/deleted.
- <c>OldRecs</c> is the contents before the operation.
- and <c>ActivityId</c> is the identity of the transaction
- performing the operation.
- The various table related events that may occur are:
- </p>
+ <p>The <em>detailed table events</em> are tuples like
+ <c>{Oper, Table, Data, [OldRecs], ActivityId}</c>, where:</p>
+ <list type="bulleted">
+ <item><c>Oper</c> is the operation performed.
+ </item>
+ <item><c>Table</c> is the table involved in the operation.
+ </item>
+ <item><c>Data</c> is the record/OID written/deleted.
+ </item>
+ <item><c>OldRecs</c> is the contents before the operation.
+ </item>
+ <item><c>ActivityId</c> is the identity of the transaction
+ performing the operation.
+ </item>
+ </list>
+ <p>The table-related events that can occur are as follows:</p>
<taglist>
<tag><c>{write, Table, NewRecord, [OldRecords], ActivityId}</c></tag>
- <item>
- <p>a new record has been written.
- NewRecord contains the new value of the record and OldRecords
- contains the records before the operation is performed.
- Note that the new content is dependent on the type of the table.</p>
+ <item>A new record has been written. <c>NewRecord</c> contains
+ the new record value and <c>OldRecords</c> contains the
+ records before the operation is performed. Notice that the
+ new content depends on the table type.
</item>
<tag><c>{delete, Table, What, [OldRecords], ActivityId}</c></tag>
- <item>
- <p>records has possibly been deleted
- <c>What</c> is either {Table, Key} or a record {RecordName, Key, ...}
- that was deleted.
- Note that the new content is dependent on the type of the table.</p>
+ <item>Records have possibly been deleted. <c>What</c> is
+ either <c>{Table, Key}</c> or a record
+ <c>{RecordName, Key, ...}</c> that was deleted. Notice
+ that the new content depends on the table type.
</item>
</taglist>
</section>
@@ -1126,69 +1107,55 @@ ok
<section>
<title>Debugging Mnesia Applications</title>
- <p>Debugging a Mnesia application can be difficult due to a number of reasons, primarily related
+ <p>Debugging a <c>Mnesia</c> application can be difficult
+ for various reasons, primarily related
to difficulties in understanding how the transaction
- and table load mechanisms work. An other source of
- confusion may be the semantics of nested transactions.
- </p>
- <p>We may set the debug level of Mnesia by calling:
- </p>
- <list type="bulleted">
- <item><c>mnesia:set_debug_level(Level)</c></item>
- </list>
- <p>Where the parameter <c>Level</c> is:
- </p>
+ and table load mechanisms work. Another source of
+ confusion can be the semantics of nested transactions.</p>
+ <p>The debug level of <c>Mnesia</c> is set by calling the function
+ <seealso marker="mnesia#set_debug_level/1">mnesia:set_debug_level(Level)</seealso>,
+ where <c>Level</c>is one of the following:</p>
<taglist>
<tag><c>none</c></tag>
- <item>
- <p>no trace outputs at all. This is the default.
- </p>
+ <item>No trace outputs. This is the default.
</item>
<tag><c>verbose</c></tag>
- <item>
- <p>activates tracing of important debug events. These
- debug events will generate <c>{mnesia_info, Format, Args}</c>
- system events. Processes may subscribe to these events with
- <c>mnesia:subscribe/1</c>. The events are always sent to Mnesia's
- event handler.
- </p>
+ <item>Activates tracing of important debug events. These
+ events generate <c>{mnesia_info, Format, Args}</c>
+ system events. Processes can subscribe to these events with
+ the function
+ <seealso marker="mnesia#subscribe/1">mnesia:subscribe/1</seealso>.
+ The events are always sent to the <c>Mnesia</c> event handler.
</item>
<tag><c>debug</c></tag>
- <item>
- <p>activates all events at the verbose level plus
- traces of all debug events. These debug events will generate
- <c>{mnesia_info, Format, Args}</c> system events. Processes may
- subscribe to these events with <c>mnesia:subscribe/1</c>. The
- events are always sent to Mnesia's event handler. On this
- debug level Mnesia's event handler starts subscribing
- updates in the schema table.
- </p>
+ <item>Activates all events at the verbose level plus
+ traces of all debug events. These debug 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.
+ On this debug level, the <c> Mnesia</c> event handler starts
+ subscribing to updates in the schema table.
</item>
<tag><c>trace</c></tag>
- <item>
- <p>activates all events at the debug level. On this
- debug level Mnesia's event handler starts subscribing
- updates on all Mnesia tables. This level is only intended
- for debugging small toy systems, since many large
- events may be generated.</p>
+ <item>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 intended
+ only for debugging small toy systems, as many large
+ events can be generated.
</item>
<tag><c>false</c></tag>
- <item>
- <p>is an alias for none.</p>
+ <item>An alias for none.
</item>
<tag><c>true</c></tag>
- <item>
- <p>is an alias for debug.</p>
+ <item>An alias for debug.
</item>
</taglist>
- <p>The debug level of Mnesia itself, is also an application
- parameter, thereby making it possible to start an Erlang system
- in order to turn on Mnesia debug in the initial
- start-up phase by using the following code:
- </p>
+ <p>The debug level of <c>Mnesia</c> itself is also an application
+ parameter, making it possible to start an Erlang system
+ to turn on <c>Mnesia</c> debug in the initial
+ startup phase by using the following code:</p>
<pre>
- % erl -mnesia debug verbose
- </pre>
+ % erl -mnesia debug verbose</pre>
</section>
<section>
@@ -1196,85 +1163,81 @@ ok
<p>Programming concurrent Erlang systems is the subject of
a separate book. However, it is worthwhile to draw attention to
the following features, which permit concurrent processes to
- exist in a Mnesia system.
- </p>
- <p>A group of functions or processes can be called within a
- transaction. A transaction may include statements that read,
- write or delete data from the DBMS. A large number of such
+ exist in a <c>Mnesia</c> system:</p>
+ <list type="bulleted">
+ <item><p>A group of functions or processes can be called within a
+ transaction. A transaction can include statements that read,
+ write, or delete data from the DBMS. Many such
transactions can run concurrently, and the programmer does not
- have to explicitly synchronize the processes which manipulate
- the data. All programs accessing the database through the
- transaction system may be written as if they had sole access to
- the data. This is a very desirable property since all
+ need to explicitly synchronize the processes that manipulate
+ the data.</p>
+ <p>All programs accessing the database through the
+ transaction system can be written as if they had sole access to
+ the data. This is a desirable property, as all
synchronization is taken care of by the transaction handler. If
a program reads or writes data, the system ensures that no other
- program tries to manipulate the same data at the same time.
- </p>
- <p>It is possible to move tables, delete tables or reconfigure
- the layout of a table in various ways. An important aspect of
- the actual implementation of these functions is that it is
- possible for user programs to continue to use a table while it
- is being reconfigured. For example, it is possible to
- simultaneously move a table and perform write operations to the
- table . This is important for many applications that
- require continuously available services. Refer to Chapter 4:
- <seealso marker="Mnesia_chap4#trans_prop">Transactions and other access contexts</seealso> for more information.
- </p>
+ program tries to manipulate the same data at the same time.</p>
+ </item>
+ <item>Tables can be moved or deleted, and the layout of a table
+ can be reconfigured in various ways. An important aspect of
+ the implementation of these functions is that user programs
+ can continue to use a table while it
+ is being reconfigured. For example, it is possible to move a
+ table and perform write operations to the table at the same
+ time. This is important for many applications that require
+ continuously available services. For more information, see
+ <seealso marker="Mnesia_chap4#trans_prop">Transactions and Other Access Contexts</seealso>.
+ </item>
+ </list>
</section>
<section>
<title>Prototyping</title>
- <p>If and when we decide that we would like to start and manipulate
- Mnesia, it is often easier to write the definitions and
+ <p>If and when you would like to start and manipulate
+ <c>Mnesia</c>, it is often easier to write the definitions and
data into an ordinary text file.
Initially, no tables and no data exist, or which
- tables are required. At the initial stages of prototyping it
- is prudent write all data into one file, process
- that file and have the data in the file inserted into the database.
- It is possible to initialize Mnesia with data read from a text file.
- We have the following two functions to work with text files.
- </p>
+ tables are required. At the initial stages of prototyping, it
+ is prudent to write all data into one file, process that
+ file, and have the data in the file inserted into the database.
+ <c>Mnesia</c> can be initialized with data read from a text file.
+ The following two functions can be used to work with text
+ files.</p>
<list type="bulleted">
<item>
- <p><c>mnesia:load_textfile(Filename)</c> Which loads a
- series of local table definitions and data found in the file
- into Mnesia. This function also starts Mnesia and possibly
- creates a new schema. The function only operates on the
- local node.
- </p>
+ <seealso marker="mnesia#load_textfile/1">mnesia:load_textfile(Filename)</seealso>
+ loads a series of local table definitions and data found in the
+ file into <c>Mnesia</c>. This function also starts <c>Mnesia</c>
+ and possibly creates a new schema. The function operates
+ on the local node only.
</item>
<item>
- <p><c>mnesia:dump_to_textfile(Filename)</c> Dumps
- all local tables of a mnesia system into a text file which can
- then be edited (by means of a normal text editor) and then
- later reloaded.</p>
+ <seealso marker="mnesia#dump_to_textfile/1">mnesia:dump_to_textfile(Filename)</seealso>
+ dumps all local
+ tables of a <c>Mnesia</c> system into a text file, which
+ can be edited (with a normal text editor) and later reloaded.
</item>
</list>
- <p>These functions are of course much slower than the ordinary
- store and load functions of Mnesia. However, this is mainly intended for minor experiments
- and initial prototyping. The major advantages of these functions is that they are very easy
- to use.
- </p>
- <p>The format of the text file is:
- </p>
+ <p>These functions are much slower than the ordinary store and
+ load functions of <c>Mnesia</c>. However, this is mainly intended
+ for minor experiments and initial prototyping. The major
+ advantage of these functions is that they are easy to use.</p>
+ <p>The format of the text file is as follows:</p>
<pre>
{tables, [{Typename, [Options]},
{Typename2 ......}]}.
- {Typename, Attribute1, Atrribute2 ....}.
- {Typename, Attribute1, Atrribute2 ....}.
- </pre>
+ {Typename, Attribute1, Attribute2 ....}.
+ {Typename, Attribute1, Attribute2 ....}.</pre>
<p><c>Options</c> is a list of <c>{Key,Value}</c> tuples conforming
- to the options we could give to <c>mnesia:create_table/2</c>.
- </p>
- <p>For example, if we want to start playing with a small
- database for healthy foods, we enter then following data into
- the file <c>FRUITS</c>.
- </p>
+ to the options that you can give to
+ <seealso marker="mnesia#create_table/2">mnesia:create_table/2</seealso>.
+ </p>
+ <p>For example, to start playing with a small database for healthy
+ foods, enter the following data into file <c>FRUITS</c>:</p>
<codeinclude file="FRUITS" tag="%0" type="erl"></codeinclude>
- <p>The following session with the Erlang shell then shows how
- to load the fruits database.
- </p>
+ <p>The following session with the Erlang shell shows how
+ to load the <c>FRUITS</c> database:</p>
<pre><![CDATA[
% erl
Erlang (BEAM) emulator version 4.9
@@ -1311,54 +1274,49 @@ ok
ok
3>
]]></pre>
- <p>Where we can see that the DBMS was initiated from a
- regular text file.
- </p>
+ <p>It can be seen that the DBMS was initiated from a
+ regular text file.</p>
</section>
<section>
- <title>Object Based Programming with Mnesia</title>
- <p>The Company database introduced in Chapter 2 has three tables
- which store records (employee, dept, project), and three tables
- which store relationships (manager, at_dep, in_proj). This is a
- normalized data model, which has some advantages over a
- non-normalized data model.
- </p>
- <p>It is more efficient to do a
+ <title>Object-Based Programming with Mnesia</title>
+ <p>The <c>Company</c> database, introduced in
+ <seealso marker="Mnesia_chap2#getting_started">Getting Started</seealso>,
+ has three tables that store records (<c>employee</c>,
+ <c>dept</c>, <c>project</c>), and three tables that store
+ relationships (<c>manager</c>, <c>at_dep</c>, <c>in_proj</c>).
+ This is a normalized data model, which has some advantages over
+ a non-normalized data model.</p>
+ <p>It is more efficient to do a
generalized search in a normalized database. Some operations are
also easier to perform on a normalized data model. For example,
- we can easily remove one project, as the following example
- illustrates:
- </p>
+ one project can easily be removed, as the following example
+ illustrates:</p>
<codeinclude file="company.erl" tag="%13" type="erl"></codeinclude>
<p>In reality, data models are seldom fully normalized. A
realistic alternative to a normalized database model would be
- a data model which is not even in first normal form. Mnesia
- is very suitable for applications such as telecommunications,
- because it is easy to organize data in a very flexible manner. A
- Mnesia database is always organized as a set of tables. Each
- table is filled with rows/objects/records. What sets Mnesia
- apart is that individual fields in a record can contain any type
- of compound data structures. An individual field in a record can
- contain lists, tuples, functions, and even record code.
- </p>
+ a data model that is not even in first normal form. <c>Mnesia</c>
+ is suitable for applications such as telecommunications,
+ because it is easy to organize data in a flexible manner. A
+ <c>Mnesia</c> database is always organized as a set of tables.
+ Each table is filled with rows, objects, and records.
+ What sets <c>Mnesia</c> apart is that individual fields in
+ a record can contain any type of
+ compound data structures. An individual field in a record can
+ contain lists, tuples, functions, and even record code.</p>
<p>Many telecommunications applications have unique requirements
- on lookup times for certain types of records. If our Company
- database had been a part of a telecommunications system, then it
- could be that the lookup time of an employee <em>together</em>
- with a list of the projects the employee is working on, should
- be minimized. If this was the case, we might choose a
- drastically different data model which has no direct
- relationships. We would only have the records themselves, and
- different records could contain either direct references to
- other records, or they could contain other records which are not
- part of the Mnesia schema.
- </p>
- <p>We could create the following record definitions:
- </p>
+ on lookup times for certain types of records. If the <c>Company</c>
+ database had been a part of a telecommunications system, it
+ could be to minimize the lookup time of an employee
+ <em>together</em> with a list of the projects the employee is
+ working on. If this is the case, a drastically different data model
+ without direct relationships can be chosen. You would then have
+ only the records themselves, and different records could contain
+ either direct references to other records, or contain other
+ records that are not part of the <c>Mnesia</c> schema.</p>
+ <p>The following record definitions can be created:</p>
<codeinclude file="company_o.hrl" tag="%0" type="erl"></codeinclude>
- <p>An record which describes an employee might look like this:
- </p>
+ <p>A record that describes an employee can look as follows:</p>
<pre>
Me = #employee{emp_no= 104732,
name = klacke,
@@ -1368,50 +1326,43 @@ ok
room_no = {221, 015},
dept = 'B/SFR',
projects = [erlang, mnesia, otp],
- manager = 114872},
- </pre>
- <p>This model only has three different tables, and the employee
- records contain references to other records. We have the following
- references in the record.
- </p>
+ manager = 114872},</pre>
+ <p>This model has only three different tables, and the employee
+ records contain references to other records. The record has the
+ following references:</p>
<list type="bulleted">
- <item><c>'B/SFR'</c> refers to a <c>dept</c> record.
+ <item><c>'B/SFR'</c> refers to a <c>dept</c> record.
</item>
- <item><c>[erlang, mnesia, otp]</c>. This is a list of three
- direct references to three different <c>projects</c> records.
+ <item><c>[erlang, mnesia, otp]</c> is a list of three
+ direct references to three different <c>projects</c> records.
</item>
- <item><c>114872</c>. This refers to another employee record.
+ <item><c>114872</c> refers to another employee record.
</item>
</list>
- <p>We could also use the Mnesia record identifiers (<c>{Tab, Key}</c>)
- as references. In this case, the <c>dept</c> attribute would be
- set to the value <c>{dept, 'B/SFR'}</c> instead of
- <c>'B/SFR'</c>.
- </p>
+ <p>The <c>Mnesia</c> record identifiers (<c>{Tab, Key}</c>) can
+ also be used as references. In this case, attribute <c>dept</c>
+ would be set to value <c>{dept, 'B/SFR'}</c> instead of
+ <c>'B/SFR'</c>.</p>
<p>With this data model, some operations execute considerably
- faster than they do with the normalized data model in our
- Company database. On the other hand, some other operations
+ faster than they do with the normalized data model in the
+ <c>Company</c> database. However, some other operations
become much more complicated. In particular, it becomes more
difficult to ensure that records do not contain dangling
- pointers to other non-existent, or deleted, records.
- </p>
+ pointers to other non-existent, or deleted, records.</p>
<p>The following code exemplifies a search with a non-normalized
- data model. To find all employees at department
- <c>Dep</c> with a salary higher than <c>Salary</c>, use the following code:
- </p>
+ data model. To find all employees at department <c>Dep</c> with
+ a salary higher than <c>Salary</c>, use the following code:</p>
<codeinclude file="company_o.erl" tag="%9" type="erl"></codeinclude>
- <p>This code is not only easier to write and to understand, but it
- also executes much faster.
- </p>
- <p>It is easy to show examples of code which executes faster if
- we use a non-normalized data model, instead of a normalized
- model. The main reason for this is that fewer tables are
- required. For this reason, we can more easily combine data from
- different tables in join operations. In the above example, the
- <c>get_emps/2</c> function was transformed from a join operation
- into a simple query which consists of a selection and a projection
- on one single table.
- </p>
+ <p>This code is easier to write and to understand, and it
+ also executes much faster.</p>
+ <p>It is easy to show examples of code that executes faster if
+ a non-normalized data model is used, instead of a normalized
+ model. The main reason is that fewer tables are required.
+ Therefore, data from different tables can more easily be
+ combined in join operations. In the previous example, the
+ function <c>get_emps/2</c> is transformed from a join operation
+ into a simple query, which consists of a selection and a
+ projection on one single table.</p>
</section>
</chapter>