diff options
Diffstat (limited to 'lib/mnesia/doc')
49 files changed, 11521 insertions, 0 deletions
diff --git a/lib/mnesia/doc/html/.gitignore b/lib/mnesia/doc/html/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/mnesia/doc/html/.gitignore diff --git a/lib/mnesia/doc/man3/.gitignore b/lib/mnesia/doc/man3/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/mnesia/doc/man3/.gitignore diff --git a/lib/mnesia/doc/man6/.gitignore b/lib/mnesia/doc/man6/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/mnesia/doc/man6/.gitignore diff --git a/lib/mnesia/doc/misc/Makefile b/lib/mnesia/doc/misc/Makefile new file mode 100644 index 0000000000..e5fa327f5b --- /dev/null +++ b/lib/mnesia/doc/misc/Makefile @@ -0,0 +1,64 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1996-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +# +OTP_MAKE_ROOT=/home/super/otp/otp_make +include $(OTP_MAKE_ROOT)/otp.mk + +# +# Release Macros +# + +# +# Common macros +# + + +SGML_FILES= release_notes.sgml + +FIG_FILES = +HTML_FILES= $(SGML_FILES:.sgml=.html) +TEX_FILES= $(SGML_FILES:.sgml=.tex) +DVI_FILES= $(SGML_FILES:.sgml=.dvi) +PSFIG_FILES= $(FIG_FILES:.fig=.ps) +PS_FILES= $(SGML_FILES:.sgml=.ps) +GIF_FILES= min_head.gif +ERL_FILES= +HRL_FILES= +DATA_FILES= + + +# +# Make Rules +# + +all $(DEFAULT_OPT_TARGETS) $(DEFAULT_DEBUG_TARGETS): $(HTML_FILES) $(TEX_FILES) $(PSFIG_FILES) $(DVI_FILES) $(PS_FILES) + +clean: + @rm -f *.toc *.log *.aux *.tex sgmls_output sgmls_errs $(HTML_FILES) $(TEX_FILES) $(DVI_FILES) $(PSFIG_FILES) $(PS_FILES) + +# +# Release Targets +# +include $(OTP_MAKE_ROOT)/otp_release_targets.mk + +release_variant: opt.$(TARGET) + $(MKPATH.$(TARGET)) $(RELEASE_PATH)/$(TARGET)/lib/mnesia-$(VSN)/doc/misc + $(INSTALLFILES.$(TARGET)) $(HTML_FILES) $(GIF_FILES) $(ERL_FILES) $(HRL_FILES) $(DATA_FILES) $(PS_FILES) $(RELEASE_PATH)/$(TARGET)/lib/mnesia-$(VSN)/doc/misc + diff --git a/lib/mnesia/doc/misc/implementation.txt b/lib/mnesia/doc/misc/implementation.txt new file mode 100644 index 0000000000..1b8369e466 --- /dev/null +++ b/lib/mnesia/doc/misc/implementation.txt @@ -0,0 +1,375 @@ + +Mnesia + +1 Introduction + +This document aims to give a brief introduction of the implementation +of mnesia, it's data and functions. + +H�kan has written other mnesia papers of interest, (see ~hakan/public_html/): +o Resource consumption (mnesia_consumption.txt) +o What to think about when changing mnesia (mnesia_upgrade_policy.txt) +o Mnesia internals course (mnesia_internals_slides.pdf) +o Mnesia overview (mnesia_overview.pdf) + +1.1. Basic concepts + +In a mnesia cluster all nodes are equal, there is no concept off +master or backup nodes. That said when mixing disc based (uses the +disc to store meta information) nodes and ram based (do not use disc +at all) nodes the disc based ones sometimes have precedence over ram +based nodes. + +2 Meta Data + +Mnesia has two types of global meta data, static and dynamic. +All the meta data is stored in the ets table mnesia_gvar. + +2.1 Static Meta Data +The static data is the schema information, usually kept in +'schema.DAT' file, the data is created with +mnesia:create_schema(Nodes) for disc nodes (i.e. nodes which uses the +disc). Ram based mnesia nodes create an empty schema at startup. + +The static data i.e. schema, contains information about which nodes +are involved in the cluster and which type (ram or disc) they have. It +also contains information about which tables exist on which node and +so on. + +The schema information (static data) must always be the same on all +active nodes in the mnesia cluster. Schema information is updated via +schema functions, e.g. mnesia:add_table_copy/3, +mnesia:change_table_copy/3... + +2.2 Dynamic Meta Data + +The dynamic data is transient and is local to each mnesia node +in the cluster. Examples of dynamic data is: currently active mnesia +nodes, which tables are currently available and where are they +located. Dynamic data is updated internally by each mnesia during the +nodes lifetime, i.e. when nodes goes up and down or are added to or +deleted from the mnesia cluster. + +3 Processes and Files + +The most important processes in mnesia are mnesia_monitor, +mnesia_controller, mnesia_tm and mnesia_locker. + +Mnesia_monitor acts as supervisor and monitors all resources. It +listens for nodeup and nodedown and keeps links to other mnesia nodes, +if a node goes down it forwards the information to all the necessary +processes, e.g. mnesia_controller, mnesia_locker, mnesia_tm and all +transactions. During start it negotiates the protocol version with +the other nodes and keep track of which nodes uses which version. The +monitor process also detects and warns about partioned networks, it is +then up to the user to deal with them. It is the owner of all open +files, ets tables and so on. + +The mnesia_controller process is responsible for loading tables, +keeping the dynamic meta data updated, synchronize dangerous work such +as schema transactions vs dump log operation vs table loading/sending. + +The last two processes are involved in all transactions, the +mnesia_locker process manages transaction locks, and mnesia_tm manages +all transaction work. + +4 Startup and table Loading + +The early startup is mostly driven by the mnesia_tm process/module, +logs are dumped (see log dumping), node-names of other nodes in the +cluster are retrieved from the static meta data or from environment +parameters and initial connections are made to the other mnesia +nodes. + +The rest of start up is driven by the mnesia_controller process where +the schema (static meta data) is merged between each node, this is +done to keep the schema consistent between all nodes in the +cluster. When the schema is merged all local tables are put in a +loading queue, tables which are only available or have local content +is loaded directly from disc or created if they are type ram_copies. + +The other tables are kept in the queue until mnesia decides whether to +load them from disk or from another node. If another mnesia node has +already loaded the table, i.e. got a copy in ram or an open dets file, +the table is always loaded from that node to keep the data consistent. +If no other node has a loaded copy of the table, some mnesia node has +to load it first, and the other nodes can copy the table from the +first node. Mnesia keeps information about when other nodes went down, +a starting mnesia will check which nodes have been down, if some of +the nodes have not been down the starting node will let those nodes +load the table first. If all other nodes have been down then the +starting mnesia will load the table. The node that is allowed to load +the table will load it and the other nodes will copy it from that node. + +If a node, which the starter node has not a 'mnesia_down' note from, +is down the starter node will have to wait until that node comes up +and decision can be taken, this behavior can be overruled by user +settings. The order of table loading could be described as: + +1. Mnesia downs, Normally decides from where mnesia should load tables. +2. Master nodes (overrides mnesia downs). +3. Force load (overrides Master nodes). + 1) If possible, load table from active master nodes + 2) if no master nodes is active load from any active nodes, + 3) if no active node has an active table get local copy + (if ram create empty one) + +Currently mnesia can handle one download and one upload at the same +time. Dumping and loading/sending may run simultaneously but neither +of them may run during schema commit. Loaders/senders may not start if +a schema commit is enqueued. That synchronization is made to prohibit +that the schema transaction modifies the meta data and the +prerequisites of the table loading changes. + +The actual loading of a table is implemented in 'mnesia_loader.erl'. +It currently works as follows: + +Receiver Sender +-------- ------ +Spawned +Find sender node +Queue sender request ----> + Spawned +*)Spawn real receiver <---- Send init info +*)Grab schema lock for Grab write table lock + that table to avoid Subscribe receiver + deadlock with schema transactions to table updates +Create table (ets or dets) Release Lock + +Get data (with ack ----> + as flow control) <---- Burst data to receiver + Send no_more +Apply subscription messages +Store copy on disc Grab read lock +Create index, snmp data Update meta data info +and checkpoints if needed cleanup +no_more ----> + Release lock + +*) Don't spawn or grab schema lock if operation is add_table_copy, + it's already a schema operation. + + +5 Transaction + +Transaction are normally driven from the client process, i.e. the +process that call 'mnesia:transaction'. The client first acquires a +globally unique transaction id (tid) and temporary transaction storage +(ts an ets table) from mnesia_tm and then executes the transaction +fun. Mnesia-api calls such as 'mnesia:write/1' and 'mnesia:read' +contains code for acquiring the needed locks. Intermediate database +states and acquired locks are kept in the transaction storage, and all +mnesia operations has to be "patched" against that store. I.e. a write +operation in a transaction should be seen within (and only within) +that transaction, if the same key is read after the write. +After the transaction fun is completed the ts is analyzed to see which +nodes are involved in the transaction, and what type of commit protocol +shall be used. Then the result is committed and additional work such as +snmp, checkpoints and index updates are performed. The transaction is +finish by releasing all resources. + +An example: + +Example = fun(X) -> + {table1, key, Value} = mnesia:read(table1, key), + ok = mnesia:write(table1, {table1, key, Value+X}), + {table1, key, Updated} = mnesia:read(table1, key), + Updated + end, +mnesia:transaction(Example, [10]). + +A message overview of a simple successful asynchronous transaction + non local +Client Process mnesia_tm(local) mnesia_locker mnesia_tm +------------------------------------------------------------------------ +Get tid ----> + <--- Tid and ts +Get read lock +from available node -------------------------------> +Value <----------Value or restart trans--- +Patch value against ts + +Get write lock +from all nodes -------------------------------> + -------------------------------> +ok's <<---------ok's or restart trans--- +write data in ts + +Get read lock,already done. +Read data Value +'Patch' data with ts +Fun return Value+X. + +If everything is ok +commit transaction + +Find the nodes that the transaction +needs to be committed on and +collect every update from ts. + +Ask for commit -----------> + -----------------------------------------------> + +Ok's <<--------- ------------------------------ +Commit -----------------------------------------------> +log commit decision on disk +Commit locally + update snmp + update checkpoints + notify subscribers + update index + +Release locks -------------------------------> +Release transaction -----> + +Return trans result +---------------------- + +If all needed resources are available, i.e. the needed tables are +loaded somewhere in the cluster during the transaction, and the user +code doesn't crash, a transaction in mnesia won't fail. If something +happens in the mnesia cluster such as node down from the replica the +transaction was about to read from, or that a lock couldn't be +acquired and the transaction was not allowed to be queued on that +lock, the transaction is restarted, i.e. all resources are released +and the fun is called again. By default a transaction can be +restarted is infinity many times, but the user may choose to limit +the number of restarts. + +The dirty operations don't do any of the above they just finds out +where to write the data, logs the operation to disk and casts (or call +in case of sync_dirty operation) the data to those nodes. Therefore +the dirty operations have the drawback that each write or delete sends +a message per operation to the involved nodes. + +There is also a synchronous variant of 2-phase commit protocol which +waits on an additional ack message after the transaction is committed +on every node. The intention is to provide the user with a way to +solve overloading problems. + +A 3-phase commit protocol is used for schema transaction or if the +transaction result is going to be committed in a asymmetrical way, +i.e. a transaction that writes to table a and b where table a and b +have replicas on different nodes. The outcome of the transactions are +stored temporary in an ets table and in the log file. + +6 Schema transactions + +Schema transactions are handled differently than ordinary +transactions, they are implemented in mnesia_schema (and in +mnesia_dumper). The schema operation is always spawned to protect from +that the client process dies during the transaction. + +The actual transaction fun checks the pre-conditions and acquires the +needed locks and notes the operation in the transaction store. During +the commit, the schema transaction runs a schema prepare operation (on +every node) that does the needed prerequisite job. Then the operation +is logged to disc, and the actual commit work is done by dumping the +log. Every schema operation has special clause in mnesia_dumper to +handle the finishing work. Every schema prepare operation has a +matching undo_prepare operation which needs to be invoked if the +transaction is aborted. + +7 Locks + +"The locking algorithm is a traditional 'two-phase locking'* and the +deadlock prevention is 'wait-die'*, time stamps for the wait-die algorithm +is 'Lamport clock'* maintained by mnesia_tm. The Lamport clock is kept +when the transaction is restarted to avoid starving." + +* References can be found in the paper mnesia_overview.pdf + Klacke, H�kan and Hans wrote about mnesia. + +What the quote above means is that read locks are acquired on the +replica that mnesia read from, write locks are acquired on all nodes +which have a replica. Several read lock can lock the same object, but +write locks are exclusive. The transaction identifier (tid) is a ever +increasing system uniq counter which have the same sort order on every +node (a Lamport clock), which enables mnesia_locker to order the lock +requests. When a lock request arrives, mnesia_locker checks whether +the lock is available, if it is a 'granted' is sent back to the client +and the lock is noted as taken in an ets table. If the lock is already +occupied, it's tid is compared with tid of the transaction holding the +lock. If the tid of holding transaction is greater than the tid of +asking transaction it's allowed to be put in the lock queue (another +ets table) and no response is sent back until the lock is released, if +not the transaction will get a negative response and mnesia_tm will +restart the transaction after it has slept for a random time. + +Sticky locks works almost as a write lock, the first time a sticky +lock is acquired a request is sent to all nodes. The lock is marked as +taken by the requesting node (not transaction), when the lock is later +released it's only released on the node that has the sticky lock, +thus the next time a transaction is requesting the lock it don't need +to ask the others nodes. If another node wants the lock it has to request +a lock release first, before it can acquire the lock. + +8 Fragmented tables + +Fragmented tables are used to split a large table in smaller parts. +It is implemented as a layer between the client and mnesia which +extends the meta data with additional properties and maps a {table, +key} tuple to a table_fragment. + +The default mapping is erlang:phash() but the user may provide his own +mapping function to be able to predict which records is stored in +which table fragment, e.g. the client may want to steer where a +record generated from a certain device is placed. + +The foreign key is used to co-locate other tables to the same node. +The other additinal table attributes are also used to distribute the +table fragments. + +9 Log Dumping + +All operations on disk tables are stored on a log 'LATEST.LOG' on +disk, so mnesia can redo the transactions if the node goes down. +Dumping the log means that mnesia moves the committed data from the +general log to the table specific disk storage. To avoid that the log +grows to large and uses a lot of disk space and makes the startup slow, +mnesia dumps the log during it's uptime. There are two triggers that +start the log dumping, timeouts and the number of commits since last +dump, both are user configurable. + +Disc copies tables are implemented with two disk_log files, one +'table.DCD' (disc copies data) and one 'table.DCL' (disc copies log). +The dcd contains raw records, and the dcl contains operations on that +table, i.e. '{write, {table, key, value}}' or '{delete, {table, +key}}'. First time a record for a specific table is found when +dumping the table, the size of both the dcd and the dcl files are +checked. And if the sizeof(dcl)/sizeof(dcd) is greater than a +threshold, the current ram table is dumped to file 'table.DCD' and the +corresponding dcl file is deleted, and all other records in the +general log that belongs to that table are ignored. If the threshold +is not meet than the operations in the general log to that table are +appended to the dcl file. On start up both files are read, first the +contents of the dcd are loaded to an ets table, then it's modified by +the operations stored in the corresponding dcl file. + +Disc only copies tables updates the 'dets' file directly when +committing the data so those entries can be ignored during normal log +dumping, they are only added to the 'dets' file during startup when +mnesia don't know the state of the disk table. + +10 Checkpoints and backups + +Checkpoints are created to be able to take snapshots of the database, +which is pretty good when you want consistent backups, i.e. you don't +want half of a transaction in the backup. The checkpoint creates a +shadow table (called retainer) for each table involved in the +checkpoint. When a checkpoint is requested it will not start until all +ongoing transactions are completed. The new transactions will update +both the real table and update the shadow table with operations to +undo the changes on the real table, when a key is modified the first +time. I.e. when write operation '{table, a, 14}' is made, the shadow +table is checked if key 'a' has a undo operation, if it has, nothing +more is done. If not a {write, {table, a, OLD_VALUE}} is added to the +shadow table if the real table had an old value, if not a {delete, +{table, a}} operation is added to the shadow table. + +The backup is taken by copying every record in the real table and then +appending every operation in the shadow table to the backup, thus +undoing the changes that where made since the checkpoint where +started. + + diff --git a/lib/mnesia/doc/pdf/.gitignore b/lib/mnesia/doc/pdf/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/mnesia/doc/pdf/.gitignore diff --git a/lib/mnesia/doc/src/DATA b/lib/mnesia/doc/src/DATA new file mode 100644 index 0000000000..5b4bedacaf --- /dev/null +++ b/lib/mnesia/doc/src/DATA @@ -0,0 +1,100 @@ +%S0 +{tables, + [{employee, [{attributes, [emp_no,name,salary,sex, phone,room_no]}]}, + {dept, [{attributes, [id, name]}]}, + {project, [{attributes, [name, number]}]}, + {manager, [{attributes, [emp, dept]}, + {type, bag}]}, + {at_dep, [{attributes, [emp, dept_id]}]}, + {in_proj, [{attributes, [emp, proj_name]}, + {type, bag}]} + ] +}. + +%E0 + + +%S1 +{employee, 104465, "Johnson Torbjorn", 1, male, 99184, {242,038}}. +{employee, 107912, "Carlsson Tuula", 2, female, 94556, {242,056}}. +{employee, 114872, "Dacker Bjarne", 3, male, 99415, {221,035}}. +{employee, 114849, "Armstrong Josef", 3, male, 99452, {221,023}}. +{employee, 114952, "Froberg Magnus", 5, male, 99469, {222,018}}. +{employee, 104531, "Nilsson Hans", 3, male, 99495, {222,026}}. +{employee, 104659, "Tornkvist Torbjorn", 2, male, 99514, {222,022}}. +{employee, 104732, "Wikstrom Claes", 2, male, 99586, {221,015}}. +{employee, 117716, "Fedoriw Anna", 1, female, 99143, {221,031}}. +{employee, 115020, "Hansson Catrin", 6, female, 99129, {222,072}}. +{employee, 115018, "Mattsson Hakan", 3, male, 99251, {203,348}}. +{employee, 113069, "Eriksson Morgan", 6, male, 99186, {241,543}}. +%E1 + +%S2 +%% departments +{dept, 'B/SF', "Open Telecom Platform"}. +{dept, 'B/SFP', "OTP - Product Development"}. +{dept, 'B/SFR', "Computer Science Laboratory"}. +%E2 + + +%% projects +%S3 +{project, erlang, 1}. +{project, otp, 2}. +{project, beam, 3}. +{project, mnesia, 5}. +{project, wolf, 6}. +{project, documentation, 7}. +{project, www, 8}. + +%E3 + + + +%% manager +%S4 +{manager, 104465, 'B/SF'}. +{manager, 104465, 'B/SFP'}. +{manager, 114872, 'B/SFR'}. + +%E4 +%S5 +{at_dep, 104465, 'B/SF'}. +{at_dep, 107912, 'B/SF'}. +{at_dep, 114872, 'B/SFR'}. +{at_dep, 114849, 'B/SFR'}. +{at_dep, 114952, 'B/SFR'}. +{at_dep, 104531, 'B/SFR'}. +{at_dep, 104659, 'B/SFR'}. +{at_dep, 104732, 'B/SFR'}. +{at_dep, 117716, 'B/SFP'}. +{at_dep, 115020, 'B/SFP'}. +{at_dep, 115018, 'B/SFP'}. +{at_dep, 113069, 'B/SFP'}. + + +%E5 +%S6 +{in_proj, 104465, otp}. +{in_proj, 107912, otp}. +{in_proj, 114872, otp}. +{in_proj, 114849, otp}. +{in_proj, 114849, erlang}. +{in_proj, 114952, otp}. +{in_proj, 104531, otp}. +{in_proj, 104531, mnesia}. +{in_proj, 104545, wolf}. +{in_proj, 104659, otp}. +{in_proj, 104659, wolf}. +{in_proj, 104732, otp}. +{in_proj, 104732, mnesia}. +{in_proj, 104732, erlang}. +{in_proj, 117716, otp}. +{in_proj, 117716, documentation}. +{in_proj, 115020, otp}. +{in_proj, 115018, otp}. +{in_proj, 115018, mnesia}. +{in_proj, 113069, otp}. + +%E6 + diff --git a/lib/mnesia/doc/src/DATA2 b/lib/mnesia/doc/src/DATA2 new file mode 100644 index 0000000000..e547e84d99 --- /dev/null +++ b/lib/mnesia/doc/src/DATA2 @@ -0,0 +1,17 @@ + + +%S0 +{tables + [{foo, [{attributes, [x,y,z]}]}]}. +%E0 + + +%S1 +{foo, a, benny, 18}. +{foo, b, elvis, 19}. +{foo, c, benny, 20}. +{foo, d, elvis, 21}. +{foo, e, klacke, 22}. +{foo, f, hans, 23}. +%E1 + diff --git a/lib/mnesia/doc/src/FRUITS b/lib/mnesia/doc/src/FRUITS new file mode 100644 index 0000000000..43d64f9d8c --- /dev/null +++ b/lib/mnesia/doc/src/FRUITS @@ -0,0 +1,12 @@ +%0 +{tables, + [{fruit, [{attributes, [name, color, taste]}]}, + {vegetable, [{attributes, [name, color, taste, price]}]}]}. + + +{fruit, orange, orange, sweet}. +{fruit, apple, green, sweet}. +{vegetable, carrot, orange, carrotish, 2.55}. +{vegetable, potato, yellow, none, 0.45}. +%0 + diff --git a/lib/mnesia/doc/src/Makefile b/lib/mnesia/doc/src/Makefile new file mode 100644 index 0000000000..f45b5137a3 --- /dev/null +++ b/lib/mnesia/doc/src/Makefile @@ -0,0 +1,240 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 1997-2009. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../../vsn.mk +VSN=$(MNESIA_VSN) +APPLICATION=mnesia + +# ---------------------------------------------------- +# Include dependency +# ---------------------------------------------------- + +ifndef DOCSUPPORT +include make.dep +endif + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- +XML_APPLICATION_FILES = ref_man.xml +XML_REF3_FILES = \ + mnesia.xml \ + mnesia_frag_hash.xml \ + mnesia_registry.xml + +XML_PART_FILES = \ + part.xml \ + part_notes.xml \ + part_notes_history.xml + +XML_CHAPTER_FILES = \ + Mnesia_chap1.xml \ + Mnesia_chap2.xml \ + Mnesia_chap3.xml \ + Mnesia_chap4.xml \ + Mnesia_chap5.xml \ + Mnesia_chap7.xml \ + Mnesia_chap8.xml \ + Mnesia_App_A.xml \ + Mnesia_App_B.xml \ + Mnesia_App_C.xml \ + Mnesia_App_D.xml \ + notes.xml + +BOOK_FILES = book.xml + + +XML_FILES = \ + $(BOOK_FILES) $(XML_CHAPTER_FILES) \ + $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES) + +GIF_FILES = \ + book.gif \ + company.gif \ + mnesia.gif \ + note.gif \ + notes.gif \ + ref_man.gif \ + user_guide.gif \ + warning.gif + +XML_HTML_FILES = \ + notes_history.xml + + +# ---------------------------------------------------- + +HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \ + $(XML_HTML_FILES:%.xml=$(HTMLDIR)/%.html) \ + $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html) + +INFO_FILE = ../../info +EXTRA_FILES = summary.html.src \ + $(DEFAULT_GIF_FILES) \ + $(DEFAULT_HTML_FILES) \ + $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \ + $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html) + +MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3) + +ifdef DOCSUPPORT + +HTML_REF_MAN_FILE = $(HTMLDIR)/index.html + +TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf + +else +TEX_FILES_BOOK = \ + $(BOOK_FILES:%.xml=%.tex) +TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \ + $(XML_APPLICATION_FILES:%.xml=%.tex) +TEX_FILES_USERS_GUIDE = \ + $(XML_CHAPTER_FILES:%.xml=%.tex) + +TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf +TOP_PS_FILE = $(APPLICATION)-$(VSN).ps + +$(TOP_PDF_FILE): book.dvi ../../vsn.mk + $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@ + +$(TOP_PS_FILE): book.dvi ../../vsn.mk + $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@ + +endif + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +XML_FLAGS += +DVIPS_FLAGS += + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- +$(HTMLDIR)/%.gif: %.gif + $(INSTALL_DATA) $< $@ + +ifdef DOCSUPPORT + +docs: pdf html man + +$(TOP_PDF_FILE): $(XML_FILES) + +pdf: $(TOP_PDF_FILE) + +html: gifs $(HTML_REF_MAN_FILE) + +clean clean_docs: + rm -rf $(HTMLDIR)/* + rm -f $(MAN3DIR)/* + rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f errs core *~ + +else + +ifeq ($(DOCTYPE),pdf) +docs: pdf +else +ifeq ($(DOCTYPE),ps) +docs: ps +else +docs: html gifs man +endif +endif + +pdf: $(TOP_PDF_FILE) + +ps: $(TOP_PS_FILE) + +html: $(HTML_FILES) + + +clean clean_docs clean_tex: + rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK) + rm -f $(HTML_FILES) $(MAN3_FILES) + rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE) + rm -f errs core *~ *xmls_output *xmls_errs $(LATEX_CLEAN) + +endif + +man: $(MAN3_FILES) + +gifs: $(GIF_FILES:%=$(HTMLDIR)/%) + +$(INDEX_TARGET): $(INDEX_SRC) ../../vsn.mk + sed -e 's;%VSN%;$(VSN);' $< > $@ + +debug opt: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +ifdef DOCSUPPORT + +release_docs_spec: docs + $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf + $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf + $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DATA) $(HTMLDIR)/* \ + $(RELSYSDIR)/doc/html + $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) + $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 + $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 + +else + +ifeq ($(DOCTYPE),pdf) +release_docs_spec: pdf + $(INSTALL_DIR) $(RELEASE_PATH)/pdf + $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf +else +ifeq ($(DOCTYPE),ps) +release_docs_spec: ps + $(INSTALL_DIR) $(RELEASE_PATH)/ps + $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps +else +release_docs_spec: docs + $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \ + $(RELSYSDIR)/doc/html + $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) + $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 + $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 +endif +endif + +endif + + +release_spec: + diff --git a/lib/mnesia/doc/src/Mnesia_App_A.xml b/lib/mnesia/doc/src/Mnesia_App_A.xml new file mode 100644 index 0000000000..86e5b7d03c --- /dev/null +++ b/lib/mnesia/doc/src/Mnesia_App_A.xml @@ -0,0 +1,87 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Appendix A: Mnesia Error Messages</title> + <prepared>Claes Wikström, Hans Nilsson and Håkan Mattsson</prepared> + <responsible>Bjarne Däcker</responsible> + <docno></docno> + <approved>Bjarne Däcker</approved> + <checked>Bjarne Däcker</checked> + <date>96-11-20</date> + <rev>B</rev> + <file>Mnesia_App_A.xml</file> + </header> + <p>Whenever an operation returns an error in Mnesia, a description + of the error is available. For example, the functions + <c>mnesia:transaction(Fun)</c>, or <c>mnesia:create_table(N,L)</c> + may return the tuple <c>{aborted, Reason}</c>, where <c>Reason</c> + is a term describing the error. The following function is used to + retrieve more detailed information about the error: + </p> + <list type="bulleted"> + <item><c>mnesia:error_description(Error)</c></item> + </list> + + <section> + <title>Errors in Mnesia</title> + <p>The following is a list of valid errors in Mnesia.</p> + <list type="bulleted"> + <item><c>badarg</c>. Bad or invalid argument, possibly bad type. + </item> + <item><c>no_transaction</c>. Operation not allowed outside transactions. + </item> + <item><c>combine_error</c>. Table options were illegally combined. + </item> + <item><c>bad_index</c>. Index already exists, or was out of bounds. + </item> + <item><c>already_exists</c>. Schema option to be activated is already on. + </item> + <item><c>index_exists</c>. Some operations cannot be performed on tables with an index. + </item> + <item><c>no_exists</c>.; Tried to perform operation on non-existing (non-alive) item. + </item> + <item><c>system_limit</c>.; A system limit was exhausted. + </item> + <item><c>mnesia_down</c>. A transaction involves records on a + remote node which became unavailable before the transaction + was completed. Record(s) are no longer available elsewhere in + the network.</item> + <item><c>not_a_db_node</c>. A node was mentioned which does not exist in the schema.</item> + <item><c>bad_type</c>.; Bad type specified in argument.</item> + <item><c>node_not_running</c>. Node is not running.</item> + <item><c>truncated_binary_file</c>. Truncated binary in file.</item> + <item><c>active</c>. Some delete operations require that all active records are removed.</item> + <item><c>illegal</c>. Operation not supported on this record.</item> + </list> + <p>The following example illustrates a function which returns an error, and the method to retrieve more detailed error information. + </p> + <p>The function <c>mnesia:create_table(bar, [{attributes, 3.14}])</c> will return the tuple <c>{aborted,Reason}</c>, where <c>Reason</c> is the tuple + <c>{bad_type,bar,3.14000}</c>. + </p> + <p>The function <c>mnesia:error_description(Reason)</c>, returns the term + <c>{"Bad type on some provided arguments",bar,3.14000}</c> which is an error + description suitable + for display.</p> + </section> +</chapter> + diff --git a/lib/mnesia/doc/src/Mnesia_App_B.xmlsrc b/lib/mnesia/doc/src/Mnesia_App_B.xmlsrc new file mode 100644 index 0000000000..52f5e06d83 --- /dev/null +++ b/lib/mnesia/doc/src/Mnesia_App_B.xmlsrc @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Appendix B: The Backup Call Back Interface</title> + <prepared>Claes Wikström, Hans Nilsson and Håkan Mattsson</prepared> + <responsible>Bjarne Däcker</responsible> + <docno></docno> + <approved>Bjarne Däcker</approved> + <checked>Bjarne Däcker</checked> + <date>97-05-27</date> + <rev>C</rev> + <file>Mnesia_App_B.xml</file> + </header> + + <section> + <title>mnesia_backup callback behavior</title> + <p></p> + <codeinclude file="../../src/mnesia_backup.erl" tag="%0" type="erl"></codeinclude> + </section> +</chapter> + diff --git a/lib/mnesia/doc/src/Mnesia_App_C.xmlsrc b/lib/mnesia/doc/src/Mnesia_App_C.xmlsrc new file mode 100644 index 0000000000..d8916f25cb --- /dev/null +++ b/lib/mnesia/doc/src/Mnesia_App_C.xmlsrc @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1998</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Appendix C: The Activity Access Call Back Interface</title> + <prepared>Håkan Mattsson</prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date></date> + <rev></rev> + <file>Mnesia_App_C.xml</file> + </header> + + <section> + <title>mnesia_access callback behavior</title> + <p></p> + <codeinclude file="../../src/mnesia_frag.erl" tag="%header_doc_include" type="erl"></codeinclude> + <p></p> + <codeinclude file="../../src/mnesia_frag.erl" tag="%impl_doc_include" type="erl"></codeinclude> + </section> +</chapter> + diff --git a/lib/mnesia/doc/src/Mnesia_App_D.xmlsrc b/lib/mnesia/doc/src/Mnesia_App_D.xmlsrc new file mode 100644 index 0000000000..d98680640d --- /dev/null +++ b/lib/mnesia/doc/src/Mnesia_App_D.xmlsrc @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2002</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Appendix D: The Fragmented Table Hashing Call Back Interface</title> + <prepared>Håkan Mattsson</prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date></date> + <rev></rev> + <file>Mnesia_App_D.xml</file> + </header> + + <section> + <title>mnesia_frag_hash callback behavior</title> + <p></p> + <codeinclude file="../../src/mnesia_frag_hash.erl" tag="%header_doc_include" type="erl"></codeinclude> + <p></p> + <codeinclude file="../../src/mnesia_frag_hash.erl" tag="%impl_doc_include" type="erl"></codeinclude> + </section> +</chapter> + diff --git a/lib/mnesia/doc/src/Mnesia_chap1.xml b/lib/mnesia/doc/src/Mnesia_chap1.xml new file mode 100644 index 0000000000..9af81c85cb --- /dev/null +++ b/lib/mnesia/doc/src/Mnesia_chap1.xml @@ -0,0 +1,265 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Introduction</title> + <prepared>Claes Wikström, Hans Nilsson and Håkan Mattsson</prepared> + <responsible>Bjarne Däcker</responsible> + <docno></docno> + <approved>Bjarne Däcker</approved> + <checked>Bjarne Däcker</checked> + <date></date> + <rev>C</rev> + <file>Mnesia_chap1.xml</file> + </header> + <p>This book describes the Mnesia DataBase Management + System (DBMS). <em>Mnesia</em> is a distributed Database Management + System, appropriate for telecommunications applications and other + Erlang applications which require continuous operation and soft + real-time properties. It is one section of the Open Telecom Platform + (OTP), which is a control system platform for building + telecommunications applications.</p> + + <section> + <title>About Mnesia</title> + <p>The management of data in telecommunications system has many + aspects whereof some, but not all, are addressed by traditional + commercial DBMSs (Data Base Management Systems). In particular the + very high level of fault tolerance which 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 brand new + DBMS. called Mnesia. Mnesia is implemented in, and very tightly + connected to, the programming language Erlang and it provides the + functionality that is necessary for the implementation of fault + tolerant telecommunications systems. Mnesia is a multiuser Distributed + DBMS specially made for industrial telecommunications applications + written in the symbolic programming language Erlang, which is also + the intended target language. Mnesia tries to address all of the data + management issues required for typical telecommunications systems and + it has a number of features that are not normally found in traditional + databases. <br></br> + + In telecommunications applications there are different needs + from the features provided by traditional DBMSs. The applications now + implemented in the Erlang language need a mixture of a broad range + of features, which generally are not satisfied by traditional DBMSs. + Mnesia is designed with requirements like the following in mind:</p> + <list type="ordered"> + <item>Fast real-time key/value lookup</item> + <item>Complicated non real-time queries mainly for + operation and maintenance</item> + <item>Distributed data due to distributed + applications</item> + <item>High fault tolerance</item> + <item>Dynamic re-configuration</item> + <item>Complex objects</item> + </list> + <p>What + sets Mnesia apart from most other DBMSs is that it is designed with + the typical data management problems of telecommunications applications + in mind. Hence Mnesia combines many concepts found in traditional + databases, such as transactions and queries with concepts found in data + management systems for telecommunications applications, such as very + fast real-time operations, configurable degree of fault tolerance (by + means of replication) and the ability to reconfigure the system without + stopping or suspending it. Mnesia is also interesting due to its tight + coupling to the programming language Erlang, thus almost turning Erlang + into a database programming language. This has many benefits, the + foremost is that + the impedance mismatch between data format used by the + DBMS and data format used by the programming language, which is used + to manipulate the data, completely disappears. <br></br> +</p> + </section> + + <section> + <title>The Mnesia DataBase Management System (DBMS)</title> + <p></p> + + <section> + <title>Features</title> + <p>Mnesia contains the following features which combine to produce a fault-tolerant, + distributed database management system written in Erlang: + </p> + <list type="bulleted"> + <item>Database schema can be dynamically reconfigured at runtime. + </item> + <item>Tables can be declared to have properties such as location, + replication, and persistence. + </item> + <item>Tables can be moved or replicated to several nodes to improve + fault tolerance. The rest of the system can still access the tables + to read, write, and delete records. + </item> + <item>Table locations are transparent to the programmer. + Programs address table names and the system itself keeps track of + table locations. + </item> + <item>Database transactions can be distributed, and a large number of + functions can be called within one transaction. + </item> + <item>Several transactions can run concurrently, and their execution is + fully synchronized by the database management system. + Mnesia ensures that no two processes manipulate data simultaneously. + </item> + <item>Transactions can be assigned the property of being executed on + all nodes in the system, or on none. Transactions can also be bypassed + in favor of running so called "dirty operations", which reduce + overheads and run very fast. + </item> + </list> + <p>Details of these features are described in the following sections.</p> + </section> + <p></p> + + <section> + <title>Add-on Applications</title> + <p>QLC and Mnesia Session can be used in conjunction with Mnesia to produce + specialized functions which enhance the operational ability of Mnesia. + Both Mnesia Session and QLC have their own documentation as part + of the OTP documentation set. Below are the main features of Mnesia Session + and QLC when used in conjunction with Mnesia:</p> + <list type="bulleted"> + <item><em>QLC</em> has the ability to optimize the query + compiler for the Mnesia Database Management System, essentially making + the DBMS more efficient.</item> + <item><em>QLC</em>, can be used as a database programming + 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> + <item><em>Mnesia Session</em> is an interface for the Mnesia Database + Management System</item> + <item><em>Mnesia Session</em> enables access to the + Mnesia DBMS from foreign programming languages (i.e. other + languages than Erlang).</item> + </list> + <p></p> + + <section> + <title>When to Use Mnesia</title> + <p>Use Mnesia with the following types of applications: + </p> + <list type="bulleted"> + <item>Applications that need to replicate data. + </item> + <item>Applications that perform complicated searches on data. + </item> + <item>Applications that need to use atomic transactions to + update several records simultaneously. + </item> + <item>Applications that use soft real-time characteristics. + </item> + </list> + <p>On the other hand, Mnesia may not be appropriate with the + following types of applications: + </p> + <list type="bulleted"> + <item>Programs that process plain text or binary data files + </item> + <item>Applications that merely need a look-up dictionary + which can be stored to disc can utilize the standard + library module <c>dets</c>, which is a disc based version + of the module <c>ets</c>. + </item> + <item>Applications which need disc logging facilities can + utilize the module <c>disc_log</c> by preference. + </item> + <item>Not suitable for hard real time systems. + </item> + </list> + </section> + </section> + + <section> + <title>Scope and Purpose</title> + <p>This manual is included in the OTP document set. It describes + how to build Mnesia database applications, and how to integrate + and utilize the Mnesia database management system with + OTP. Programming constructs are described, and numerous + programming examples are included to illustrate the use of + Mnesia. + </p> + </section> + + <section> + <title>Prerequisites</title> + <p>Readers of this manual are assumed to be familiar with system + development principles and database management systems. Readers + are also assumed to be familiar with the Erlang programming + language.</p> + </section> + + <section> + <title>About This Book</title> + <p>This book contains the following chapters: + </p> + <list type="bulleted"> + <item>Chapter 2, "Getting Started with Mnesia", introduces + Mnesia with an example database. Examples are shown of how to + start an Erlang session, specify a Mnesia database directory, + initialize a database schema, start Mnesia, and create + tables. Initial prototyping of record definitions is also + discussed. + </item> + <item>Chapter 3, "Building a Mnesia Database", more formally + describes the steps introduced in Chapter 2, namely the Mnesia + functions which define a database schema, start Mnesia, and + create the required tables. + </item> + <item>Chapter 4, "Transactions and other access contexts", + describes the transactions properties which make Mnesia into a + fault tolerant, real-time distributed database management + system. This chapter also describes the concept of locking in + order to ensure consistency in tables, and so called "dirty + operations", or short cuts which bypass the transaction system + to improve speed and reduce overheads. + </item> + <item>Chapter 5, "Miscellaneous Mnesia Features", describes + features which enable the construction of more complex + database applications. These features includes indexing, + checkpoints, distribution and fault tolerance, disc-less + nodes, replication manipulation, local content tables, concurrency, + and object based programming in Mnesia. + </item> + <item>Chapter 6, "Mnesia System Information", describes the + files contained in the 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>Chapter 7, "Combining Mnesia with SNMP", is a short + chapter which outlines Mnesia integrated with SNMP. + </item> + <item>Appendix A, "Mnesia Errors Messages", lists Mnesia error + messages and their meanings. + </item> + <item>Appendix B, "The Backup Call Back Interface", is a + program listing of the default implementation of this facility. + </item> + <item>Appendix C, "The Activity Access Call Back Interface", + is a program outlining of one possible implementations of this facility. + </item> + </list> + </section> + </section> +</chapter> + diff --git a/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc new file mode 100644 index 0000000000..0714c7b645 --- /dev/null +++ b/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc @@ -0,0 +1,647 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Getting Started with Mnesia</title> + <prepared>Claes Wikström, Hans Nilsson and Håkan Mattsson</prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date></date> + <rev>C</rev> + <file>Mnesia_chap2.xml</file> + </header> + <p>This chapter introduces Mnesia. Following a brief discussion + about the first initial setup, a Mnesia database example is + demonstrated. This database example will be referenced in the + following chapters, where this example is modified in order to + illustrate various program constructs. In this chapter, the + following mandatory procedures are illustrated by examples: + </p> + <list type="bulleted"> + <item>Starting an Erlang session and specifying a directory for the + Mnesia database. + </item> + <item>Initializing a database schema. + </item> + <item>Starting Mnesia and creating the required tables.</item> + </list> + + <section> + <title>Starting Mnesia for the first time</title> + <p>Following is a simplified demonstration of a Mnesia system startup. This is the dialogue from the Erlang + shell: + </p> + <pre><![CDATA[ + unix> erl -mnesia dir '"/tmp/funky"' + Erlang (BEAM) emulator version 4.9 + + Eshell V4.9 (abort with ^G) + 1> + 1> mnesia:create_schema([node()]). + ok + 2> mnesia:start(). + ok + 3> mnesia:create_table(funky, []). + {atomic,ok} + 4> mnesia:info(). + ---> Processes holding locks <--- + ---> Processes waiting for locks <--- + ---> Pending (remote) transactions <--- + ---> Active (local) transactions <--- + ---> Uncertain transactions <--- + ---> Active tables <--- + funky : with 0 records occupying 269 words of mem + schema : with 2 records occupying 353 words of mem + ===> System info in version "1.0", debug level = none <=== + opt_disc. Directory "/tmp/funky" is used. + use fall-back at restart = false + running db nodes = [nonode@nohost] + stopped db nodes = [] + remote = [] + ram_copies = [funky] + disc_copies = [schema] + disc_only_copies = [] + [{nonode@nohost,disc_copies}] = [schema] + [{nonode@nohost,ram_copies}] = [funky] + 1 transactions committed, 0 aborted, 0 restarted, 1 logged to disc + 0 held locks, 0 in queue; 0 local transactions, 0 remote + 0 transactions waits for other nodes: [] + ok + ]]></pre> + <p>In the example above the following actions were performed: + </p> + <list type="bulleted"> + <item>The Erlang system was started from the UNIX prompt + with a flag <c>-mnesia dir '"/tmp/funky"'</c>. This flag indicates + to Mnesia which directory will store the data. + </item> + <item>A new empty schema was initialized on the local node by evaluating + <c>mnesia:create_schema([node()]).</c> The schema contains + information about the database in general. This will be + thoroughly explained later on. + </item> + <item>The DBMS was started by evaluating <c>mnesia:start()</c>. + </item> + <item>A first table was created, called <c>funky</c> by evaluating + the expression <c>mnesia:create_table(funky, [])</c>. The table + was given default properties. + </item> + <item><c>mnesia:info()</c> was evaluated and subsequently displayed + information regarding the status of the database on the terminal. + </item> + </list> + </section> + + <section> + <title>An Introductory Example</title> + <p>A Mnesia database is organized as a set of tables. + Each table is populated with instances (Erlang records). + A table also has a number of properties, such as location and + persistence. + </p> + <p>In this example we shall: + </p> + <list type="bulleted"> + <item>Start an Erlang system, and specify the directory where + the database will be located. + </item> + <item>Initiate a new schema with an attribute that specifies + on which node, or nodes, the database will operate. + </item> + <item>Start Mnesia itself. + </item> + <item>Create and populate the database tables. + </item> + </list> + + <section> + <title>The Example Database</title> + </section> + <p>In this database example, we will create the database and + relationships depicted in the following diagram. We will call this + database the <em>Company</em> database. + </p> + <image file="company.gif"> + <icaption>Company Entity-Relation Diagram</icaption> + </image> + <p>The database model looks as follows: + </p> + <list type="bulleted"> + <item>There are three entities: employee, project, and + department. + </item> + <item> + <p>There are three relationships between these entities:</p> + <list type="ordered"> + <item>A department is managed by an employee, hence the + <em>manager</em> relationship. + </item> + <item>An employee works at a department, hence the + <em>at_dep</em> relationship. + </item> + <item>Each employee works on a number of projects, hence + the <em>in_proj</em> relationship. + </item> + </list> + </item> + </list> + + <section> + <title>Defining Structure and Content</title> + <p>We first enter our record definitions into a text file + named <c>company.hrl</c>. This file defines the following + structure for our sample database: + </p> + <codeinclude file="company.hrl" tag="%0" type="erl"></codeinclude> + <p>The structure defines six tables in our database. In Mnesia, + the function <c>mnesia:create_table(Name, ArgList)</c> is + used to create tables. <c>Name</c> is the table + name <em>Note:</em> The current version of Mnesia does + not require that the name of the table is the same as the record + name, See Chapter 4: + <seealso marker="Mnesia_chap4#recordnames_tablenames">Record Names Versus Table Names.</seealso></p> + <p>For example, the table + for employees will be created with the function + <c>mnesia:create_table(employee, [{attributes, record_info(fields, employee)}]).</c> The table + name <c>employee</c> matches the name for records specified + in <c>ArgList</c>. The expression <c>record_info(fields, RecordName)</c> is processed by the Erlang preprocessor and + evaluates to a list containing the names of the different + fields for a record. + </p> + </section> + + <section> + <title>The Program</title> + <p>The following shell interaction starts Mnesia and + initializes the schema for our <c>company</c> database: + </p> + <pre> + + % <input>erl -mnesia dir '"/ldisc/scratch/Mnesia.Company"'</input> + Erlang (BEAM) emulator version 4.9 + + Eshell V4.9 (abort with ^G) + 1> mnesia:create_schema([node()]). + ok + 2> mnesia:start(). + ok + </pre> + <p>The following program module creates and populates previously defined tables: + </p> + <codeinclude file="company.erl" tag="%0" type="erl"></codeinclude> + </section> + + <section> + <title>The Program Explained</title> + <p>The following commands and functions were used to initiate the + Company database: + </p> + <list type="bulleted"> + <item><c>% erl -mnesia dir '"/ldisc/scratch/Mnesia.Company"'.</c> This is a UNIX + command line entry which starts the Erlang system. The flag + <c>-mnesia dir Dir</c> specifies the location of the + database directory. The system responds and waits for + further input with the prompt <em>1></em>. + </item> + <item><c>mnesia:create_schema([node()]).</c> This function + has the format <c>mnesia:create_schema(DiscNodeList)</c> and + initiates a new schema. In this example, we have created a + non-distributed system using only one node. Schemas are fully + explained in Chapter 3:<seealso marker="Mnesia_chap3#def_schema">Defining a Schema</seealso>. + </item> + <item><c>mnesia:start().</c> This function starts + Mnesia. This function is fully explained in Chapter 3: + <seealso marker="Mnesia_chap3#start_mnesia">Starting Mnesia</seealso>. + </item> + </list> + <p>Continuing the dialogue with the Erlang shell will produce the following + the following: + </p> + <pre><![CDATA[ + 3> company:init(). + {atomic,ok} + 4> mnesia:info(). + ---> Processes holding locks <--- + ---> Processes waiting for locks <--- + ---> Pending (remote) transactions <--- + ---> Active (local) transactions <--- + ---> Uncertain transactions <--- + ---> Active tables <--- + in_proj : with 0 records occuping 269 words of mem + at_dep : with 0 records occuping 269 words of mem + manager : with 0 records occuping 269 words of mem + project : with 0 records occuping 269 words of mem + dept : with 0 records occuping 269 words of mem + employee : with 0 records occuping 269 words of mem + schema : with 7 records occuping 571 words of mem + ===> System info in version "1.0", debug level = none <=== + opt_disc. Directory "/ldisc/scratch/Mnesia.Company" is used. + use fall-back at restart = false + running db nodes = [nonode@nohost] + stopped db nodes = [] + remote = [] + ram_copies = + [at_dep,dept,employee,in_proj,manager,project] + disc_copies = [schema] + disc_only_copies = [] + [{nonode@nohost,disc_copies}] = [schema] + [{nonode@nohost,ram_copies}] = + [employee,dept,project,manager,at_dep,in_proj] + 6 transactions committed, 0 aborted, 0 restarted, 6 logged to disc + 0 held locks, 0 in queue; 0 local transactions, 0 remote + 0 transactions waits for other nodes: [] + ok + ]]></pre> + <p>A set of tables is created: + </p> + <list type="bulleted"> + <item><c>mnesia:create_table(Name,ArgList)</c>. This + function is used to create the required database tables. The + options available with <c>ArgList</c> are explained in + Chapter 3: <seealso marker="Mnesia_chap3#create_tables">Creating New Tables</seealso>. </item> + </list> + <p>The <c>company:init/0</c> function creates our tables. Two tables are + of type <c>bag</c>. This is the <c>manager</c> relation as well + the <c>in_proj</c> relation. This shall be interpreted as: An + employee can be manager over several departments, and an employee + can participate in several projects. However, the <c>at_dep</c> + relation is <c>set</c> because an employee can only work in one department. + In this data model we have examples of relations that are one-to-one (<c>set</c>), + as well as one-to-many (<c>bag</c>). + </p> + <p><c>mnesia:info()</c> now indicates that a database + which has seven local tables, of which, six are our + user defined tables and one is the schema. + Six transactions have been committed, as six successful transactions were run when + creating the tables. + </p> + <p>To write a function which inserts an employee record into the database, there must be an + <c>at_dep</c> record and a set of <c>in_proj</c> records inserted. Examine the following + code used to complete this action: + </p> + <codeinclude file="company.erl" tag="%1" type="erl"></codeinclude> + <list type="bulleted"> + <item> + <p><c>insert_emp(Emp, DeptId, ProjNames) -></c>. The + <c>insert_emp/3</c> arguments are:</p> + <list type="ordered"> + <item><c>Emp</c> is an employee record. + </item> + <item><c>DeptId</c> is the identity of the department where the employee is working. + </item> + <item><c>ProjNames</c> is a list of the names of the projects where the employee are working.</item> + </list> + </item> + </list> + <p>The <c>insert_emp(Emp, DeptId, ProjNames) -></c> function + creates a <em>functional object</em>. Functional objects + are identified by the term <c>Fun</c>. The Fun is passed + as a single argument to the function + <c>mnesia:transaction(Fun)</c>. This means that Fun is + run as a transaction with the following properties: + </p> + <list type="bulleted"> + <item>Fun either succeeds or fails completely. + </item> + <item>Code which manipulates the same data records can be + run concurrently without the different processes interfering + with each other. + </item> + </list> + <p>The function can be used as:</p> + <code type="none"> + Emp = #employee{emp_no= 104732, + name = klacke, + salary = 7, + sex = male, + phone = 98108, + room_no = {221, 015}}, + insert_emp(Me, 'B/SFR', [Erlang, mnesia, otp]). + </code> + <note> + <p>Functional Objects (Funs) are described in the + Erlang Reference Manual, "Fun Expressions". + </p> + </note> + </section> + + <section> + <title>Initial Database Content</title> + <p>After the insertion of the employee named <c>klacke</c> + we have the following records in the database: + </p> + <marker id="table2_1"></marker> + <table> + <row> + <cell align="left" valign="middle">emp_no</cell> + <cell align="left" valign="middle">name</cell> + <cell align="left" valign="middle">salary</cell> + <cell align="left" valign="middle">sex</cell> + <cell align="left" valign="middle">phone</cell> + <cell align="left" valign="middle">room_no</cell> + </row> + <row> + <cell align="left" valign="middle">104732</cell> + <cell align="left" valign="middle">klacke</cell> + <cell align="left" valign="middle">7</cell> + <cell align="left" valign="middle">male</cell> + <cell align="left" valign="middle">99586</cell> + <cell align="left" valign="middle">{221, 015}</cell> + </row> + <tcaption> +Employee</tcaption> + </table> + <p>An employee record has the following Erlang record/tuple + representation: <c>{employee, 104732, klacke, 7, male, 98108, {221, 015}}</c></p> + <marker id="table2_2"></marker> + <table> + <row> + <cell align="left" valign="middle">emp</cell> + <cell align="left" valign="middle">dept_name</cell> + </row> + <row> + <cell align="left" valign="middle">klacke</cell> + <cell align="left" valign="middle">B/SFR</cell> + </row> + <tcaption> +At_dep</tcaption> + </table> + <p>At_dep has the following Erlang tuple representation: + <c>{at_dep, klacke, 'B/SFR'}</c>. + </p> + <marker id="table3_3"></marker> + <table> + <row> + <cell align="left" valign="middle">emp</cell> + <cell align="left" valign="middle">proj_name</cell> + </row> + <row> + <cell align="left" valign="middle">klacke</cell> + <cell align="left" valign="middle">Erlang</cell> + </row> + <row> + <cell align="left" valign="middle">klacke</cell> + <cell align="left" valign="middle">otp</cell> + </row> + <row> + <cell align="left" valign="middle">klacke</cell> + <cell align="left" valign="middle">mnesia</cell> + </row> + <tcaption> +In_proj</tcaption> + </table> + <p>In_proj has the following Erlang tuple representation: + <c>{in_proj, klacke, 'Erlang', klacke, 'otp', klacke, 'mnesia'}</c></p> + <p>There is no difference between rows in a table and Mnesia + records. Both concepts are the same and will be used + interchangeably throughout this book. + </p> + <p>A Mnesia table is populated by Mnesia records. For example, + the tuple <c>{boss, klacke, bjarne}</c> is an record. The + second element in this tuple is the key. In order to uniquely + identify a table row both the key and the table name is + needed. The term <em>object identifier</em>, + (oid) is sometimes used for the arity two tuple {Tab, Key}. The oid for + the <c>{boss, klacke, bjarne}</c> record is the arity two + tuple <c>{boss, klacke}</c>. The first element of the tuple is + the type of the record and the second element is the key. An + oid can lead to zero, one, or more records depending on + whether the table type is <c>set</c> or <c>bag</c>. + </p> + <p>We were also able to insert the <c>{boss, klacke, bjarne}</c> record which contains an implicit reference to + another employee which does not yet exist in the + database. Mnesia does not enforce this. + </p> + </section> + + <section> + <title>Adding Records and Relationships to the Database</title> + <p>After adding additional record to the Company database, we + may end up with the following records: + </p> + <p><em>Employees</em></p> + <code type="none"> + {employee, 104465, "Johnson Torbjorn", 1, male, 99184, {242,038}}. + {employee, 107912, "Carlsson Tuula", 2, female,94556, {242,056}}. + {employee, 114872, "Dacker Bjarne", 3, male, 99415, {221,035}}. + {employee, 104531, "Nilsson Hans", 3, male, 99495, {222,026}}. + {employee, 104659, "Tornkvist Torbjorn", 2, male, 99514, {222,022}}. + {employee, 104732, "Wikstrom Claes", 2, male, 99586, {221,015}}. + {employee, 117716, "Fedoriw Anna", 1, female,99143, {221,031}}. + {employee, 115018, "Mattsson Hakan", 3, male, 99251, {203,348}}. + </code> + <p><em>Dept</em></p> + <code type="none"> + + {dept, 'B/SF', "Open Telecom Platform"}. + {dept, 'B/SFP', "OTP - Product Development"}. + {dept, 'B/SFR', "Computer Science Laboratory"}. + </code> + <p><em>Projects</em></p> + <code type="none"> + %% projects + {project, erlang, 1}. + {project, otp, 2}. + {project, beam, 3}. + {project, mnesia, 5}. + {project, wolf, 6}. + {project, documentation, 7}. + {project, www, 8}. + </code> + <p>The above three tables, titled <c>employees</c>, + <c>dept</c>, and <c>projects</c>, are the tables which are + made up of real records. The following database content is + stored in the tables which is built on + relationships. These tables are titled <c>manager</c>, + <c>at_dep</c>, and <c>in_proj</c>. + </p> + <p><em>Manager</em></p> + <code type="none"> + + {manager, 104465, 'B/SF'}. + {manager, 104465, 'B/SFP'}. + {manager, 114872, 'B/SFR'}. + </code> + <p><em>At_dep</em></p> + <code type="none"> + {at_dep, 104465, 'B/SF'}. + {at_dep, 107912, 'B/SF'}. + {at_dep, 114872, 'B/SFR'}. + {at_dep, 104531, 'B/SFR'}. + {at_dep, 104659, 'B/SFR'}. + {at_dep, 104732, 'B/SFR'}. + {at_dep, 117716, 'B/SFP'}. + {at_dep, 115018, 'B/SFP'}. + </code> + <p><em>In_proj</em></p> + <code type="none"> + {in_proj, 104465, otp}. + {in_proj, 107912, otp}. + {in_proj, 114872, otp}. + {in_proj, 104531, otp}. + {in_proj, 104531, mnesia}. + {in_proj, 104545, wolf}. + {in_proj, 104659, otp}. + {in_proj, 104659, wolf}. + {in_proj, 104732, otp}. + {in_proj, 104732, mnesia}. + {in_proj, 104732, erlang}. + {in_proj, 117716, otp}. + {in_proj, 117716, documentation}. + {in_proj, 115018, otp}. + {in_proj, 115018, mnesia}. + </code> + <p>The room number is an attribute of the employee + record. This is a structured attribute which consists of a + tuple. The first element of the tuple identifies a corridor, + and the second element identifies the actual room in the + corridor. We could have chosen to represent this as a record + <c>-record(room, {corr, no}).</c> instead of an anonymous + tuple representation. + </p> + <p>The Company database is now initialized and contains + data. </p> + </section> + + <section> + <title>Writing Queries</title> + <p>Retrieving data from DBMS should usually be done with <c>mnesia:read/3</c> or + <c>mnesia:read/1</c> functions. The following function raises the salary:</p> + <codeinclude file="company.erl" tag="%5" type="erl"></codeinclude> + <p>Since we want to update the record using <c>mnesia:write/1</c> after we have + increased the salary we acquire a write lock (third argument to read) when we read the + record from the table. + </p> + <p>It is not always the case that we can directly read the values from the table, + we might need to search the table or several tables to get the data we want, this + is done by writing database queries. Queries are always more expensive operations + than direct lookups done with <c>mnesia:read</c> and should be avoided in performance + critical code.</p> + <p>There are two methods for writing database queries: + </p> + <list type="bulleted"> + <item>Mnesia functions + </item> + <item>QLC</item> + </list> + + <section> + <title>Mnesia functions </title> + <p></p> + <p>The following function extracts the names of the female employees + stored in the database: + </p> + <pre> +\011 mnesia:select(employee, [{#employee{sex = female, name = '$1', _ = '_'},[], ['$1']}]). + </pre> + <p>Select must always run within an activity such as a + transaction. To be able to call from the shell we might + construct a function as: + </p> + <codeinclude file="company.erl" tag="%20" type="erl"></codeinclude> + <p>The select expression matches all entries in table employee with + the field sex set to female. + </p> + <p>This function can be called from the shell as follows: + </p> + <pre> + (klacke@gin)1> <input>company:all_females().</input> + {atomic, ["Carlsson Tuula", "Fedoriw Anna"]} + </pre> + <p>See also the <seealso marker="Mnesia_chap4#matching">Pattern Matching </seealso> + chapter for a description of select and its syntax. + </p> + </section> + + <section> + <title>Using QLC </title> + <p>This section contains simple introductory examples + only. Refer to <em>QLC reference manual</em> for a + full description of the QLC query language. Using QLC + might be more expensive than using Mnesia functions directly but + offers a nice syntax. + </p> + <p>The following function extracts a list of female employees + from the database: + </p> + <pre> + Q = qlc:q([E#employee.name || E <![CDATA[<-]]> mnesia:table(employee), +\011 E#employee.sex == female]), +\011 qlc:e(Q), + </pre> + <p>Accessing mnesia tables from a QLC list comprehension must + always be done within a transaction. Consider the following + function: + </p> + <codeinclude file="company.erl" tag="%2" type="erl"></codeinclude> + <p>This function can be called from the shell as follows: + </p> + <pre> + (klacke@gin)1> <input>company:females().</input> + {atomic, ["Carlsson Tuula", "Fedoriw Anna"]} + </pre> + <p>In traditional relational database terminology, the above + operation would be called a selection, followed by a projection. + </p> + <p>The list comprehension expression shown above contains a + number of syntactical elements. + </p> + <list type="bulleted"> + <item>the first <c>[</c> bracket should be read as "build the + list" + </item> + <item>the <c>||</c> "such that" and the arrow <c><![CDATA[<-]]></c> should + be read as "taken from" + </item> + </list> + <p>Hence, the above list comprehension demonstrates the + formation of the list <c>E#employee.name</c> such that <c>E</c> is + taken from the table of employees and the <c>sex</c> attribute + of each records is equal with the atom <c>female</c>. + </p> + <p>The whole list comprehension must be given to the + <c>qlc:q/1</c> function. + </p> + <p>It is possible to combine list comprehensions with low + level Mnesia functions in the same transaction. If we want to + raise the salary of all female employees we execute: + </p> + <codeinclude file="company.erl" tag="%4" type="erl"></codeinclude> + <p>The function <c>raise_females/1</c> returns the tuple + <c>{atomic, Number}</c>, where <c>Number</c> is the number of + female employees who received a salary increase. Should an error + occur, the value <c>{aborted, Reason}</c> is returned. In the + case of an error, Mnesia guarantees that the salary is not + raised for any employees at all. + </p> + <pre> + + 33><input>company:raise_females(33).</input> + {atomic,2} + </pre> + </section> + </section> + </section> +</chapter> + diff --git a/lib/mnesia/doc/src/Mnesia_chap3.xml b/lib/mnesia/doc/src/Mnesia_chap3.xml new file mode 100644 index 0000000000..9a382bcb5a --- /dev/null +++ b/lib/mnesia/doc/src/Mnesia_chap3.xml @@ -0,0 +1,556 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Building A Mnesia Database</title> + <prepared></prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date></date> + <rev></rev> + <file>Mnesia_chap3.xml</file> + </header> + <p>This chapter details the basic steps involved when designing + a Mnesia database and the programming constructs which make different + solutions available to the programmer. The chapter includes the following + sections: + </p> + <list type="bulleted"> + <item>defining a schema</item> + <item>the datamodel</item> + <item>starting Mnesia</item> + <item>creating new tables.</item> + </list> + + <section> + <marker id="def_schema"></marker> + <title>Defining a Schema</title> + <p>The configuration of a Mnesia system is described in the + schema. The schema is a special table which contains information + such as the table names and each table's + storage type, (i.e. whether a table should be stored in RAM, + on disc or possibly on both, as well as its location). + </p> + <p>Unlike data tables, information contained in schema tables can only be + accessed and modified by using the schema related functions + described in this section. + </p> + <p>Mnesia has various functions for defining the + database schema. It is possible to move tables, delete tables, + or reconfigure the layout of tables. + </p> + <p>An important aspect of these functions is that the system can access a + table while it is being reconfigured. For example, it is possible to move a + table and simultaneously perform write operations to the same + table. This feature is essential for applications that require + continuous service. + </p> + <p>The following section describes the functions available for schema management, + all of which return a tuple: + </p> + <list type="bulleted"> + <item><c>{atomic, ok}</c>; or, + </item> + <item><c>{aborted, Reason}</c> if unsuccessful.</item> + </list> + + <section> + <title>Schema Functions</title> + <list type="bulleted"> + <item><c>mnesia:create_schema(NodeList)</c>. This function is + used to initialize a new, empty schema. This is a mandatory + requirement before Mnesia can be started. Mnesia is a truly + distributed DBMS and the schema is a system table that is + replicated on all nodes in a Mnesia system. + The function will fail if a schema is already present on any of + the nodes in <c>NodeList</c>. This function requires Mnesia + to be stopped on the all + <c>db_nodes</c> contained in the parameter <c>NodeList</c>. + Applications call this function only once, + since it is usually a one-time activity to initialize a new + database. + </item> + <item><c>mnesia:delete_schema(DiscNodeList)</c>. This function + erases any old schemas on the nodes in + <c>DiscNodeList</c>. It also removes all old tables together + with all data. This function requires Mnesia to be stopped + on all <c>db_nodes</c>. + </item> + <item><c>mnesia:delete_table(Tab)</c>. This function + permanently deletes all replicas of table <c>Tab</c>. + </item> + <item><c>mnesia:clear_table(Tab)</c>. This function + permanently deletes all entries in table <c>Tab</c>. + </item> + <item><c>mnesia:move_table_copy(Tab, From, To)</c>. This + function moves the copy of table <c>Tab</c> from node + <c>From</c> to node <c>To</c>. The table storage type, + <c>{type}</c> is preserved, so if a RAM table is moved from + one node to another node, it remains a RAM table on the new + node. It is still possible for other transactions to perform + read and write operation to the table while it is being + moved. + </item> + <item><c>mnesia:add_table_copy(Tab, Node, Type)</c>. This + function creates a replica of the table <c>Tab</c> at node + <c>Node</c>. The <c>Type</c> argument must be either of the + atoms <c>ram_copies</c>, <c>disc_copies</c>, or + <c>disc_only_copies</c>. If we add a copy of the system + table <c>schema</c> to a node, this means that we want the + Mnesia schema to reside there as well. This action then + extends the set of nodes that comprise this particular + Mnesia system. + </item> + <item><c>mnesia:del_table_copy(Tab, Node)</c>. This function + deletes the replica of table <c>Tab</c> at node <c>Node</c>. + When the last replica of a table is removed, the table is + deleted. + </item> + <item> + <p><c>mnesia:transform_table(Tab, Fun, NewAttributeList, NewRecordName)</c>. This + function changes the format on all records in table + <c>Tab</c>. It applies the argument <c>Fun</c> to all + records in the table. <c>Fun</c> shall be a function which + takes an record of the old type, and returns the record of the new + type. The table key may not be changed.</p> + <code type="none"> +-record(old, {key, val}). +-record(new, {key, val, extra}). + +Transformer = + fun(X) when record(X, old) -> + #new{key = X#old.key, + val = X#old.val, + extra = 42} + end, +{atomic, ok} = mnesia:transform_table(foo, Transformer, + record_info(fields, new), + new), + </code> + <p>The <c>Fun</c> argument can also be the atom + <c>ignore</c>, it indicates that only the meta data about the table will + be updated. Usage of <c>ignore</c> is not recommended (since it creates + inconsistencies between the meta data and the actual data) but included + as a possibility for the user do to his own (off-line) transform.</p> + </item> + <item><c>change_table_copy_type(Tab, Node, ToType)</c>. This + function changes the storage type of a table. For example, a + RAM table is changed to a disc_table at the node specified + as <c>Node</c>.</item> + </list> + </section> + </section> + + <section> + <title>The Data Model</title> + <p>The data model employed by Mnesia is an extended + relational data model. Data is organized as a set of + tables and relations between different data records can + be modeled as additional tables describing the actual + relationships. + Each table contains instances of Erlang records + and records are represented as Erlang tuples. + </p> + <p>Object identifiers, also known as oid, are made up of a table name and a key. + For example, if we have an employee record represented by the tuple + <c>{employee, 104732, klacke, 7, male, 98108, {221, 015}}</c>. + This record has an object id, (Oid) which is the tuple + <c>{employee, 104732}</c>. + </p> + <p>Thus, each table is made up of records, where the first element + is a record name and the second element of the table is a key + which identifies the particular record in that table. The + combination of the table name and a key, is an arity two tuple + <c>{Tab, Key}</c> called the Oid. See Chapter 4:<seealso marker="Mnesia_chap4#recordnames_tablenames">Record Names Versus Table Names</seealso>, for more information + regarding the relationship between the record name and the table + name. + </p> + <p>What makes the Mnesia data model an extended relational model + is the ability to store arbitrary Erlang terms in the attribute + fields. One attribute value could for example be a whole tree of + oids leading to other terms in other tables. This + type of record is hard to model in traditional relational + DBMSs.</p> + </section> + + <section> + <marker id="start_mnesia"></marker> + <title>Starting Mnesia</title> + <p>Before we can start Mnesia, we must initialize an empty schema + on all the participating nodes. + </p> + <list type="bulleted"> + <item>The Erlang system must be started. + </item> + <item>Nodes with disc database schema must be defined and + implemented with the function <c>create_schema(NodeList).</c></item> + </list> + <p>When running a distributed system, with two or more + participating nodes, then the <c>mnesia:start( ).</c> function + must be executed on each participating node. Typically this would + be part of the boot script in an embedded environment. + In a test environment or an interactive environment, + <c>mnesia:start()</c> can also be used either from the + Erlang shell, or another program. + </p> + + <section> + <title>Initializing a Schema and Starting Mnesia</title> + <p>To use a known example, we illustrate how to run the + Company database described in Chapter 2 on two separate nodes, + which we call <c>a@gin</c> and <c>b@skeppet</c>. Each of these + nodes must have have a Mnesia directory as well as an + initialized schema before Mnesia can be started. There are two + ways to specify the Mnesia directory to be used: + </p> + <list type="bulleted"> + <item> + <p>Specify the Mnesia directory by providing an application + parameter either when starting the Erlang shell or in the + application script. Previously the following example was used + to create the directory for our Company database:</p> + <pre> +%<input>erl -mnesia dir '"/ldisc/scratch/Mnesia.Company"'</input> + </pre> + </item> + <item>If no command line flag is entered, then the Mnesia + directory will be the current working directory on the node + where the Erlang shell is started.</item> + </list> + <p>To start our Company database and get it running on the two + specified nodes, we enter the following commands: + </p> + <list type="ordered"> + <item> + <p>On the node called gin:</p> + <pre> + gin %<input>erl -sname a -mnesia dir '"/ldisc/scratch/Mnesia.company"'</input> + </pre> + </item> + <item> + <p>On the node called skeppet:</p> + <pre> +skeppet %<input>erl -sname b -mnesia dir '"/ldisc/scratch/Mnesia.company"'</input> + </pre> + </item> + <item> + <p>On one of the two nodes:</p> + <pre> +(a@gin1)><input>mnesia:create_schema([a@gin, b@skeppet]).</input> + </pre> + </item> + <item>The function <c>mnesia:start()</c> is called on both + nodes. + </item> + <item> + <p>To initialize the database, execute the following + code on one of the two nodes.</p> + <codeinclude file="company.erl" tag="%12" type="erl"></codeinclude> + </item> + </list> + <p>As illustrated above, the two directories reside on different nodes, because the + <c>/ldisc/scratch</c> (the "local" disc) exists on the two different + nodes. + </p> + <p>By executing these commands we have configured two Erlang + nodes to run the Company database, and therefore, initialize the + database. This is required only once when setting up, the next time the + system is started <c>mnesia:start()</c> is called + on both nodes, to initialize the system from disc. + </p> + <p>In a system of Mnesia nodes, every node is aware of the + current location of all tables. In this example, data is + replicated on both nodes and functions which manipulate the + data in our tables can be executed on either of the two nodes. + Code which manipulate Mnesia data behaves identically + regardless of where the data resides. + </p> + <p>The function <c>mnesia:stop()</c> stops Mnesia on the node + where the function is executed. Both the <c>start/0</c> and + the <c>stop/0</c> functions work on the "local" Mnesia system, + and there are no functions which start or stop a set of nodes. + </p> + </section> + + <section> + <title>The Start-Up Procedure</title> + <p>Mnesia is started by calling the following function: + </p> + <code type="none"> + mnesia:start(). + </code> + <p>This function initiates the DBMS locally. </p> + <p>The choice of configuration will alter the location and load + order of the tables. The alternatives are listed below: <br></br> +</p> + <list type="ordered"> + <item>Tables that are stored locally only, are initialized + from the local Mnesia directory. + </item> + <item>Replicated tables that reside locally + as well as somewhere else are either initiated from disc or + by copying the entire table from the other node depending on + which of the different replicas is the most recent. Mnesia + determines which of the tables is the most recent. + </item> + <item>Tables that reside on remote nodes are available to other nodes as soon + as they are loaded.</item> + </list> + <p>Table initialization is asynchronous, the function + call <c>mnesia:start()</c> returns the atom <c>ok</c> and + then starts to initialize the different tables. Depending on + the size of the database, this may take some time, and the + application programmer must wait for the tables that the + application needs before they can be used. This achieved by using + the function:</p> + <list type="bulleted"> + <item><c>mnesia:wait_for_tables(TabList, Timeout)</c></item> + </list> + <p>This function suspends the caller until all tables + specified in <c>TabList</c> are properly initiated. + </p> + <p>A problem can arise if a replicated table on one node is + initiated, but Mnesia deduces that another (remote) + replica is more recent than the replica existing on + the local node, the initialization procedure will not proceed. + In this situation, a call to to + <c>mnesia:wait_for_tables/2</c> suspends the caller until the + remote node has initiated the table from its local disc and + the node has copied the table over the network to the local node. + </p> + <p>This procedure can be time consuming however, the shortcut function + shown below will load all the tables from disc at a faster rate: + </p> + <list type="bulleted"> + <item><c>mnesia:force_load_table(Tab)</c>. This function forces + tables to be loaded from disc regardless of the network + situation.</item> + </list> + <p>Thus, we can assume that if an application + wishes to use tables <c>a</c> and <c>b</c>, then the + application must perform some action similar to the below code before it can utilize the tables. + </p> + <pre> + case mnesia:wait_for_tables([a, b], 20000) of + {timeout, RemainingTabs} -> + panic(RemainingTabs); + ok -> + synced + end. + </pre> + <warning> + <p>When tables are forcefully loaded from the local disc, + all operations that were performed on the replicated table + while the local node was down, and the remote replica was + alive, are lost. This can cause the database to become + inconsistent.</p> + </warning> + <p>If the start-up procedure fails, the + <c>mnesia:start()</c> function returns the cryptic tuple + <c>{error,{shutdown, {mnesia_sup,start,[normal,[]]}}}</c>. + Use command line arguments -boot start_sasl as argument to + the erl script in order to get more information + about the start failure. + </p> + </section> + </section> + + <section> + <marker id="create_tables"></marker> + <title>Creating New Tables</title> + <p>Mnesia provides one function to create new tables. This + function is: <c>mnesia:create_table(Name, ArgList).</c></p> + <p>When executing this function, it returns one of the following + responses: + </p> + <list type="bulleted"> + <item><c>{atomic, ok}</c> if the function executes + successfully + </item> + <item><c>{aborted, Reason}</c> if the function fails. + </item> + </list> + <p>The function arguments are: + </p> + <list type="bulleted"> + <item><c>Name</c> is the atomic name of the table. It is + usually the same name as the name of the records that + constitute the table. (See <c>record_name</c> for more + details.) + </item> + <item> + <p><c>ArgList</c> is a list of <c>{Key,Value}</c> tuples. + The following arguments are valid: + </p> + <list type="bulleted"> + <item> + <p><c>{type, Type}</c> where <c>Type</c> must be either of the + atoms <c>set</c>, <c>ordered_set</c> or <c>bag</c>. + The default value is + <c>set</c>. Note: currently 'ordered_set' + is not supported for 'disc_only_copies' tables. + A table of type <c>set</c> or <c>ordered_set</c> has either zero or + one record per key. Whereas a table of type <c>bag</c> can + have an arbitrary number of records per key. The key for + each record is always the first attribute of the record.</p> + <p>The following example illustrates the difference between + type <c>set</c> and <c>bag</c>: </p> + <pre> + f() -> F = fun() -> +\011 mnesia:write({foo, 1, 2}), mnesia:write({foo, 1, 3}), +\011 mnesia:read({foo, 1}) end, mnesia:transaction(F). </pre> + <p>This transaction will return the list <c>[{foo,1,3}]</c> if + the <c>foo</c> table is of type <c>set</c>. However, list + <c>[{foo,1,2}, {foo,1,3}]</c> will return if the table is + of type <c>bag</c>. Note the use of <c>bag</c> and + <c>set</c> table types. </p> + <p>Mnesia tables can never contain + duplicates of the same record in the same table. Duplicate + records have attributes with the same contents and key. + </p> + </item> + <item> + <p><c>{disc_copies, NodeList}</c>, where <c>NodeList</c> is a + list of the nodes where this table will reside on disc.</p> + <p>Write operations to a table replica of type + <c>disc_copies</c> will write data to the disc copy as well + as to the RAM copy of the table. </p> + <p>It is possible to have a + replicated table of type <c>disc_copies</c> on one node, and + the same table stored as a different type on another node. + The default value is <c>[]</c>. This arrangement is + desirable if we want the following operational + characteristics are required:</p> + <list type="ordered"> + <item>read operations must be very fast and performed in RAM + </item> + <item>all write operations must be written to persistent + storage.</item> + </list> + <p>A write operation on a <c>disc_copies</c> table + replica will be performed in two steps. First the write + operation is appended to a log file, then the actual + operation is performed in RAM. + </p> + </item> + <item> + <p><c>{ram_copies, NodeList}</c>, where <c>NodeList</c> is a + list of the nodes where this table is stored in RAM. The + default value for <c>NodeList</c> is <c>[node()]</c>. If the + default value is used to create a new table, it will be + located on the local node only. </p> + <p>Table replicas of type + <c>ram_copies</c> can be dumped to disc with the function + <c>mnesia:dump_tables(TabList)</c>. + </p> + </item> + <item><c>{disc_only_copies, NodeList}</c>. These table + replicas are stored on disc only and are therefore slower to + access. However, a disc only replica consumes less memory than + a table replica of the other two storage types. + </item> + <item><c>{index, AttributeNameList}</c>, where + <c>AttributeNameList</c> is a list of atoms specifying the + names of the attributes Mnesia shall build and maintain. An + index table will exist for every element in the list. The + first field of a Mnesia record is the key and thus need no + extra index. + <br></br> +The first field of a record is the second element of the + tuple, which is the representation of the record. + </item> + <item><c>{snmp, SnmpStruct}</c>. <c>SnmpStruct</c> is + described in the SNMP User Guide. Basically, if this attribute + is present in <c>ArgList</c> of <c>mnesia:create_table/2</c>, + the table is immediately accessible by means of the Simple + Network Management Protocol (SNMP). + <br></br> +It is easy to design applications which use SNMP to + manipulate and control the system. Mnesia provides a direct + mapping between the logical tables that make up an SNMP + control application and the physical data which make up a + Mnesia table. <c>[]</c> + is default. + </item> + <item><c>{local_content, true}</c> When an application needs a + table whose contents should be locally unique on each + node, + <c>local_content</c> tables may be used. The name of the + table is known to all Mnesia nodes, but its contents is + unique for each node. Access to this type of table must be + done locally. </item> + <item> + <p><c>{attributes, AtomList}</c> is a list of the attribute + names for the records that are supposed to populate the + table. The default value is the list <c>[key, val]</c>. The + table must at least have one extra attribute besides the + key. When accessing single attributes in a record, it is not + recommended to hard code the attribute names as atoms. Use + the construct <c>record_info(fields,record_name)</c> + instead. The expression + <c>record_info(fields,record_name)</c> is processed by the + Erlang macro pre-processor and returns a list of the + record's field names. With the record definition + <c>-record(foo, {x,y,z}).</c> the expression + <c>record_info(fields,foo)</c> is expanded to the list + <c>[x,y,z]</c>. Accordingly, it is possible to provide the + attribute names yourself, or to use the <c>record_info/2</c> + notation. </p> + <p>It is recommended that + the <c>record_info/2</c> notation be used as it is easier to + maintain the program and it will be more robust with regards + to future record changes. + </p> + </item> + <item> + <p><c>{record_name, Atom}</c> specifies the common name of + all records stored in the table. All records, stored in + the table, must have this name as their first element. + The <c>record_name</c> defaults to the name of the + table. For more information see Chapter 4:<seealso marker="Mnesia_chap4#recordnames_tablenames">Record Names Versus Table Names</seealso>.</p> + </item> + </list> + </item> + </list> + <p>As an example, assume we have the record definition:</p> + <pre> + -record(funky, {x, y}). + </pre> + <p>The below call would create a table which is replicated on two + nodes, has an additional index on the <c>y</c> attribute, and is + of type + <c>bag</c>.</p> + <pre> + mnesia:create_table(funky, [{disc_copies, [N1, N2]}, {index, + [y]}, {type, bag}, {attributes, record_info(fields, funky)}]). + </pre> + <p>Whereas a call to the below default code values: </p> + <pre> +mnesia:create_table(stuff, []) </pre> + <p>would return a table with a RAM copy on the + local node, no additional indexes and the attributes defaulted to + the list <c>[key,val]</c>.</p> + </section> +</chapter> + diff --git a/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc new file mode 100644 index 0000000000..7d89c1b0dd --- /dev/null +++ b/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc @@ -0,0 +1,1171 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Transactions and Other Access Contexts</title> + <prepared>Claes Wikström, Hans Nilsson and Håkan Mattsson</prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date></date> + <rev></rev> + <file>Mnesia_chap4.xml</file> + </header> + <p>This chapter describes the Mnesia transaction system and the + transaction properties which make Mnesia a fault tolerant, + distributed database management system. + </p> + <p>Also covered in this chapter are the locking functions, + including table locks and sticky locks, as well as alternative + functions which bypass the transaction system in favor of improved + speed and reduced overheads. These functions are called "dirty + operations". We also describe the usage of nested transactions. + This chapter contains the following sections: + </p> + <list type="bulleted"> + <item>transaction properties, which include atomicity, + consistency, isolation, and durability + </item> + <item>Locking + </item> + <item>Dirty operations + </item> + <item>Record names vs table names + </item> + <item>Activity concept and various access contexts + </item> + <item>Nested transactions + </item> + <item>Pattern matching + </item> + <item>Iteration + </item> + </list> + + <section> + <marker id="trans_prop"></marker> + <title>Transaction Properties</title> + <p>Transactions are an important tool when designing fault + tolerant, distributed systems. A Mnesia transaction is a mechanism + by which a series of database operations can be executed as one + functional block. The functional block which is run as a + transaction is called a Functional Object (Fun), and this code can + read, write, or delete Mnesia records. The Fun is evaluated as a + transaction which either commits, or aborts. If a transaction + succeeds in executing Fun it will replicate the action on all nodes + involved, or abort if an error occurs. + </p> + <p>The following example shows a transaction which raises the + salary of certain employee numbers. + </p> + <codeinclude file="company.erl" tag="%5" type="erl"></codeinclude> + <p>The transaction <c>raise(Eno, Raise) - ></c> contains a Fun + made up of four lines of code. This Fun is called by the statement + <c>mnesia:transaction(F)</c> and returns a value. + </p> + <p>The Mnesia transaction system facilitates the construction of + reliable, distributed systems by providing the following important + properties: + </p> + <list type="bulleted"> + <item>The transaction handler ensures that a Fun which is placed + inside a transaction does not interfere with operations embedded + in other transactions when it executes a series of operations on + tables. + </item> + <item>The transaction handler ensures that either all operations + in the transaction are performed successfully on all nodes + atomically, or the transaction fails without permanent effect on + any of the nodes. + </item> + <item>The Mnesia transactions have four important properties, + which we call <em>A</em>tomicity, + <em>C</em>onsistency,<em>I</em>solation, and + <em>D</em>urability, or ACID for short. These properties are + described in the following sub-sections.</item> + </list> + + <section> + <title>Atomicity</title> + <p><em>Atomicity</em> means that database changes which are + executed by a transaction take effect on all nodes involved, or + on none of the nodes. In other words, the transaction either + succeeds entirely, or it fails entirely. + </p> + <p>Atomicity is particularly important when we want to + atomically write more than one record in the same + transaction. The <c>raise/2</c> function, shown as an example + above, writes one record only. The <c>insert_emp/3</c> function, + shown in the program listing in Chapter 2, writes the record + <c>employee</c> as well as employee relations such as + <c>at_dep</c> and <c>in_proj</c> into the database. If we run + this latter code inside a transaction, then the transaction + handler ensures that the transaction either succeeds completely, + or not at all. + </p> + <p>Mnesia is a distributed DBMS where data can be replicated on + several nodes. In many such applications, it is important that a + series of write operations are performed atomically inside a + transaction. The atomicity property ensures that a transaction + take effect on all nodes, or none at all. </p> + </section> + + <section> + <title>Consistency</title> + <p><em>Consistency</em>. This transaction property ensures that + a transaction always leaves the DBMS in a consistent state. For + example, Mnesia ensures that inconsistencies will not occur if + Erlang, Mnesia or the computer crashes while a write operation + is in progress. + </p> + </section> + + <section> + <title>Isolation</title> + <p><em>Isolation</em>. This transaction property ensures that + transactions which execute on different nodes in a network, and + access and manipulate the same data records, will not interfere + with each other. + </p> + <p>The isolation property makes it possible to concurrently execute + the <c>raise/2</c> function. A classical problem in concurrency control + theory is the so called "lost update problem". + </p> + <p>The isolation property is extremely useful if the following + circumstances occurs where an employee (with an employee number + 123) and two processes, (P1 and P2), are concurrently trying to + raise the salary for the employee. The initial value of the + employees salary is, for example, 5. Process P1 then starts to execute, + it reads the employee record and adds 2 to the salary. At this + point in time, process P1 is for some reason preempted and + process P2 has the opportunity to run. P2 reads the record, adds 3 + to the salary, and finally writes a new employee record with + the salary set to 8. Now, process P1 start to run again and + writes its employee record with salary set to 7, thus + effectively overwriting and undoing the work performed by + process P2. The update performed by P2 is lost. + </p> + <p>A transaction system makes it possible to concurrently + execute two or more processes which manipulate the same + record. The programmer does not need to check that the + updates are synchronous, this is overseen by the + transaction handler. All programs accessing the database through + the transaction system may be written as if they had sole access + to the data. + </p> + </section> + + <section> + <title>Durability</title> + <p><em>Durability</em>. This transaction property ensures that + changes made to the DBMS by a transaction are permanent. Once a + transaction has been committed, all changes made to the database + are durable - i.e. they are written safely to disc and will not + be corrupted or disappear. + </p> + <note> + <p>The durability feature described does not entirely apply to + situations where Mnesia is configured as a "pure" primary memory + database. + </p> + </note> + </section> + </section> + + <section> + <title>Locking</title> + <p>Different transaction managers employ different strategies to + satisfy the isolation property. Mnesia uses the standard technique + of two-phase locking. This means that locks are set on records + before they are read or written. Mnesia uses five different kinds + of locks. + </p> + <list type="bulleted"> + <item><em>Read locks</em>. A read lock is set on one replica of + a record before it can be read. + </item> + <item><em>Write locks</em>. Whenever a transaction writes to an + record, write locks are first set on all replicas of that + particular record. + </item> + <item><em>Read table locks</em>. If a transaction traverses an + entire table in search for a record which satisfy some + particular property, it is most inefficient to set read locks on + the records, one by one. It is also very memory consuming, since + the read locks themselves may take up considerable space if the + table is very large. For this reason, Mnesia can set a read lock + on an entire table. + </item> + <item><em>Write table locks</em>. If a transaction writes a + large number of records to one table, it is possible to set a + write lock on the entire table. + </item> + <item><em>Sticky locks</em>. These are write locks that stay in + place at a node after the transaction which initiated the lock + has terminated. </item> + </list> + <p>Mnesia employs a strategy whereby functions such as + <c>mnesia:read/1</c> acquire the necessary locks dynamically as + the transactions execute. Mnesia automatically sets and releases + the locks and the programmer does not have to code these + operations. + </p> + <p>Deadlocks can occur when concurrent processes set and release + locks on the same records. Mnesia employs a "wait-die" strategy to + resolve these situations. If Mnesia suspects that a deadlock can + occur when a transaction tries to set a lock, the transaction is + forced to release all its locks and sleep for a while. The + Fun in the transaction will be evaluated one more time. + </p> + <p>For this reason, it is important that the code inside the Fun given to + <c>mnesia:transaction/1</c> is pure. Some strange results can + occur if, for example, messages are sent by the transaction + Fun. The following example illustrates this situation: + </p> + <codeinclude file="company.erl" tag="%6" type="erl"></codeinclude> + <p>This transaction could write the text <c>"Trying to write ... "</c> a thousand times to the terminal. Mnesia does guarantee, + however, that each and every transaction will eventually run. As a + result, Mnesia is not only deadlock free, but also livelock + free. + </p> + <p>The Mnesia programmer cannot prioritize one particular + transaction to execute before other transactions which are waiting + to execute. As a result, the Mnesia DBMS transaction system is not + suitable for hard real time applications. However, Mnesia contains + other features that have real time properties. + </p> + <p>Mnesia dynamically sets and releases locks as + transactions execute, therefore, it is very dangerous to execute code with + transaction side-effects. In particular, a <c>receive</c> + statement inside a transaction can lead to a situation where the + transaction hangs and never returns, which in turn can cause locks + not to release. This situation could bring the whole system to a + standstill since other transactions which execute in other + processes, or on other nodes, are forced to wait for the defective + transaction. + </p> + <p>If a transaction terminates abnormally, Mnesia will + automatically release the locks held by the transaction. + </p> + <p>We have shown examples of a number of functions that can be + used inside a transaction. The following list shows the + <em>simplest</em> Mnesia functions that work with transactions. It + is important to realize that these functions must be embedded in a + transaction. If no enclosing transaction (or other enclosing + Mnesia activity) exists, they will all fail. + </p> + <list type="bulleted"> + <item><c>mnesia:transaction(Fun) -> {aborted, Reason} |{atomic, Value}</c>. This function executes one transaction with the + functional object <c>Fun</c> as the single parameter. + </item> + <item><c>mnesia:read({Tab, Key}) -> transaction abort | RecordList</c>. This function reads all records with <c>Key</c> + as key from table <c>Tab</c>. This function has the same semantics + regardless of the location of <c>Table</c>. If the table is of + type <c>bag</c>, the <c>read({Tab, Key})</c> can return an arbitrarily + long list. If the table is of type <c>set</c>, the list is + either of length one, or <c>[]</c>. + </item> + <item><c>mnesia:wread({Tab, Key}) -> transaction abort | RecordList</c>. This function behaves the same way as the + previously listed <c>read/1</c> function, except that it + acquires a write lock instead of a read lock. If we execute a + transaction which reads a record, modifies the record, and then + writes the record, it is slightly more efficient to set the + write lock immediately. In cases where we issue a + <c>mnesia:read/1</c>, followed by a <c>mnesia:write/1</c>, the + first read lock must be upgraded to a write lock when the write + operation is executed. + </item> + <item><c>mnesia:write(Record) -> transaction abort | ok</c>. This function writes a record into the database. The + <c>Record</c> argument is an instance of a record. The function + returns <c>ok</c>, or aborts the transaction if an error should + occur. + </item> + <item><c>mnesia:delete({Tab, Key}) -> transaction abort | ok</c>. This + function deletes all records with the given key. + </item> + <item><c>mnesia:delete_object(Record) -> transaction abort | ok</c>. This function deletes records with object id + <c>Record</c>. This function is used when we want to delete only + some records in a table of type <c>bag</c>. </item> + </list> + + <section> + <title>Sticky Locks</title> + <p>As previously stated, the locking strategy used by Mnesia is + to lock one record when we read a record, and lock all replicas + of a record when we write a record. However, there are + applications which use Mnesia mainly for its fault-tolerant + qualities, and these applications may be configured with one + node doing all the heavy work, and a standby node which is ready + to take over in case the main node fails. Such applications may + benefit from using sticky locks instead of the normal locking + scheme. + </p> + <p>A sticky lock is a lock which stays in place at a node after + the transaction which first acquired the lock has terminated. To + illustrate this, assume that we execute the following + transaction: + </p> + <code type="none"> + F = fun() -> + mnesia:write(#foo{a = kalle}) + end, + mnesia:transaction(F). + </code> + <p>The <c>foo</c> table is replicated on the two nodes <c>N1</c> + and <c>N2</c>. + <br></br> +Normal locking requires: + </p> + <list type="bulleted"> + <item>one network rpc (2 messages) to acquire the write lock + </item> + <item>three network messages to execute the two-phase commit protocol. + </item> + </list> + <p>If we use sticky locks, we must first change the code as follows: + </p> + <code type="none"> + + F = fun() -> + mnesia:s_write(#foo{a = kalle}) + end, + mnesia:transaction(F). + </code> + <p>This code uses the <c>s_write/1</c> function instead of the + <c>write/1</c> function. The <c>s_write/1</c> function sets a + sticky lock instead of a normal lock. If the table is not + replicated, sticky locks have no special effect. If the table is + replicated, and we set a sticky lock on node <c>N1</c>, this + lock will then stick to node <c>N1</c>. The next time we try to + set a sticky lock on the same record at node <c>N1</c>, Mnesia + will see that the lock is already set and will not do a network + operation in order to acquire the lock. + </p> + <p>It is much more efficient to set a local lock than it is to set + a networked lock, and for this reason sticky locks can benefit + application that use a replicated table and perform most of the + work on only one of the nodes. + </p> + <p>If a record is stuck at node <c>N1</c> and we try to set a + sticky lock for the record on node <c>N2</c>, the record must be + unstuck. This operation is expensive and will reduce performance. The unsticking is + done automatically if we issue <c>s_write/1</c> requests at + <c>N2</c>. + </p> + </section> + + <section> + <title>Table Locks</title> + <p>Mnesia supports read and write locks on whole tables as a + complement to the normal locks on single records. As previously + stated, Mnesia sets and releases locks automatically, and the + programmer does not have to code these operations. However, + transactions which read and write a large number of records in a + specific table will execute more efficiently if we start the + transaction by setting a table lock on this table. This will + block other concurrent transactions from the table. The + following two function are used to set explicit table locks for + read and write operations: + </p> + <list type="bulleted"> + <item><c>mnesia:read_lock_table(Tab)</c> Sets a read lock on + the table <c>Tab</c></item> + <item><c>mnesia:write_lock_table(Tab)</c> Sets a write lock on + the table <c>Tab</c></item> + </list> + <p>Alternate syntax for acquisition of table locks is as follows: + </p> + <code type="none"> + mnesia:lock({table, Tab}, read) + mnesia:lock({table, Tab}, write) + </code> + <p>The matching operations in Mnesia may either lock the entire + table or just a single record (when the key is bound in the + pattern). + </p> + </section> + + <section> + <title>Global Locks</title> + <p>Write locks are normally acquired on all nodes where a + replica of the table resides (and is active). Read locks are + acquired on one node (the local one if a local + replica exists). + </p> + <p>The function <c>mnesia:lock/2</c> is intended to support + table locks (as mentioned previously) + but also for situations when locks need to be + acquired regardless of how tables have been replicated: + </p> + <code type="none"> + mnesia:lock({global, GlobalKey, Nodes}, LockKind) + + LockKind ::= read | write | ... + </code> + <p>The lock is acquired on the LockItem on all Nodes in the + nodes list.</p> + </section> + </section> + + <section> + <title>Dirty Operations</title> + <p>In many applications, the overhead of processing a transaction + may result in a loss of performance. Dirty operation are short + cuts which bypass much of the processing and increase the speed + of the transaction. + </p> + <p>Dirty operation are useful in many situations, for example in a datagram routing + application where Mnesia stores the routing table, and it is time + consuming to start a whole transaction every time a packet is + received. For this reason, Mnesia has functions which manipulate + tables without using transactions. This alternative + to processing is known as a dirty operation. However, it is important to + realize the trade-off in avoiding the overhead of transaction + processing: + </p> + <list type="bulleted"> + <item>The atomicity and the isolation properties of Mnesia are lost. + </item> + <item>The isolation property is compromised, because other + Erlang processes, which use transaction to manipulate the data, + do not get the benefit of isolation if we simultaneously use + dirty operations to read and write records from the same table. + </item> + </list> + <p>The major advantage of dirty operations is that they execute + much faster than equivalent operations that are processed as + functional objects within a transaction. + </p> + <p>Dirty operations + are written to disc if they are performed on a table of type + <c>disc_copies</c>, or type <c>disc_only_copies</c>. Mnesia also + ensures that all replicas of a table are updated if a + dirty write operation is performed on a table. + </p> + <p>A dirty operation will ensure a certain level of consistency. + For example, it is not possible for dirty operations to return + garbled records. Hence, each individual read or write operation + is performed in an atomic manner. + </p> + <p>All dirty functions execute a call to <c>exit({aborted, Reason})</c> on failure. Even if the following functions are + executed inside a transaction no locks will be acquired. The + following functions are available: + </p> + <list type="bulleted"> + <item><c>mnesia:dirty_read({Tab, Key})</c>. This function reads + record(s) from Mnesia. + </item> + <item><c>mnesia:dirty_write(Record)</c>. This function writes + the record <c>Record</c></item> + <item><c>mnesia:dirty_delete({Tab, Key})</c>. This function deletes + record(s) with the key <c>Key</c>. + </item> + <item><c>mnesia:dirty_delete_object(Record)</c> This function is + the dirty operation alternative to the function + <c>delete_object/1</c></item> + <item> + <p><c>mnesia:dirty_first(Tab)</c>. This function returns the + "first" key in the table <c>Tab</c>. </p> + <p>Records in <c>set</c> or <c>bag</c> tables are not sorted. + However, there is + a record order which is not known to the user. + This means that it is possible to traverse a table by means of + this function in conjunction with the <c>dirty_next/2</c> + function. + </p> + <p>If there are no records at all in the table, this function + will return the atom <c>'$end_of_table'</c>. It is not + recommended to use this atom as the key for any user + records. + </p> + </item> + <item><c>mnesia:dirty_next(Tab, Key)</c>. This function returns + the "next" key in the table <c>Tab</c>. This function makes it + possible to traverse a table and perform some operation on all + records in the table. When the end of the table is reached the + special key <c>'$end_of_table'</c> is returned. Otherwise, the + function returns a key which can be used to read the actual + record. + <br></br> +The behavior is undefined if any process perform a write + operation on the table while we traverse the table with the + <c>dirty_next/2</c> function. This is because <c>write</c> + operations on a Mnesia table may lead to internal reorganizations + of the table itself. This is an implementation detail, but remember + the dirty functions are low level functions. + </item> + <item><c>mnesia:dirty_last(Tab)</c> This function works exactly as + <c>mnesia:dirty_first/1</c> but returns the last object in + Erlang term order for the <c>ordered_set</c> table type. For + all other table types, <c>mnesia:dirty_first/1</c> and + <c>mnesia:dirty_last/1</c> are synonyms. + </item> + <item><c>mnesia:dirty_prev(Tab, Key)</c> This function works exactly as + <c>mnesia:dirty_next/2</c> but returns the previous object in + Erlang term order for the ordered_set table type. For + all other table types, <c>mnesia:dirty_next/2</c> and + <c>mnesia:dirty_prev/2</c> are synonyms. + </item> + <item> + <p><c>mnesia:dirty_slot(Tab, Slot)</c></p> + <p>Returns the list of records that are associated with Slot + in a table. It can be used to traverse a table in a manner + similar to the <c>dirty_next/2</c> function. A table has a + number of slots that range from zero to some unknown upper + bound. The function <c>dirty_slot/2</c> returns the special + atom <c>'$end_of_table'</c> when the end of the table is + reached. + <br></br> +The behavior of this function is undefined if the + table is written on while being + traversed. <c>mnesia:read_lock_table(Tab)</c> may be used to + ensure that no transaction protected writes are performed + during the iteration. + </p> + </item> + <item> + <p><c>mnesia:dirty_update_counter({Tab,Key}, Val)</c>. </p> + <p>Counters are positive integers with a value greater than or + equal to zero. Updating a counter will add the <c>Val</c> and + the counter where <c>Val</c> is a positive or negative integer. + <br></br> + There exists no special counter records in + Mnesia. However, records on the form of <c>{TabName, Key, Integer}</c> can be used as counters, and can be + persistent. + </p> + <p>It is not possible to have transaction protected updates of + counter records. + </p> + <p>There are two significant differences when using this + function instead of reading the record, performing the + arithmetic, and writing the record: + </p> + <list type="ordered"> + <item>it is much more efficient + </item> + <item>the <c>dirty_update_counter/2</c> function is + performed as an atomic operation although it is not protected by + a transaction. Accordingly, no table update is lost if two + processes simultaneously execute the + <c>dirty_update_counter/2</c> function. + </item> + </list> + </item> + <item><c>mnesia:dirty_match_object(Pat)</c>. This function is + the dirty equivalent of <c>mnesia:match_object/1</c>. + </item> + <item><c>mnesia:dirty_select(Tab, Pat)</c>. This function is + the dirty equivalent of <c>mnesia:select/2</c>. + </item> + <item><c>mnesia:dirty_index_match_object(Pat, Pos)</c>. This + function is the dirty equivalent of + <c>mnesia:index_match_object/2</c>. + </item> + <item><c>mnesia:dirty_index_read(Tab, SecondaryKey, Pos)</c>. This + function is the dirty equivalent of <c>mnesia:index_read/3</c>. + </item> + <item><c>mnesia:dirty_all_keys(Tab)</c>. This function is the + dirty equivalent of <c>mnesia:all_keys/1</c>. + </item> + </list> + </section> + + <section> + <marker id="recordnames_tablenames"></marker> + <title>Record Names versus Table Names</title> + <p>In Mnesia, all records in a table must have the same name. All + the records must be instances of the same + record type. The record name does however not necessarily be + the same as the table name. Even though that it is the case in + the most of the examples in this document. If a table is created + without the <c>record_name</c> property the code below will + ensure all records in the tables have the same name as the table: + </p> + <code type="none"> + mnesia:create_table(subscriber, []) + </code> + <p>However, if the table is is created with an explicit record name + as argument, as shown below, it is possible to store subscriber + records in both of the tables regardless of the table names: + </p> + <code type="none"> + TabDef = [{record_name, subscriber}], + mnesia:create_table(my_subscriber, TabDef), + mnesia:create_table(your_subscriber, TabDef). + </code> + <p>In order to access such + tables it is not possible to use the simplified access functions + as described earlier in the document. For example, + writing a subscriber record into a table requires a + <c>mnesia:write/3</c>function instead of the simplified functions + <c>mnesia:write/1</c> and <c>mnesia:s_write/1</c>: + </p> + <code type="none"> + mnesia:write(subscriber, #subscriber{}, write) + mnesia:write(my_subscriber, #subscriber{}, sticky_write) + mnesia:write(your_subscriber, #subscriber{}, write) + </code> + <p>The following simplified piece of code illustrates the + relationship between the simplified access functions used in + most examples and their more flexible counterparts: + </p> + <code type="none"> + mnesia:dirty_write(Record) -> + Tab = element(1, Record), + mnesia:dirty_write(Tab, Record). + + mnesia:dirty_delete({Tab, Key}) -> + mnesia:dirty_delete(Tab, Key). + + mnesia:dirty_delete_object(Record) -> + Tab = element(1, Record), + mnesia:dirty_delete_object(Tab, Record) + + mnesia:dirty_update_counter({Tab, Key}, Incr) -> + mnesia:dirty_update_counter(Tab, Key, Incr). + + mnesia:dirty_read({Tab, Key}) -> + Tab = element(1, Record), + mnesia:dirty_read(Tab, Key). + + mnesia:dirty_match_object(Pattern) -> + Tab = element(1, Pattern), + mnesia:dirty_match_object(Tab, Pattern). + + mnesia:dirty_index_match_object(Pattern, Attr) + Tab = element(1, Pattern), + mnesia:dirty_index_match_object(Tab, Pattern, Attr). + + mnesia:write(Record) -> + Tab = element(1, Record), + mnesia:write(Tab, Record, write). + + mnesia:s_write(Record) -> + Tab = element(1, Record), + mnesia:write(Tab, Record, sticky_write). + + mnesia:delete({Tab, Key}) -> + mnesia:delete(Tab, Key, write). + + mnesia:s_delete({Tab, Key}) -> + mnesia:delete(Tab, Key, sticky_write). + + mnesia:delete_object(Record) -> + Tab = element(1, Record), + mnesia:delete_object(Tab, Record, write). + + mnesia:s_delete_object(Record) -> + Tab = element(1, Record), + mnesia:delete_object(Tab, Record. sticky_write). + + mnesia:read({Tab, Key}) -> + mnesia:read(Tab, Key, read). + + mnesia:wread({Tab, Key}) -> + mnesia:read(Tab, Key, write). + + mnesia:match_object(Pattern) -> + Tab = element(1, Pattern), + mnesia:match_object(Tab, Pattern, read). + + mnesia:index_match_object(Pattern, Attr) -> + Tab = element(1, Pattern), + mnesia:index_match_object(Tab, Pattern, Attr, read). + </code> + </section> + + <section> + <title>Activity Concept and Various Access Contexts</title> + <p>As previously described, a functional object (Fun) performing + table access operations as listed below may be + passed on as arguments to the function + <c>mnesia:transaction/1,2,3</c>: + </p> + <list type="bulleted"> + <item> + <p>mnesia:write/3 (write/1, s_write/1)</p> + </item> + <item> + <p>mnesia:delete/3 (delete/1, s_delete/1)</p> + </item> + <item> + <p>mnesia:delete_object/3 (delete_object/1, s_delete_object/1)</p> + </item> + <item> + <p>mnesia:read/3 (read/1, wread/1)</p> + </item> + <item> + <p>mnesia:match_object/2 (match_object/1)</p> + </item> + <item> + <p>mnesia:select/3 (select/2)</p> + </item> + <item> + <p>mnesia:foldl/3 (foldl/4, foldr/3, foldr/4)</p> + </item> + <item> + <p>mnesia:all_keys/1</p> + </item> + <item> + <p>mnesia:index_match_object/4 (index_match_object/2)</p> + </item> + <item> + <p>mnesia:index_read/3</p> + </item> + <item> + <p>mnesia:lock/2 (read_lock_table/1, write_lock_table/1)</p> + </item> + <item> + <p>mnesia:table_info/2</p> + </item> + </list> + <p>These functions will be performed in a + transaction context involving mechanisms like locking, logging, + replication, checkpoints, subscriptions, commit protocols + etc.However, the same function may also be + evaluated in other activity contexts. + <br></br> +The following activity access contexts are currently supported: + </p> + <list type="bulleted"> + <item> + <p>transaction </p> + </item> + <item> + <p>sync_transaction</p> + </item> + <item> + <p>async_dirty</p> + </item> + <item> + <p>sync_dirty</p> + </item> + <item> + <p>ets</p> + </item> + </list> + <p>By passing the same "fun" as argument to the function + <c>mnesia:sync_transaction(Fun [, Args])</c> it will be performed + in synced transaction context. Synced transactions waits until all + active replicas has committed the transaction (to disc) before + returning from the mnesia:sync_transaction call. Using + sync_transaction is useful for applications that are executing on + several nodes and want to be sure that the update is performed on + the remote nodes before a remote process is spawned or a message + is sent to a remote process, and also when combining transaction + writes with dirty_reads. This is also useful in situations where + an application performs frequent or voluminous updates which may + overload Mnesia on other nodes. + </p> + <p>By passing the same "fun" as argument to the function + <c>mnesia:async_dirty(Fun [, Args])</c> it will be performed in + dirty context. The function calls will be mapped to the + corresponding dirty functions. This will still involve logging, + replication and subscriptions but there will be no locking, + local transaction storage or commit protocols involved. + Checkpoint retainers will be updated but will be updated + "dirty". Thus, they will be updated asynchronously. The + functions will wait for the operation to be performed on one + node but not the others. If the table resides locally no waiting + will occur. + </p> + <p>By passing the same "fun" as an argument to the function + <c>mnesia:sync_dirty(Fun [, Args])</c> it will be performed in + almost the same context as <c>mnesia:async_dirty/1,2</c>. The + difference is that the operations are performed + synchronously. The caller will wait for the updates to be + performed on all active replicas. Using sync_dirty is useful for + applications that are executing on several nodes and want to be + sure that the update is performed on the remote nodes before a remote + process is spawned or a message is sent to a remote process. This + is also useful in situations where an application performs frequent or + voluminous updates which may overload Mnesia on other + nodes. + </p> + <p>You can check if your code is executed within a transaction with + <c>mnesia:is_transaction/0</c>, it returns <c>true</c> when called + inside a transaction context and false otherwise.</p> + + <p>Mnesia tables with storage type RAM_copies and disc_copies + are implemented internally as "ets-tables" and + it is possible for applications to access the these tables + directly. This is only recommended if all options have been weighed + and the possible outcomes are understood. By passing the earlier + mentioned "fun" to the function + <c>mnesia:ets(Fun [, Args])</c> it will be performed but in a very raw + context. The operations will be performed directly on the + local ets tables assuming that the local storage type are + RAM_copies and that the table is not replicated on other + nodes. Subscriptions will not be triggered nor + checkpoints updated, but this operation is blindingly fast. Disc resident + tables should not be updated with the ets-function since the + disc will not be updated. + </p> + <p>The Fun may also be passed as an argument to the function + <c>mnesia:activity/2,3,4</c> which enables usage of customized + activity access callback modules. It can either be obtained + directly by stating the module name as argument or implicitly + by usage of the <c>access_module</c> configuration parameter. A + customized callback module may be used for several purposes, + such as providing triggers, integrity constraints, run time + statistics, or virtual tables. + <br></br> + The callback module does + not have to access real Mnesia tables, it is free to do whatever + it likes as long as the callback interface is fulfilled. + <br></br> + In Appendix C "The Activity Access Call Back Interface" the source + code for one alternate implementation is provided + (mnesia_frag.erl). The context sensitive function + <c>mnesia:table_info/2</c> may be used to provide virtual + information about a table. One usage of this is to perform + <c>QLC</c> queries within an activity context with a + customized callback module. By providing table information about + table indices and other <c>QLC</c> requirements, + <c>QLC</c> may be used as a generic query language to + access virtual tables. + </p> + <p>QLC queries may be performed in all these activity + contexts (transaction, sync_transaction, async_dirty, sync_dirty + and ets). The ets activity will only work if the table has no + indices. + </p> + <note> + <p>The mnesia:dirty_* function always executes with + async_dirty semantics regardless of which activity access contexts + are invoked. They may even invoke contexts without any + enclosing activity access context.</p> + </note> + </section> + + <section> + <title>Nested transactions</title> + <p>Transactions may be nested in an arbitrary fashion. A child transaction + must run in the same process as its parent. When a child transaction + aborts, the caller of the child transaction will get the + return value <c>{aborted, Reason}</c> and any work performed + by the child will be erased. If a child transaction commits, the + records written by the child will be propagated to the parent. + </p> + <p>No locks are released when child transactions terminate. Locks + created by a sequence of nested transactions are kept until + the topmost transaction terminates. Furthermore, any updates + performed by a nested transaction are only propagated + in such a manner so that the parent of the nested transaction + sees the updates. No final commitment will be done until + the top level transaction is terminated. + So, although a nested transaction returns <c>{atomic, Val}</c>, + if the enclosing parent transaction is aborted, the entire + nested operation is aborted. + </p> + <p>The ability to have nested transaction with identical semantics + as top level transaction makes it easier to write + library functions that manipulate mnesia tables. + </p> + <p>Say for example that we have a function that adds a + new subscriber to a telephony system:</p> + <pre> + add_subscriber(S) -> + mnesia:transaction(fun() -> + case mnesia:read( .......... + </pre> + <p>This function needs to be called as a transaction. + Now assume that we wish to write a function that + both calls the <c>add_subscriber/1</c> function and + is in itself protected by the context of a transaction. + By simply calling the <c>add_subscriber/1</c> from within + another transaction, a nested transaction is created. + </p> + <p>It is also possible to mix different activity access contexts while nesting, + but the dirty ones (async_dirty,sync_dirty and ets) will inherit the transaction + semantics if they are called inside a transaction and thus it will grab locks and + use two or three phase commit. + </p> + <pre> + add_subscriber(S) -> + mnesia:transaction(fun() -> + %% Transaction context + mnesia:read({some_tab, some_data}), + mnesia:sync_dirty(fun() -> + %% Still in a transaction context. + case mnesia:read( ..) ..end), end). + add_subscriber2(S) -> + mnesia:sync_dirty(fun() -> + %% In dirty context + mnesia:read({some_tab, some_data}), + mnesia:transaction(fun() -> + %% In a transaction context. + case mnesia:read( ..) ..end), end). + </pre> + </section> + + <section> + <title>Pattern Matching</title> + <marker id="matching"></marker> + <p>When it is not possible to use <c>mnesia:read/3</c> Mnesia + provides the programmer with several functions for matching + records against a pattern. The most useful functions of these are: + </p> + <code type="none"> + mnesia:select(Tab, MatchSpecification, LockKind) -> + transaction abort | [ObjectList] + mnesia:select(Tab, MatchSpecification, NObjects, Lock) -> + transaction abort | {[Object],Continuation} | '$end_of_table' + mnesia:select(Cont) -> + transaction abort | {[Object],Continuation} | '$end_of_table' + mnesia:match_object(Tab, Pattern, LockKind) -> + transaction abort | RecordList + </code> + <p>These functions matches a <c>Pattern</c> against all records in + table <c>Tab</c>. In a <c>mnesia:select</c> call <c>Pattern</c> is + a part of <c>MatchSpecification</c> described below. It is not + necessarily performed as an exhaustive search of the entire + table. By utilizing indices and bound values in the key of the + pattern, the actual work done by the function may be condensed + into a few hash lookups. Using <c>ordered_set</c> tables may reduce the + search space if the keys are partially bound. + </p> + <p>The pattern provided to the functions must be a valid record, + and the first element of the provided tuple must be the + <c>record_name</c> of the table. The special element <c>'_'</c> + matches any data structure in Erlang (also known as an Erlang + term). The special elements <c><![CDATA['$<number>']]></c> behaves as Erlang + variables i.e. matches anything and binds the first occurrence and + matches the coming occurrences of that variable against the bound value. + </p> + <p>Use the function <c>mnesia:table_info(Tab, wild_pattern)</c> + to obtain a basic pattern which matches all records in a table + or use the default value in record creation. + Do not make the pattern hard coded since it will make your code more + vulnerable to future changes of the record definition. + </p> + <code type="none"> + Wildpattern = mnesia:table_info(employee, wild_pattern), + %% Or use + Wildpattern = #employee{_ = '_'}, + </code> + <p>For the employee table the wild pattern will look like:</p> + <code type="none"> + {employee, '_', '_', '_', '_', '_',' _'}. + </code> + <p>In order to constrain the match you must replace some + of the <c>'_'</c> elements. The code for matching out + all female employees, looks like: + </p> + <code type="none"> + Pat = #employee{sex = female, _ = '_'}, + F = fun() -> mnesia:match_object(Pat) end, + Females = mnesia:transaction(F). + </code> + <p>It is also possible to use the match function if we want to + check the equality of different attributes. Assume that we want + to find all employees which happens to have a employee number + which is equal to their room number: + </p> + <code type="none"> + Pat = #employee{emp_no = '$1', room_no = '$1', _ = '_'}, + F = fun() -> mnesia:match_object(Pat) end, + Odd = mnesia:transaction(F). + </code> + <p>The function <c>mnesia:match_object/3</c> lacks some important + features that <c>mnesia:select/3</c> have. For example + <c>mnesia:match_object/3</c> can only return the matching records, + and it can not express constraints other then equality. + If we want to find the names of the male employees on the second floor + we could write: + </p> + <codeinclude file="company.erl" tag="%21" type="erl"></codeinclude> + <p>Select can be used to add additional constraints and create + output which can not be done with <c>mnesia:match_object/3</c>. </p> + <p>The second argument to select is a <c>MatchSpecification</c>. + A <c>MatchSpecification</c> is list of <c>MatchFunctions</c>, where + each <c>MatchFunction</c> consists of a tuple containing + <c>{MatchHead, MatchCondition, MatchBody}</c>. <c>MatchHead</c> + is the same pattern used in <c>mnesia:match_object/3</c> + described above. <c>MatchCondition</c> is a list of additional + constraints applied to each record, and <c>MatchBody</c> is used + to construct the return values. + </p> + <p>A detailed explanation of match specifications can be found in + the <em>Erts users guide: Match specifications in Erlang </em>, + and the ets/dets documentations may provide some additional + information. + </p> + <p>The functions <c>select/4</c> and <c>select/1</c> are used to + get a limited number of results, where the <c>Continuation</c> + are used to get the next chunk of results. Mnesia uses the + <c>NObjects</c> as an recommendation only, thus more or less + results then specified with <c>NObjects</c> may be returned in + the result list, even the empty list may be returned despite there + are more results to collect. + </p> + <warning> + <p>There is a severe performance penalty in using + <c>mnesia:select/[1|2|3|4]</c> after any modifying operations + are done on that table in the same transaction, i.e. avoid using + <c>mnesia:write/1</c> or <c>mnesia:delete/1</c> before a + <c>mnesia:select</c> in the same transaction.</p> + </warning> + <p>If the key attribute is bound in a pattern, the match operation + is very efficient. However, if the key attribute in a pattern is + given as <c>'_'</c>, or <c>'$1'</c>, the whole <c>employee</c> + table must be searched for records that match. Hence if the table is + large, this can become a time consuming operation, but it can be + remedied with indices (refer to Chapter 5: <seealso marker="Mnesia_chap5#indexing">Indexing</seealso>) if + <c>mnesia:match_object</c> is used. + </p> + <p>QLC queries can also be used to search Mnesia tables. By + using <c>mnesia:table/[1|2]</c> as the generator inside a QLC + query you let the query operate on a mnesia table. Mnesia + specific options to <c>mnesia:table/2</c> are {lock, Lock}, + {n_objects,Integer} and {traverse, SelMethod}. The <c>lock</c> + option specifies whether mnesia should acquire a read or write + lock on the table, and <c>n_objects</c> specifies how many + results should be returned in each chunk to QLC. The last option is + <c>traverse</c> and it specifies which function mnesia should + use to traverse the table. Default <c>select</c> is used, but by using + <c>{traverse, {select, MatchSpecification}}</c> as an option to + <c>mnesia:table/2</c> the user can specify it's own view of the + table. + </p> + <p>If no options are specified a read lock will acquired and 100 + results will be returned in each chunk, and select will be used + to traverse the table, i.e.: + </p> + <code type="none"> + mnesia:table(Tab) -> + mnesia:table(Tab, [{n_objects,100},{lock, read}, {traverse, select}]). + </code> + <p>The function <c>mnesia:all_keys(Tab)</c> returns all keys in a + table.</p> + </section> + + <section> + <title>Iteration</title> + <marker id="iteration"></marker> + <p>Mnesia provides a couple of functions which iterates over all + the records in a table. + </p> + <code type="none"> + mnesia:foldl(Fun, Acc0, Tab) -> NewAcc | transaction abort + mnesia:foldr(Fun, Acc0, Tab) -> NewAcc | transaction abort + mnesia:foldl(Fun, Acc0, Tab, LockType) -> NewAcc | transaction abort + mnesia:foldr(Fun, Acc0, Tab, LockType) -> NewAcc | transaction abort + </code> + <p>These functions iterate over the mnesia table <c>Tab</c> and + apply the function <c>Fun</c> to each record. The <c>Fun</c> + takes two arguments, the first argument is a record from the + table and the second argument is the accumulator. The + <c>Fun</c> return a new accumulator. </p> + <p>The first time the <c>Fun</c> is applied <c>Acc0</c> will + be the second argument. The next time the <c>Fun</c> is called + the return value from the previous call, will be used as the + second argument. The term the last call to the Fun returns + will be the return value of the <c>fold[lr]</c> function. + </p> + <p>The difference between <c>foldl</c> and <c>foldr</c> is the + order the table is accessed for <c>ordered_set</c> tables, + for every other table type the functions are equivalent. + </p> + <p><c>LockType</c> specifies what type of lock that shall be + acquired for the iteration, default is <c>read</c>. If + records are written or deleted during the iteration a write + lock should be acquired. </p> + <p>These functions might be used to find records in a table + when it is impossible to write constraints for + <c>mnesia:match_object/3</c>, or when you want to perform + some action on certain records. + </p> + <p>For example finding all the employees who has a salary + below 10 could look like:</p> + <code type="none"><![CDATA[ + find_low_salaries() -> + Constraint = + fun(Emp, Acc) when Emp#employee.salary < 10 -> + [Emp | Acc]; + (_, Acc) -> + Acc + end, + Find = fun() -> mnesia:foldl(Constraint, [], employee) end, + mnesia:transaction(Find). + ]]></code> + <p>Raising the salary to 10 for everyone with a salary below 10 + and return the sum of all raises:</p> + <code type="none"><![CDATA[ + increase_low_salaries() -> + Increase = + fun(Emp, Acc) when Emp#employee.salary < 10 -> + OldS = Emp#employee.salary, + ok = mnesia:write(Emp#employee{salary = 10}), + Acc + 10 - OldS; + (_, Acc) -> + Acc + end, + IncLow = fun() -> mnesia:foldl(Increase, 0, employee, write) end, + mnesia:transaction(IncLow). + ]]></code> + <p>A lot of nice things can be done with the iterator functions + but some caution should be taken about performance and memory + utilization for large tables. </p> + <p>Call these iteration functions on nodes that contain a replica of the + table. Each call to the function <c>Fun</c> access the table and if the table + resides on another node it will generate a lot of unnecessary + network traffic. </p> + <p>Mnesia also provides some functions that make it possible for + the user to iterate over the table. The order of the + iteration is unspecified if the table is not of the <c>ordered_set</c> + type. </p> + <code type="none"> + mnesia:first(Tab) -> Key | transaction abort + mnesia:last(Tab) -> Key | transaction abort + mnesia:next(Tab,Key) -> Key | transaction abort + mnesia:prev(Tab,Key) -> Key | transaction abort + mnesia:snmp_get_next_index(Tab,Index) -> {ok, NextIndex} | endOfTable + </code> + <p>The order of first/last and next/prev are only valid for + <c>ordered_set</c> tables, for all other tables, they are synonyms. + When the end of the table is reached the special key + <c>'$end_of_table'</c> is returned.</p> + <p>If records are written and deleted during the traversal, use + <c>mnesia:fold[lr]/4</c> with a <c>write</c> lock. Or + <c>mnesia:write_lock_table/1</c> when using first and next.</p> + <p>Writing or deleting in transaction context creates a local copy + of each modified record, so modifying each record in a large + table uses a lot of memory. Mnesia will compensate for every + written or deleted record during the iteration in a transaction + context, which may reduce the performance. If possible avoid writing + or deleting records in the same transaction before iterating over the + table.</p> + <p>In dirty context, i.e. <c>sync_dirty</c> or <c>async_dirty</c>, + the modified records are not stored in a local copy; instead, + each record is updated separately. This generates a lot of + network traffic if the table has a replica on another node and + has all the other drawbacks that dirty operations + have. Especially for the <c>mnesia:first/1</c> and + <c>mnesia:next/2</c> commands, the same drawbacks as described + above for <c>dirty_first</c> and <c>dirty_next</c> applies, i.e. + no writes to the table should be done during iteration.</p> + <p></p> + </section> +</chapter> + diff --git a/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc new file mode 100644 index 0000000000..3ec0aa37f5 --- /dev/null +++ b/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc @@ -0,0 +1,1398 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Miscellaneous Mnesia Features</title> + <prepared>Claes Wikström, Hans Nilsson and Håkan Mattsson</prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date></date> + <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> + <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> + </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> + <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> + </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> + <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> + <item><c>mnesia:index_match_object(Pattern, AttributeName) -> transaction abort | RecordList</c> + 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. + </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 + 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> + </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 + 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> + <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> + <item>The database can be reconfigured, and tables can be + moved between nodes. These operations do not effect 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>Table attributes are specified when the table is created. For + example, the following function will create a new table with two + RAM replicas: + </p> + <pre> + mnesia:create_table(foo, + [{ram_copies, [N1, N2]}, + {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> + <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 + 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> + <item>The table replicas can be backed up; either from + RAM, or from disc if dumped there with the above + 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 + 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 + 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 + 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>There are basically two reasons for using more than one table + replica: fault tolerance, or speed. It is worthwhile to note + 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 + important property in many applications. Furthermore, if a table + replica exists at two specific nodes, applications which 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> + <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 + 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> + </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 + 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> + <code type="none"><![CDATA[ +Eshell V4.7.3.3 (abort with ^G) +(a@sam)1> mnesia:start(). +ok +(a@sam)2> mnesia:system_info(running_db_nodes). +[b@sam,c@sam,a@sam] +(a@sam)3> Tab = dictionary. +dictionary +(a@sam)4> mnesia:create_table(Tab, [{ram_copies, [a@sam, b@sam]}]). +{atomic,ok} +(a@sam)5> Write = fun(Keys) -> [mnesia:write({Tab,K,-K}) || K <- Keys], ok end. +#Fun<erl_eval> +(a@sam)6> mnesia:activity(sync_dirty, Write, [lists:seq(1, 256)], mnesia_frag). +ok +(a@sam)7> mnesia:change_table_frag(Tab, {activate, []}). +{atomic,ok} +(a@sam)8> mnesia:table_info(Tab, frag_properties). +[{base_table,dictionary}, + {foreign_key,undefined}, + {n_doubles,0}, + {n_fragments,1}, + {next_n_to_split,1}, + {node_pool,[a@sam,b@sam,c@sam]}] +(a@sam)9> Info = fun(Item) -> mnesia:table_info(Tab, Item) end. +#Fun<erl_eval> +(a@sam)10> Dist = mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag). +[{c@sam,0},{a@sam,1},{b@sam,1}] +(a@sam)11> mnesia:change_table_frag(Tab, {add_frag, Dist}). +{atomic,ok} +(a@sam)12> Dist2 = mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag). +[{b@sam,1},{c@sam,1},{a@sam,2}] +(a@sam)13> mnesia:change_table_frag(Tab, {add_frag, Dist2}). +{atomic,ok} +(a@sam)14> Dist3 = mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag). +[{a@sam,2},{b@sam,2},{c@sam,2}] +(a@sam)15> mnesia:change_table_frag(Tab, {add_frag, Dist3}). +{atomic,ok} +(a@sam)16> Read = fun(Key) -> mnesia:read({Tab, Key}) end. +#Fun<erl_eval> +(a@sam)17> mnesia:activity(transaction, Read, [12], mnesia_frag). +[{dictionary,12,-12}] +(a@sam)18> mnesia:activity(sync_dirty, Info, [frag_size], mnesia_frag). +[{dictionary,64}, + {dictionary_frag2,64}, + {dictionary_frag3,64}, + {dictionary_frag4,64}] +(a@sam)19> + ]]></code> + </section> + + <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> + <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 + 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> + </item> + <tag><c>{node_pool, List}</c></tag> + <item> + <p>The node pool contains a list of nodes and may + 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 + the replicas of each fragment evenly over all the nodes in + the node pool. Hopefully all nodes will 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> + </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 + <c>0</c>, but if <c>n_disc_copies</c> and + <c>n_disc_only_copies</c> also are <c>0</c>, + <c>n_ram_copies</c>\011will default be set to <c>1</c>. + </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> + </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> + </item> + <tag><c>{foreign_key, ForeignKey}</c></tag> + <item> + <p><c>ForeignKey</c> may either be the atom + <c>undefined</c> or the tuple <c>{ForeignTab, Attr}</c>, + where <c>Attr</c> denotes an attribute which should be + interpreted as a key in another fragmented table named + <c>ForeignTab</c>. Mnesia will ensure 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 + 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 + other fragmentation properties to be the same values as + 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> + </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> + <code type="none"><![CDATA[ +Eshell V4.7.3.3 (abort with ^G) +(a@sam)1> mnesia:start(). +ok +(a@sam)2> PrimProps = [{n_fragments, 7}, {node_pool, [node()]}]. +[{n_fragments,7},{node_pool,[a@sam]}] +(a@sam)3> mnesia:create_table(prim_dict, + [{frag_properties, PrimProps}, + {attributes,[prim_key,prim_val]}]). +{atomic,ok} +(a@sam)4> SecProps = [{foreign_key, {prim_dict, sec_val}}]. +[{foreign_key,{prim_dict,sec_val}}] +(a@sam)5> mnesia:create_table(sec_dict, +\011 [{frag_properties, SecProps}, +(a@sam)5> {attributes, [sec_key, sec_val]}]). +{atomic,ok} +(a@sam)6> Write = fun(Rec) -> mnesia:write(Rec) end. +#Fun<erl_eval> +(a@sam)7> PrimKey = 11. +11 +(a@sam)8> SecKey = 42. +42 +(a@sam)9> mnesia:activity(sync_dirty, Write, +\011\011 [{prim_dict, PrimKey, -11}], mnesia_frag). +ok +(a@sam)10> mnesia:activity(sync_dirty, Write, +\011\011 [{sec_dict, SecKey, PrimKey}], mnesia_frag). +ok +(a@sam)11> mnesia:change_table_frag(prim_dict, {add_frag, [node()]}). +{atomic,ok} +(a@sam)12> SecRead = fun(PrimKey, SecKey) -> +\011\011 mnesia:read({sec_dict, PrimKey}, SecKey, read) end. +#Fun<erl_eval> +(a@sam)13> mnesia:activity(transaction, SecRead, +\011\011 [PrimKey, SecKey], mnesia_frag). +[{sec_dict,42,11}] +(a@sam)14> Info = fun(Tab, Item) -> mnesia:table_info(Tab, Item) end. +#Fun<erl_eval> +(a@sam)15> mnesia:activity(sync_dirty, Info, +\011\011 [prim_dict, frag_size], mnesia_frag). +[{prim_dict,0}, + {prim_dict_frag2,0}, + {prim_dict_frag3,0}, + {prim_dict_frag4,1}, + {prim_dict_frag5,0}, + {prim_dict_frag6,0}, + {prim_dict_frag7,0}, + {prim_dict_frag8,0}] +(a@sam)16> mnesia:activity(sync_dirty, Info, +\011\011 [sec_dict, frag_size], mnesia_frag). +[{sec_dict,0}, + {sec_dict_frag2,0}, + {sec_dict_frag3,0}, + {sec_dict_frag4,1}, + {sec_dict_frag5,0}, + {sec_dict_frag6,0}, + {sec_dict_frag7,0}, + {sec_dict_frag8,0}] +(a@sam)17> + ]]></code> + </item> + </taglist> + </section> + + <section> + <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> + <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> + </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> + </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 + 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> + 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> + </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 + dynamically rehashed in the same manner as for the main + 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> + </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> + </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> + <taglist> + <tag><c>base_table</c></tag> + <item> + <p>the name of the fragmented table + </p> + </item> + <tag><c>n_fragments</c></tag> + <item> + <p>the actual number of fragments + </p> + </item> + <tag><c>node_pool</c></tag> + <item> + <p>the pool of nodes + </p> + </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> + 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 + <c>n_ram_copies</c>, <c>n_disc_copies</c>, and + <c>n_disc_only_copies</c>. + </p> + </item> + <tag><c>foreign_key</c></tag> + <item> + <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> + </item> + <tag><c>frag_names</c></tag> + <item> + <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 + <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> + </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> + </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> + </item> + <tag><c>size</c></tag> + <item> + <p>total size of all fragments + </p> + </item> + <tag><c>memory</c></tag> + <item> + <p>the total memory of all fragments</p> + </item> + </taglist> + </section> + + <section> + <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 + 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 + 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> + </section> + </section> + + <section> + <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> + </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 + 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> + <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> + <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> + </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> + </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> + </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> + <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> + </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 + 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 + 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 + 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, + 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 + 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> + </section> + + <section> + <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> + <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> + <item>Removes the subscription on events of type + <c>Event-Category</c></item> + </taglist> + <p><c>Event-Category</c> may either be the atom <c>system</c>, or + one of the tuples <c>{table, Tab, simple}</c>, <c>{table, Tab, detailed}</c>. The old event-category <c>{table, Tab}</c> is the same + event-category as <c>{table, Tab, simple}</c>. + The subscribe functions activate a subscription + of events. The events are delivered as messages to the process + evaluating the <c>mnesia:subscribe/1</c> function. The syntax of + system events is <c>{mnesia_system_event, Event}</c> and + <c>{mnesia_table_event, Event}</c> for table events. What system + events and table events means is described below. + </p> + <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> + + <section> + <title>System Events</title> + <p>The system events are detailed below:</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> + <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> + <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> + <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> + <tag><c>{mnesia_overload, Details}</c></tag> + <item> + <p>Mnesia on the current node is + overloaded and the subscriber should 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 + 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 + 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> + <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 + 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> + </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> + <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> + </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> + <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> + <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> + </taglist> + </section> + + <section> + <title>Table Events</title> + <p>Another 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> + <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> + <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> + <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> + </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> + <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> + <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> + </taglist> + </section> + </section> + + <section> + <title>Debugging Mnesia Applications</title> + <p>Debugging a Mnesia application can be difficult due to a number of 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> + <taglist> + <tag><c>none</c></tag> + <item> + <p>no trace outputs at all. This is the default. + </p> + </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> + <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> + <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> + <tag><c>false</c></tag> + <item> + <p>is an alias for none.</p> + </item> + <tag><c>true</c></tag> + <item> + <p>is an alias for debug.</p> + </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> + <pre> + % erl -mnesia debug verbose + </pre> + </section> + + <section> + <title>Concurrent Processes in Mnesia</title> + <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 + 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 + 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> + </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 + 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> + <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> + </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> + </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> + <pre> + {tables, [{Typename, [Options]}, + {Typename2 ......}]}. + + {Typename, Attribute1, Atrribute2 ....}. + {Typename, Attribute1, Atrribute2 ....}. + </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> + <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> + <pre><![CDATA[ + % erl + Erlang (BEAM) emulator version 4.9 + + Eshell V4.9 (abort with ^G) + 1> mnesia:load_textfile("FRUITS"). + New table fruit + New table vegetable + {atomic,ok} + 2> mnesia:info(). + ---> Processes holding locks <--- + ---> Processes waiting for locks <--- + ---> Pending (remote) transactions <--- + ---> Active (local) transactions <--- + ---> Uncertain transactions <--- + ---> Active tables <--- + vegetable : with 2 records occuping 299 words of mem + fruit : with 2 records occuping 291 words of mem + schema : with 3 records occuping 401 words of mem + ===> System info in version "1.1", debug level = none <=== + opt_disc. Directory "/var/tmp/Mnesia.nonode@nohost" is used. + use fallback at restart = false + running db nodes = [nonode@nohost] + stopped db nodes = [] + remote = [] + ram_copies = [fruit,vegetable] + disc_copies = [schema] + disc_only_copies = [] + [{nonode@nohost,disc_copies}] = [schema] + [{nonode@nohost,ram_copies}] = [fruit,vegetable] + 3 transactions committed, 0 aborted, 0 restarted, 2 logged to disc + 0 held locks, 0 in queue; 0 local transactions, 0 remote + 0 transactions waits for other nodes: [] + ok + 3> + ]]></pre> + <p>Where we can see 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 + 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> + <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> + <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> + <codeinclude file="company_o.hrl" tag="%0" type="erl"></codeinclude> + <p>An record which describes an employee might look like this: + </p> + <pre> + Me = #employee{emp_no= 104732, + name = klacke, + salary = 7, + sex = male, + phone = 99586, + 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> + <list type="bulleted"> + <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> + <item><c>114872</c>. This 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>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 + 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> + <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> + <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> + </section> +</chapter> + diff --git a/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc new file mode 100644 index 0000000000..7078499fbf --- /dev/null +++ b/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc @@ -0,0 +1,890 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Mnesia System Information</title> + <prepared>Claes Wikström, Hans Nilsson and Håkan Mattsson</prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date></date> + <rev></rev> + <file>Mnesia_chap7.xml</file> + </header> + + <section> + <title>Database Configuration Data</title> + <p>The following two functions can be used to retrieve system + information. They are described in detail in the reference manual. + </p> + <list type="bulleted"> + <item><c>mnesia:table_info(Tab, Key) -></c><c>Info | exit({aborted, Reason})</c>. + Returns information about one table. Such as the + current size of the table, on which nodes it resides etc. + </item> + <item><c>mnesia:system_info(Key) -> </c><c>Info | exit({aborted, Reason})</c>. + Returns information about the Mnesia system. For example, transaction + statistics, db_nodes, configuration parameters etc. + </item> + </list> + </section> + + <section> + <title>Core Dumps</title> + <p>If Mnesia malfunctions, system information is dumped to a file + named <c>MnesiaCore.Node.When</c>. The type of system + information contained in this file can also be generated with + the function <c>mnesia_lib:coredump()</c>. If a Mnesia system + behaves strangely, it is recommended that a Mnesia core dump + file be included in the bug report.</p> + </section> + + <section> + <title>Dumping Tables</title> + <p>Tables of type <c>ram_copies</c> are by definition stored in + memory only. It is possible, however, to dump these tables to + disc, either at regular intervals, or before the system is + shutdown. The function <c>mnesia:dump_tables(TabList)</c> dumps + all replicas of a set of RAM tables to disc. The tables can be + accessed while being dumped to disc. To dump the tables to + disc all replicas must have the storage type <c>ram_copies</c>. + </p> + <p>The table content is placed in a .DCD file on the + disc. When the Mnesia system is started, the RAM table will + initially be loaded with data from its .DCD file. + </p> + </section> + + <section> + <marker id="checkpoints"></marker> + <title>Checkpoints</title> + <p>A checkpoint is a transaction consistent state that spans over + one or more tables. When a checkpoint is activated, the system + will remember the current content of the set of tables. The + checkpoint retains a transaction consistent state of the tables, + allowing the tables to be read and updated while the checkpoint + is active. A checkpoint is typically used to + back up tables to external media, but they are also used + internally in Mnesia for other purposes. Each checkpoint is + independent and a table may be involved in several checkpoints + simultaneously. + </p> + <p>Each table retains its old contents in a checkpoint retainer + and for performance critical applications, it may be important + to realize the processing overhead associated with checkpoints. + In a worst case scenario, the checkpoint retainer will consume + even more memory than the table itself. Each update will also be + slightly slower on those nodes where checkpoint + retainers are attached to the tables. + </p> + <p>For each table it is possible to choose if there should be one + checkpoint retainer attached to all replicas of the table, or if + it is enough to have only one checkpoint retainer attached to a + single replica. With a single checkpoint retainer per table, the + checkpoint will consume less memory, but it will be vulnerable + to node crashes. With several redundant checkpoint retainers the + checkpoint will survive as long as there is at least one active + checkpoint retainer attached to each table. + </p> + <p>Checkpoints may be explicitly deactivated with the function + <c>mnesia:deactivate_checkpoint(Name)</c>, where <c>Name</c> is + the name of an active checkpoint. This function returns + <c>ok</c> if successful, or <c>{error, Reason}</c> in the case + of an error. All tables in a checkpoint must be attached to at + least one checkpoint retainer. The checkpoint is automatically + de-activated by Mnesia, when any table lacks a checkpoint + retainer. This may happen when a node goes down or when a + replica is deleted. Use the <c>min</c> and + <c>max</c> arguments described below, to control the degree of + checkpoint retainer redundancy. + </p> + <p>Checkpoints are activated with the function <marker id="mnesia:chkpt(Args)"></marker> +<c>mnesia:activate_checkpoint(Args)</c>, + where <c>Args</c> is a list of the following tuples: + </p> + <list type="bulleted"> + <item><c>{name,Name}</c>. <c>Name</c> specifies a temporary name + of the checkpoint. The name may be re-used when the checkpoint + has been de-activated. If no name is specified, a name is + generated automatically. + </item> + <item><c>{max,MaxTabs}</c>. <c>MaxTabs</c> is a list of tables + which will be included in the checkpoint. The default is + <c>[]</c> (an empty list). For these tables, the redundancy + will be maximized. The old contents of the table will be + retained in the checkpoint retainer when the main table is + updated by the applications. The checkpoint becomes more fault + tolerant if the tables have several replicas. When new + replicas are added by means of the schema manipulation + function <c>mnesia:add_table_copy/3</c>, it will also + attach a local checkpoint retainer. + </item> + <item><c>{min,MinTabs}</c>. <c>MinTabs</c> is a list of tables + that should be included in the checkpoint. The default is + <c>[]</c>. For these tables, the redundancy will be minimized, + and there will be a single checkpoint retainer per table, + preferably at the local node. + </item> + <item><c>{allow_remote,Bool}</c>. <c>false</c> means that all + checkpoint retainers must be local. If a table does not reside + locally, the checkpoint cannot be activated. <c>true</c> + allows checkpoint retainers to be allocated on any node. The + defaults is <c>true</c>. + </item> + <item><c>{ram_overrides_dump,Bool}</c>. This argument only + applies to tables of type <c>ram_copies</c>. <c>Bool</c> + specifies if the table state in RAM should override the table + state on disc. <c>true</c> means that the latest committed + records in RAM are included in the checkpoint retainer. These + are the records that the application accesses. <c>false</c> + means that the records on the disc .DAT file are + included in the checkpoint retainer. These are the records + that will be loaded on start-up. Default is <c>false</c>.</item> + </list> + <p>The <c>mnesia:activate_checkpoint(Args)</c> returns one of the + following values: + </p> + <list type="bulleted"> + <item><c>{ok, Name, Nodes}</c></item> + <item><c>{error, Reason}</c>.</item> + </list> + <p><c>Name</c> is the name of the checkpoint, and <c>Nodes</c> are + the nodes where the checkpoint is known. + </p> + <p>A list of active checkpoints can be obtained with the following + functions: + </p> + <list type="bulleted"> + <item><c>mnesia:system_info(checkpoints)</c>. This function + returns all active checkpoints on the current node.</item> + <item><c>mnesia:table_info(Tab,checkpoints)</c>. This function + returns active checkpoints on a specific table.</item> + </list> + </section> + + <section> + <title>Files</title> + <p>This section describes the internal files which are created and maintained by the Mnesia system, + in particular, the workings of the Mnesia log is described. + </p> + + <section> + <title>Start-Up Files</title> + </section> + <p>In Chapter 3 we detailed the following pre-requisites for + starting Mnesia (refer Chapter 3: <seealso marker="Mnesia_chap3#start_mnesia">Starting Mnesia</seealso>: + </p> + <list type="bulleted"> + <item>We must start an Erlang session and specify a Mnesia + directory for our database. + </item> + <item>We must initiate a database schema, using the function + <c>mnesia:create_schema/1</c>. + </item> + </list> + <p>The following example shows how these tasks are performed: + </p> + <list type="ordered"> + <item> + <pre> +% <input>erl -sname klacke -mnesia dir '"/ldisc/scratch/klacke"'</input> </pre> + </item> + <item> + <pre> +Erlang (BEAM) emulator version 4.9 + +Eshell V4.9 (abort with ^G) +(klacke@gin)1> <input>mnesia:create_schema([node()]).</input> +ok +(klacke@gin)2> +<input>^Z</input> +Suspended </pre> + <p>We can inspect the Mnesia directory to see what files have been created. Enter the following command: + </p> + <pre> +% <input>ls -l /ldisc/scratch/klacke</input> +-rw-rw-r-- 1 klacke staff 247 Aug 12 15:06 FALLBACK.BUP </pre> + <p>The response shows that the file FALLBACK.BUP has been created. This is called a backup file, and it contains an initial schema. If we had specified more than one node in the <c>mnesia:create_schema/1</c> function, identical backup files would have been created on all nodes. + </p> + </item> + <item> + <p>Continue by starting Mnesia:</p> + <pre> +(klacke@gin)3><input>mnesia:start( ).</input> +ok </pre> + <p>We can now see the following listing in the Mnesia directory: + </p> + <pre> +-rw-rw-r-- 1 klacke staff 86 May 26 19:03 LATEST.LOG +-rw-rw-r-- 1 klacke staff 34507 May 26 19:03 schema.DAT </pre> + <p>The schema in the backup file FALLBACK.BUP has been used to generate the file <c>schema.DAT.</c> Since we have no other disc resident tables than the schema, no other data files were created. The file FALLBACK.BUP was removed after the successful "restoration". We also see a number of files that are for internal use by Mnesia. + </p> + </item> + <item> + <p>Enter the following command to create a table:</p> + <pre> +(klacke@gin)4> <input>mnesia:create_table(foo,[{disc_copies, [node()]}]).</input> +{atomic,ok} </pre> + <p>We can now see the following listing in the Mnesia directory: + </p> + <pre> +% <input>ls -l /ldisc/scratch/klacke</input> +-rw-rw-r-- 1 klacke staff 86 May 26 19:07 LATEST.LOG +-rw-rw-r-- 1 klacke staff 94 May 26 19:07 foo.DCD +-rw-rw-r-- 1 klacke staff 6679 May 26 19:07 schema.DAT </pre> + <p>Where a file <c>foo.DCD</c> has been created. This file will eventually store + all data that is written into the <c>foo</c> table.</p> + </item> + </list> + + <section> + <title>The Log File</title> + <p>When starting Mnesia, a .LOG file called <c>LATEST.LOG</c> + was created and placed in the database directory. This file is + used by Mnesia to log disc based transactions. This includes all + transactions that write at least one record in a table which is + of storage type <c>disc_copies</c>, or + <c>disc_only_copies</c>. It also includes all operations which + manipulate the schema itself, such as creating new tables. The + format of the log can vary with different implementations of + Mnesia. The Mnesia log is currently implemented with the + standard library module <c>disc_log</c>. + </p> + <p>The log file will grow continuously and must be dumped at + regular intervals. "Dumping the log file" means that Mnesia will + perform all the operations listed in the log and place the + records in the corresponding .DAT, .DCD and .DCL data files. For + example, if the operation "write record <c>{foo, 4, elvis, 6}</c>" + is listed in the log, Mnesia inserts the operation into the + file <c>foo.DCL</c>, later when Mnesia thinks the .DCL has become to large + the data is moved to the .DCD file. + The dumping operation can be time consuming + if the log is very large. However, it is important to realize + that the Mnesia system continues to operate during log dumps. + </p> + <p>By default Mnesia either dumps the log whenever 100 records have + been written in the log or when 3 minutes have passed. + This is controlled by the two application parameters + <c>-mnesia dump_log_write_threshold WriteOperations</c> and + <c>-mnesia dump_log_time_threshold MilliSecs</c>. + </p> + <p>Before the log is dumped, the file <c>LATEST.LOG</c> is + renamed to <c>PREVIOUS.LOG</c>, and a new <c>LATEST.LOG</c> file + is created. Once the log has been successfully dumped, the file + <c>PREVIOUS.LOG</c> is deleted. + </p> + <p>The log is also dumped at start-up and whenever a schema + operation is performed. + </p> + </section> + + <section> + <title>The Data Files</title> + <p>The directory listing also contains one .DAT file. This contain + the schema itself, contained in the <c>schema.DAT</c> + file. The DAT files are indexed files, and it is efficient to + insert and search for records in these files with a specific + key. The .DAT files are used for the schema and for <c>disc_only_copies</c> + tables. The Mnesia data files are currently implemented with the + standard library module <c>dets</c>, and all operations which + can be performed on <c>dets</c> files can also be performed on + the Mnesia data files. For example, <c>dets</c> contains a + function <c>dets:traverse/2</c> which can be used to view the + contents of a Mnesia DAT file. However, this can only be done + when Mnesia is not running. So, to view a our schema file, we + can: </p> + <pre> +{ok, N} = dets:open_file(schema, [{file, "./schema.DAT"},{repair,false}, +{keypos, 2}]), +F = fun(X) -> io:format("~p~n", [X]), continue end, +dets:traverse(N, F), +dets:close(N). </pre> + <note> + <p>Refer to the Reference Manual, <c>std_lib</c> for information about <c>dets</c>.</p> + </note> + <warning> + <p>The DAT files must always be opened with the <c>{repair, false}</c> + option. This ensures that these files are not + automatically repaired. Without this option, the database may + become inconsistent, because Mnesia may + believe that the files were properly closed. Refer to the reference + manual for information about the configuration parameter + <c>auto_repair</c>.</p> + </warning> + <warning> + <p>It is recommended that Data files are not tampered with while Mnesia is + running. While not prohibited, the behavior of Mnesia is unpredictable. </p> + </warning> + <p>The <c>disc_copies</c> tables are stored on disk with .DCL and .DCD files, + which are standard disk_log files. + </p> + </section> + </section> + + <section> + <title>Loading of Tables at Start-up</title> + <p>At start-up Mnesia loads tables in order to make them accessible + for its applications. Sometimes Mnesia decides to load all tables + that reside locally, and sometimes the tables may not be + accessible until Mnesia brings a copy of the table + from another node. + </p> + <p>To understand the behavior of Mnesia at start-up it is + essential to understand how Mnesia reacts when it loses contact + with Mnesia on another node. At this stage, Mnesia cannot distinguish + between a communication failure and a "normal" node down. <br></br> + + When this happens, Mnesia will assume that the other node is no longer running. + Whereas, in reality, the communication between the nodes has merely failed. + </p> + <p>To overcome this situation, simply try to restart the ongoing transactions that are + accessing tables on the failing node, and write a <c>mnesia_down</c> entry to a log file. + </p> + <p>At start-up, it must be noted that all tables residing on nodes + without a <c>mnesia_down</c> entry, may have fresher replicas. + Their replicas may have been updated after the termination + of Mnesia on the current node. In order to catch up with the latest + updates, transfer a copy of the table from one of these other + "fresh" nodes. If you are unlucky, other nodes may be down + and you must wait for the table to be + loaded on one of these nodes before receiving a fresh copy of + the table. + </p> + <p>Before an application makes its first access to a table, + <c>mnesia:wait_for_tables(TabList, Timeout)</c> ought to be executed + to ensure that the table is accessible from the local node. If + the function times out the application may choose to force a + load of the local replica with + <c>mnesia:force_load_table(Tab)</c> and deliberately lose all + updates that may have been performed on the other nodes while + the local node was down. If + Mnesia already has loaded the table on another node or intends + to do so, we will copy the table from that node in order to + avoid unnecessary inconsistency. + </p> + <warning> + <p>Keep in mind that it is only + one table that is loaded by <c>mnesia:force_load_table(Tab)</c> + and since committed transactions may have caused updates in + several tables, the tables may now become inconsistent due to + the forced load.</p> + </warning> + <p>The allowed <c>AccessMode</c> of a table may be defined to + either be <c>read_only</c> or <c>read_write</c>. And it may be + toggled with the function <c>mnesia:change_table_access_mode(Tab, AccessMode)</c> in runtime. <c>read_only</c> tables and + <c>local_content</c> tables will always be loaded locally, since + there are no need for copying the table from other nodes. Other + tables will primary be loaded remotely from active replicas on + other nodes if the table already has been loaded there, or if + the running Mnesia already has decided to load the table there. + </p> + <p>At start up, Mnesia will assume that its local replica is the + most recent version and load the table from disc if either + situation is detected: + </p> + <list type="bulleted"> + <item><c>mnesia_down</c> is returned from all other nodes that holds a disc + resident replica of the table; or,</item> + <item>if all replicas are <c>ram_copies</c></item> + </list> + <p>This is normally a wise decision, but it may turn out to + be disastrous if the nodes have been disconnected due to a + communication failure, since Mnesia's normal table load + mechanism does not cope with communication failures. + </p> + <p>When Mnesia is loading many tables the default load + order. However, it is possible to + affect the load order by explicitly changing the + <c>load_order</c> property for the tables, with the function + <c>mnesia:change_table_load_order(Tab, LoadOrder)</c>. The + <c>LoadOrder</c> is by default <c>0</c> for all tables, but it + can be set to any integer. The table with the highest + <c>load_order</c> will be loaded first. Changing load order is + especially useful for applications that need to ensure early + availability of fundamental tables. Large peripheral + tables should have a low load order value, perhaps set + below 0. + </p> + </section> + + <section> + <title>Recovery from Communication Failure</title> + <p>There are several occasions when Mnesia may detect that the + network has been partitioned due to a communication failure. + </p> + <p>One is when Mnesia already is up and running and the Erlang + nodes gain contact again. Then Mnesia will try to contact Mnesia + on the other node to see if it also thinks that the network has + been partitioned for a while. If Mnesia on both nodes has logged + <c>mnesia_down</c> entries from each other, Mnesia generates a + system event, called <c>{inconsistent_database, running_partitioned_network, Node}</c> which is sent to Mnesia's + event handler and other possible subscribers. The default event + handler reports an error to the error logger. + </p> + <p>Another occasion when Mnesia may detect that the network has + been partitioned due to a communication failure, is at start-up. + If Mnesia detects that both the local node and another node received + <c>mnesia_down</c> from each other it generates a + <c>{inconsistent_database, starting_partitioned_network, Node}</c> system event and acts as described above. + </p> + <p>If the application detects that there has been a communication + failure which may have caused an inconsistent database, it may + use the function <c>mnesia:set_master_nodes(Tab, Nodes)</c> to + pinpoint from which nodes each table may be loaded.</p> + <p>At start-up Mnesia's normal table load algorithm will be + bypassed and the table will be loaded from one of the master + nodes defined for the table, regardless of potential + <c>mnesia_down</c> entries in the log. The <c>Nodes</c> may only + contain nodes where the table has a replica and if it is empty, + the master node recovery mechanism for the particular table will + be reset and the normal load mechanism will be used when next + restarting. + </p> + <p>The function <c>mnesia:set_master_nodes(Nodes)</c> sets master + nodes for all tables. For each table it will determine its + replica nodes and invoke <c>mnesia:set_master_nodes(Tab, TabNodes)</c> with those replica nodes that are included in the + <c>Nodes</c> list (i.e. <c>TabNodes</c> is the intersection of + <c>Nodes</c> and the replica nodes of the table). If the + intersection is empty the master node recovery mechanism for the + particular table will be reset and the normal load mechanism + will be used at next restart. + </p> + <p>The functions <c>mnesia:system_info(master_node_tables)</c> and + <c>mnesia:table_info(Tab, master_nodes)</c> may be used to + obtain information about the potential master nodes. + </p> + <p>The function <c>mnesia:force_load_table(Tab)</c> may be used to + force load the table regardless of which table load mechanism + is activated. + </p> + </section> + + <section> + <title>Recovery of Transactions</title> + <p>A Mnesia table may reside on one or more nodes. When a table is + updated, Mnesia will ensure that the updates will be replicated + to all nodes where the table resides. If a replica happens to be + inaccessible for some reason (e.g. due to a temporary node down), + Mnesia will then perform the replication later. + </p> + <p>On the node where the application is started, there will be a + transaction coordinator process. If the transaction is + distributed, there will also be a transaction participant process on + all the other nodes where commit work needs to be performed. + </p> + <p>Internally Mnesia uses several commit protocols. The selected + protocol depends on which table that has been updated in + the transaction. If all the involved tables are symmetrically + replicated, (i.e. they all have the same <c>ram_nodes</c>, + <c>disc_nodes</c> and <c>disc_only_nodes</c> currently + accessible from the coordinator node), a lightweight transaction + commit protocol is used. + </p> + <p>The number of messages that the + transaction coordinator and its participants needs to exchange + is few, since Mnesia's table load mechanism takes care of the + transaction recovery if the commit protocol gets + interrupted. Since all involved tables are replicated + symmetrically the transaction will automatically be recovered by + loading the involved tables from the same node at start-up of a + failing node. We do not really care if the transaction was + aborted or committed as long as we can ensure the ACID + properties. The lightweight commit protocol is non-blocking, + i.e. the surviving participants and their coordinator will + finish the transaction, regardless of some node crashes in the + middle of the commit protocol or not. + </p> + <p>If a node goes down in the middle of a dirty operation the + table load mechanism will ensure that the update will be + performed on all replicas or none. Both asynchronous dirty + updates and synchronous dirty updates use the same recovery + principle as lightweight transactions. + </p> + <p>If a transaction involves updates of asymmetrically replicated + tables or updates of the schema table, a heavyweight commit + protocol will be used. The heavyweight commit protocol is able + to finish the transaction regardless of how the tables are + replicated. The typical usage of a heavyweight transaction is + when we want to move a replica from one node to another. Then we + must ensure that the replica either is entirely moved or left as + it was. We must never end up in a situation with replicas on both + nodes or no node at all. Even if a node crashes in the middle of + the commit protocol, the transaction must be guaranteed to be + atomic. The heavyweight commit protocol involves more messages + between the transaction coordinator and its participants than + a lightweight protocol and it will perform recovery work at + start-up in order to finish the abort or commit work. + </p> + <p>The heavyweight commit protocol is also non-blocking, + which allows the surviving participants and their coordinator to + finish the transaction regardless (even if a node crashes in the + middle of the commit protocol). When a node fails at start-up, + Mnesia will determine the outcome of the transaction and + recover it. Lightweight protocols, heavyweight protocols and dirty updates, are + dependent on other nodes to be up and running in order to make the + correct heavyweight transaction recovery decision. + </p> + <p>If Mnesia has not started on some of the nodes that are involved in the + transaction AND neither the local node or any of the already + running nodes know the outcome of the transaction, Mnesia will + by default wait for one. In the worst case scenario all other + involved nodes must start before Mnesia can make the correct decision + about the transaction and finish its start-up. + </p> + <p>This means that Mnesia (on one node)may hang if a double fault occurs, i.e. when two nodes crash simultaneously + and one attempts to start when the other refuses to + start e.g. due to a hardware error. + </p> + <p>It is possible to specify the maximum time that Mnesia + will wait for other nodes to respond with a transaction + recovery decision. The configuration parameter + <c>max_wait_for_decision</c> defaults to infinity (which may + cause the indefinite hanging as mentioned above) but if it is + set to a definite time period (eg.three minutes), Mnesia will then enforce a + transaction recovery decision if needed, in order to allow + Mnesia to continue with its start-up procedure. </p> + <p>The downside of an enforced transaction recovery decision, is that the decision may be + incorrect, due to insufficient information regarding the other nodes' + recovery decisions. This may result in an + inconsistent database where Mnesia has committed the transaction + on some nodes but aborted it on others. </p> + <p>In fortunate cases the inconsistency will only appear in tables belonging to a specific + application, but if a schema transaction has been inconsistently + recovered due to the enforced transaction recovery decision, the + effects of the inconsistency can be fatal. + However, if the higher priority is availability rather than + consistency, then it may be worth the risk. </p> + <p>If Mnesia + encounters a inconsistent transaction decision a + <c>{inconsistent_database, bad_decision, Node}</c> system event + will be generated in order to give the application a chance to + install a fallback or other appropriate measures to resolve the inconsistency. The default + behavior of the Mnesia event handler is the same as if the + database became inconsistent as a result of partitioned network (see + above). + </p> + </section> + + <section> + <title>Backup, Fallback, and Disaster Recovery</title> + <p>The following functions are used to backup data, to install a + backup as fallback, and for disaster recovery. + </p> + <list type="bulleted"> + <item><c>mnesia:backup_checkpoint(Name, Opaque, [Mod])</c>. This + function performs a backup of the tables included in the + checkpoint. + </item> + <item><c>mnesia:backup(Opaque, [Mod])</c>. This function + activates a new checkpoint which covers all Mnesia tables and + performs a backup. It is performed with maximum degree of + redundancy (also refer to the function <seealso marker="#checkpoints">mnesia:activate_checkpoint(Args)</seealso>, + <c>{max, MaxTabs} and {min, MinTabs}).</c></item> + <item><c>mnesia:traverse_backup(Source,[SourceMod,]</c><c>Target,[TargetMod,]Fun,Ac)</c>. This function can be used + to read an existing backup, create a new backup from an + existing one, or to copy a backup from one type media to + another. + </item> + <item><c>mnesia:uninstall_fallback()</c>. This function removes + previously installed fallback files. + </item> + <item><c>mnesia:restore(Opaque, Args)</c>. This function + restores a set of tables from a previous backup. + </item> + <item><c>mnesia:install_fallback(Opaque, [Mod])</c>. This + function can be configured to restart the Mnesia and reload data + tables, and possibly schema tables, from an existing + backup. This function is typically used for disaster recovery + purposes, when data or schema tables are corrupted.</item> + </list> + <p>These functions are explained in the following + sub-sections. Also refer to the the section <seealso marker="#checkpoints">Checkpoints</seealso> in this chapter, which + describes the two functions used to activate and de-activate + checkpoints. + </p> + + <section> + <title>Backup</title> + <p>Backup operation are performed with the following functions: + </p> + <list type="bulleted"> + <item><c>mnesia:backup_checkpoint(Name, Opaque, [Mod])</c></item> + <item><c>mnesia:backup(Opaque, [Mod])</c></item> + <item><c>mnesia:traverse_backup(Source, [SourceMod,],</c><c>Target,[TargetMod,]Fun,Acc)</c>.</item> + </list> + <p>By default, the actual access to the backup media is + performed via the <c>mnesia_backup</c> module for both read + and write. Currently <c>mnesia_backup</c> is implemented with + the standard library module <c>disc_log</c>, but it is possible to write + your own module with the same interface as + <c>mnesia_backup</c> and configure Mnesia so the alternate + module performs the actual accesses to the backup media. This + means that the user may put the backup on medias that Mnesia + does not know about, possibly on hosts where Erlang is not + running. Use the configuration parameter <c><![CDATA[-mnesia backup_module <module>]]></c> for this purpose. </p> + <p>The source + for a backup is an activated checkpoint. The backup function + most commonly used is <c>mnesia:backup_checkpoint(Name, Opaque,[Mod])</c>. This function returns either <c>ok</c>, or + <c>{error,Reason}</c>. It has the following arguments: + </p> + <list type="bulleted"> + <item><c>Name</c> is the name of an activated + checkpoint. Refer to the section <seealso marker="#checkpoints">Checkpoints</seealso> in this chapter, the + function <c>mnesia:activate_checkpoint(ArgList)</c> for + details on how to include table names in checkpoints. + </item> + <item><c>Opaque</c>. Mnesia does not interpret this argument, + but it is forwarded to the backup module. The Mnesia default + backup module, <c>mnesia_backup</c> interprets this argument + as a local file name. + </item> + <item><c>Mod</c>. The name of an alternate backup module. + </item> + </list> + <p>The function <c>mnesia:backup(Opaque[, Mod])</c> activates a + new checkpoint which covers all Mnesia tables with maximum + degree of redundancy and performs a backup. Maximum + redundancy means that each table replica has a checkpoint + retainer. Tables with the <c>local_contents</c> property are + backed up as they + look on the current node. + </p> + <p>It is possible to iterate over a backup, either for the + purpose of transforming it into a new backup, or just reading + it. The function <c>mnesia:traverse_backup(Source, [SourceMod,]</c><c>Target, [TargeMod,] Fun, Acc)</c> which normally returns <c>{ok, LastAcc}</c>, is used for both of these purposes. + </p> + <p>Before the traversal starts, the source backup media is + opened with <c>SourceMod:open_read(Source)</c>, and the target + backup media is opened with + <c>TargetMod:open_write(Target)</c>. The arguments are: + </p> + <list type="bulleted"> + <item><c>SourceMod</c> and <c>TargetMod</c> are module names. + </item> + <item><c>Source</c> and <c>Target</c> are opaque data used + exclusively by the modules <c>SourceMod</c> and + <c>TargetMod</c> for the purpose of initializing the backup + medias. + </item> + <item><c>Acc</c> is an initial accumulator value. + </item> + <item><c>Fun(BackupItems, Acc)</c> is applied to each item in + the backup. The Fun must return a tuple <c>{ValGoodBackupItems, NewAcc}</c>, where <c>ValidBackupItems</c> is a list of valid + backup items, and <c>NewAcc</c> is a new accumulator value. + The <c>ValidBackupItems</c> are written to the target backup + with the function <c>TargetMod:write/2</c>. + </item> + <item><c>LastAcc</c> is the last accumulator value. I.e. + the last <c>NewAcc</c> value that was returned by <c>Fun</c>. + </item> + </list> + <p>It is also possible to perform a read-only traversal of the + source backup without updating a target backup. If + <c>TargetMod==read_only</c>, then no target backup is accessed + at all. + </p> + <p>By setting <c>SourceMod</c> and <c>TargetMod</c> to different + modules it is possible to copy a backup from one kind of backup + media to another. + </p> + <p>Valid <c>BackupItems</c> are the following tuples: + </p> + <list type="bulleted"> + <item><c>{schema, Tab}</c> specifies a table to be deleted. + </item> + <item><c>{schema, Tab, CreateList}</c> specifies a table to be + created. See <c>mnesia_create_table/2</c> for more + information about <c>CreateList</c>. + </item> + <item><c>{Tab, Key}</c> specifies the full identity of a record + to be deleted. + </item> + <item><c>{Record}</c> specifies a record to be inserted. It + can be a tuple with <c>Tab</c> as first field. Note that the + record name is set to the table name regardless of what + <c>record_name</c> is set to. + </item> + </list> + <p>The backup data is divided into two sections. The first + section contains information related to the schema. All schema + related items are tuples where the first field equals the atom + schema. The second section is the record section. It is not + possible to mix schema records with other records and all schema + records must be located first in the backup. + </p> + <p>The schema itself is a table and will possibly be included in + the backup. All nodes where the schema table resides are + regarded as a <c>db_node</c>. + </p> + <p>The following example illustrates how + <c>mnesia:traverse_backup</c> can be used to rename a db_node in + a backup file: + </p> + <codeinclude file="bup.erl" tag="%0" type="erl"></codeinclude> + </section> + + <section> + <title>Restore</title> + <p>Tables can be restored on-line from a backup without + restarting Mnesia. A restore is performed with the function + <c>mnesia:restore(Opaque,Args)</c>, where <c>Args</c> can + contain the following tuples: + </p> + <list type="bulleted"> + <item><c>{module,Mod}</c>. The backup module <c>Mod</c> is + used to access the backup media. If omitted, the default + backup module will be used.</item> + <item><c>{skip_tables, TableList}</c> Where <c>TableList</c> + is a list of tables which should not be read from the backup.</item> + <item><c>{clear_tables, TableList}</c> Where <c>TableList</c> + is a list of tables which should be cleared, before the + records from the backup are inserted, i.e. all records in + the tables are deleted before the tables are restored. + Schema information about the tables is not cleared or read + from backup.</item> + <item><c>{keep_tables, TableList}</c> Where <c>TableList</c> + is a list of tables which should be not be cleared, before + the records from the backup are inserted, i.e. the records + in the backup will be added to the records in the table. + Schema information about the tables is not cleared or read + from backup.</item> + <item><c>{recreate_tables, TableList}</c> Where <c>TableList</c> + is a list of tables which should be re-created, before the + records from the backup are inserted. The tables are first + deleted and then created with the schema information from the + backup. All the nodes in the backup needs to be up and running.</item> + <item><c>{default_op, Operation}</c> Where <c>Operation</c> is + one of the following operations <c>skip_tables</c>, + <c>clear_tables</c>, <c>keep_tables</c> or + <c>recreate_tables</c>. The default operation specifies + which operation should be used on tables from the backup + which are not specified in any of the lists above. + If omitted, the operation <c>clear_tables</c> will be used. </item> + </list> + <p>The argument <c>Opaque</c> is forwarded to the backup module. + It returns <c>{atomic, TabList}</c> if successful, or the + tuple <c>{aborted, Reason}</c> in the case of an error. + <c>TabList</c> is a list of the restored tables. Tables which + are restored are write locked for the duration of the restore + operation. However, regardless of any lock conflict caused by + this, applications can continue to do their work during the + restore operation. + </p> + <p>The restoration is performed as a single transaction. If the + database is very large, it may not be possible to restore it + online. In such a case the old database must be restored by + installing a fallback, and then restart. + </p> + </section> + + <section> + <title>Fallbacks</title> + <p>The function <c>mnesia:install_fallback(Opaque, [Mod])</c> is + used to install a backup as fallback. It uses the backup module + <c>Mod</c>, or the default backup module, to access the backup + media. This function returns <c>ok</c> if successful, or + <c>{error, Reason}</c> in the case of an error. + </p> + <p>Installing a fallback is a distributed operation that is + <em>only</em> performed on all <c>db_nodes</c>. The fallback + is used to restore the database the next time the system is + started. If a Mnesia node with a fallback installed detects that + Mnesia on another node has died for some reason, it will + unconditionally terminate itself. + </p> + <p>A fallback is typically used when a system upgrade is + performed. A system typically involves the installation of new + software versions, and Mnesia tables are often transformed into + new layouts. If the system crashes during an upgrade, it is + highly probable re-installation of the old + applications will be required and restoration of the database + to its previous state. This can be done if a backup is performed and + installed as a fallback before the system upgrade begins. + </p> + <p>If the system upgrade fails, Mnesia must be restarted on all + <c>db_nodes</c> in order to restore the old database. The + fallback will be automatically de-installed after a successful + start-up. The function <c>mnesia:uninstall_fallback()</c> may + also be used to de-install the fallback after a + successful system upgrade. Again, this is a distributed + operation that is either performed on all <c>db_nodes</c>, or + none. Both the installation and de-installation of fallbacks + require Erlang to be up and running on all <c>db_nodes</c>, but + it does not matter if Mnesia is running or not. + </p> + </section> + + <section> + <title>Disaster Recovery</title> + <p>The system may become inconsistent as a result of a power + failure. The UNIX <c>fsck</c> feature can possibly repair the + file system, but there is no guarantee that the file contents + will be consistent. + </p> + <p>If Mnesia detects that a file has not been properly closed, + possibly as a result of a power failure, it will attempt to + repair the bad file in a similar manner. Data may be lost, but + Mnesia can be restarted even if the data is inconsistent. The + configuration parameter <c><![CDATA[-mnesia auto_repair <bool>]]></c> can be + used to control the behavior of Mnesia at start-up. If + <c><![CDATA[<bool>]]></c> has the value <c>true</c>, Mnesia will attempt to + repair the file; if <c><![CDATA[<bool>]]></c> has the value <c>false</c>, + Mnesia will not restart if it detects a suspect file. This + configuration parameter affects the repair behavior of log + files, DAT files, and the default backup media. + </p> + <p>The configuration parameter <c><![CDATA[-mnesia dump_log_update_in_place <bool>]]></c> controls the safety level of + the <c>mnesia:dump_log()</c> function. By default, Mnesia will + dump the transaction log directly into the DAT files. If a power + failure happens during the dump, this may cause the randomly + accessed DAT files to become corrupt. If the parameter is set to + <c>false</c>, Mnesia will copy the DAT files and target the dump + to the new temporary files. If the dump is successful, the + temporary files will be renamed to their normal DAT + suffixes. The possibility for unrecoverable inconsistencies in + the data files will be much smaller with this strategy. On the + other hand, the actual dumping of the transaction log will be + considerably slower. The system designer must decide whether + speed or safety is the higher priority. + </p> + <p>Replicas of type <c>disc_only_copies</c> will only be + affected by this parameter during the initial dump of the log + file at start-up. When designing applications which have + <em>very</em> high requirements, it may be appropriate not to + use <c>disc_only_copies</c> tables at all. The reason for this + is the random access nature of normal operating system files. If + a node goes down for reason for a reason such as a power + failure, these files may be corrupted because they are not + properly closed. The DAT files for <c>disc_only_copies</c> are + updated on a per transaction basis. + </p> + <p>If a disaster occurs and the Mnesia database has been + corrupted, it can be reconstructed from a backup. This should be + regarded as a last resort, since the backup contains old data. The + data is hopefully consistent, but data will definitely be lost + when an old backup is used to restore the database. + </p> + </section> + </section> +</chapter> + diff --git a/lib/mnesia/doc/src/Mnesia_chap8.xml b/lib/mnesia/doc/src/Mnesia_chap8.xml new file mode 100644 index 0000000000..3d2e23cf57 --- /dev/null +++ b/lib/mnesia/doc/src/Mnesia_chap8.xml @@ -0,0 +1,64 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Combining Mnesia with SNMP</title> + <prepared>Claes Wikström, Hans Nilsson and Håkan Mattsson</prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date></date> + <rev></rev> + <file>Mnesia_chap8.xml</file> + </header> + + <section> + <title>Combining Mnesia and SNMP </title> + <p>Many telecommunications applications must be controlled and + reconfigured remotely. It is sometimes an advantage to perform + this remote control with an open protocol such as the Simple + Network Management Protocol (SNMP). The alternatives to this would + be: + </p> + <list type="bulleted"> + <item>Not being able to control the application remotely at all. + </item> + <item>Using a proprietary control protocol. + </item> + <item>Using a bridge which maps control messages in a + proprietary protocol to a standardized management protocol and + vice versa. + </item> + </list> + <p>All of these approaches have different advantages and + disadvantages. Mnesia applications can easily be opened to the + SNMP protocol. It is possible to establish a direct one-to-one + mapping 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 Mnesia reference + manual. + </p> + </section> +</chapter> + diff --git a/lib/mnesia/doc/src/book.gif b/lib/mnesia/doc/src/book.gif Binary files differnew file mode 100644 index 0000000000..94b3868792 --- /dev/null +++ b/lib/mnesia/doc/src/book.gif diff --git a/lib/mnesia/doc/src/book.xml b/lib/mnesia/doc/src/book.xml new file mode 100644 index 0000000000..5389e615c7 --- /dev/null +++ b/lib/mnesia/doc/src/book.xml @@ -0,0 +1,48 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE book SYSTEM "book.dtd"> + +<book xmlns:xi="http://www.w3.org/2001/XInclude"> + <header titlestyle="normal"> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Mnesia</title> + <prepared>Claes Wikström, Hans Nilsson and Håkan Mattsson</prepared> + <docno></docno> + <date>1997-05-27</date> + <rev>1.2</rev> + <file>book.sgml</file> + </header> + <insidecover> + </insidecover> + <pagetext>Mnesia DBMS</pagetext> + <preamble> + <contents level="2"></contents> + </preamble> + <parts lift="no"> + <xi:include href="part.xml"/> + </parts> + <applications> + <xi:include href="ref_man.xml"/> + </applications> + <releasenotes> + <xi:include href="notes.xml"/> + </releasenotes> + <index></index> +</book> + diff --git a/lib/mnesia/doc/src/bup.erl b/lib/mnesia/doc/src/bup.erl new file mode 100644 index 0000000000..b9e541ad6a --- /dev/null +++ b/lib/mnesia/doc/src/bup.erl @@ -0,0 +1,239 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(bup). +-export([ + change_node_name/5, + view/2, + test/0, + test/1 + ]). + +-export([ + count/1, + display/1 + ]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Management of backups, a few demos + +%0 +change_node_name(Mod, From, To, Source, Target) -> + Switch = + fun(Node) when Node == From -> To; + (Node) when Node == To -> throw({error, already_exists}); + (Node) -> Node + end, + Convert = + fun({schema, db_nodes, Nodes}, Acc) -> + {[{schema, db_nodes, lists:map(Switch,Nodes)}], Acc}; + ({schema, version, Version}, Acc) -> + {[{schema, version, Version}], Acc}; + ({schema, cookie, Cookie}, Acc) -> + {[{schema, cookie, Cookie}], Acc}; + ({schema, Tab, CreateList}, Acc) -> + Keys = [ram_copies, disc_copies, disc_only_copies], + OptSwitch = + fun({Key, Val}) -> + case lists:member(Key, Keys) of + true -> {Key, lists:map(Switch, Val)}; + false-> {Key, Val} + end + end, + {[{schema, Tab, lists:map(OptSwitch, CreateList)}], Acc}; + (Other, Acc) -> + {[Other], Acc} + end, + mnesia:traverse_backup(Source, Mod, Target, Mod, Convert, switched). + +view(Source, Mod) -> + View = fun(Item, Acc) -> + io:format("~p.~n",[Item]), + {[Item], Acc + 1} + end, + mnesia:traverse_backup(Source, Mod, dummy, read_only, View, 0). +%0 + +-record(bup_rec, {key, val}). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test change of node name +%% +%% Assume db_nodes to be current node and on all other nodes but one +%% Create new schema, start Mnesia on all db_nodes +%% Create a table of disc_copies type which is replicated to all db_nodes +%% Perform a backup and change current node to unused node in backup +%% Start Mnesia on all nodes according to the new set of db_nodes +test() -> + test(nodes()). + +test(Nodes)-> + AllNodes = (Nodes -- [node()]) ++ [node()], + case length(AllNodes) of + Length when Length > 1 -> + OldBup = "old.BUP", + NewBup = "new.BUP", + Res = (catch test2(AllNodes, OldBup, NewBup)), + case Res of + {'EXIT', Reason} -> + file:delete(OldBup), + file:delete(NewBup), + {error, Reason}; + ok -> + ok = count(NewBup), + file:delete(OldBup), + file:delete(NewBup), + ok + end; + _ -> + {error,{"Must run on at least one other node",AllNodes}} + end. + +test2(AllNodes, OldBup, NewBup) -> + ThisNode = node(), + OtherNode = hd(AllNodes -- [ThisNode]), + OldNodes = AllNodes -- [OtherNode], + NewNodes = AllNodes -- [ThisNode], + Mod = mnesia_backup, % Assume local files + file:delete(OldBup), + file:delete(NewBup), + + %% Create old backup + rpc:multicall(AllNodes, mnesia, lkill, []), + ok = mnesia:delete_schema(AllNodes), + ok = mnesia:create_schema(OldNodes), + rpc:multicall(OldNodes, mnesia, start, []), + rpc:multicall(OldNodes, mnesia, wait_for_tables, [[schema], infinity]), + + CreateList = [{disc_copies, OldNodes}, + {attributes, record_info(fields, bup_rec)}], + {atomic, ok} = mnesia:create_table(bup_rec, CreateList), + rpc:multicall(OldNodes, mnesia, wait_for_tables, [[bup_rec], infinity]), + OldRecs = [#bup_rec{key = I, val = I * I} || I <- lists:seq(1, 10)], + lists:foreach(fun(R) -> ok = mnesia:dirty_write(R) end,OldRecs), + ok = mnesia:backup(OldBup, Mod), + ok = mnesia:dirty_write(#bup_rec{key = 4711, val = 4711}), + rpc:multicall(OldNodes, mnesia, stop, []), + {ok,_} = view(OldBup, Mod), + + %% Change node name + {ok,_} = change_node_name(Mod, ThisNode, OtherNode, OldBup, NewBup), + ok = rpc:call(OtherNode, mnesia, install_fallback, [NewBup, Mod]), + {_NewStartRes,[]} = rpc:multicall(NewNodes, mnesia, start, []), + rpc:call(OtherNode, mnesia, wait_for_tables, [[bup_rec], infinity]), + Wild = rpc:call(OtherNode, mnesia, table_info, [bup_rec, wild_pattern]), + NewRecs = rpc:call(OtherNode, mnesia, dirty_match_object, [Wild]), + rpc:multicall(NewNodes, mnesia, stop, []), + {ok,_} = view(NewBup, Mod), + + %% Sanity test + case {lists:sort(OldRecs), lists:sort(NewRecs)} of + {Same, Same} -> ok + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +-record(state, {counter_tab, size_tab, acc_size = 0, n_records = 0}). + + +%% Iterates over a backup file and shows some statistics +%% The identity of ets table containing the counters is not removed +count(BupFile) -> + CounterTab = ets:new(?MODULE, [set, public]), + SizeTab = ets:new(?MODULE, [set, public]), + Mod = mnesia:system_info(backup_module), + State = #state{counter_tab = CounterTab, size_tab = SizeTab}, + case mnesia:traverse_backup(BupFile, Mod, dummy, read_only, fun incr/2, State) of + {ok, State2} -> + Res = display(State2), + ets:delete(CounterTab), + ets:delete(SizeTab), + Res; + {error, Reason} -> + ets:delete(CounterTab), + ets:delete(SizeTab), + {error, Reason} + end. + +incr(Rec, State) -> + Tab = element(1, Rec), + Key = element(2, Rec), + Oid = {Tab, Key}, + incr_counter(State#state.counter_tab, Oid), + Size = size(term_to_binary(Rec)), + max_size(State#state.size_tab, Tab, Key, Size), + AccSize = State#state.acc_size, + N = State#state.n_records, + State2 = State#state{acc_size = AccSize + Size, n_records = N + 1}, + {[Rec], State2}. + +incr_counter(T, Counter) -> + case catch ets:update_counter(T, Counter, 1) of + {'EXIT', _} -> + ets:insert(T, {Counter, 1}); + _ -> + ignore + end. + +max_size(T, Tab, Key, Size) -> + case catch ets:lookup_element(T, Tab, 2) of + {'EXIT', _} -> + ets:insert(T, {Tab, Size, Key}); + OldSize when OldSize < Size -> + ets:insert(T, {Tab, Size, Key}); + _ -> + ignore + end. + +%% Displays the statistics found in the ets table +display(State) -> + CounterTab = State#state.counter_tab, + Tabs = [T || {{_, T}, _} <- match_tab(CounterTab, schema)], + io:format("~w tables with totally: ~w records, ~w keys, ~w bytes~n", + [length(Tabs), + State#state.n_records, + ets:info(CounterTab, size), + State#state.acc_size]), + display(State, lists:sort(Tabs)). + +display(State, [Tab | Tabs]) -> + Counters = match_tab(State#state.counter_tab, Tab), + io:format("~-10w records in table ~w~n", [length(Counters), Tab]), + Fun = fun({_Oid, Val}) when Val < 5 -> + ignore; + ({Oid, Val}) -> + io:format("~-10w *** records with key ~w~n", [Val, Oid]) + end, + lists:foreach(Fun, Counters), + display_size(State#state.size_tab, Tab), + display(State, Tabs); +display(_CounterTab, []) -> + ok. + +match_tab(T, Tab) -> + ets:match_object(T, {{Tab, '_'}, '_'}). + +display_size(T, Tab) -> + case catch ets:lookup(T, Tab) of + [] -> + ignore; + [{_, Size, Key}] when Size > 1000 -> + io:format("~-10w --- bytes occupied by largest record ~w~n", + [Size, {Tab, Key}]); + [{_, _, _}] -> + ignore + end. diff --git a/lib/mnesia/doc/src/company.erl b/lib/mnesia/doc/src/company.erl new file mode 100644 index 0000000000..28c32ed513 --- /dev/null +++ b/lib/mnesia/doc/src/company.erl @@ -0,0 +1,373 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(company). + +-compile(export_all). + +%0 + +-include_lib("stdlib/include/qlc.hrl"). +-include("company.hrl"). + +init() -> + mnesia:create_table(employee, + [{attributes, record_info(fields, employee)}]), + mnesia:create_table(dept, + [{attributes, record_info(fields, dept)}]), + mnesia:create_table(project, + [{attributes, record_info(fields, project)}]), + mnesia:create_table(manager, [{type, bag}, + {attributes, record_info(fields, manager)}]), + mnesia:create_table(at_dep, + [{attributes, record_info(fields, at_dep)}]), + mnesia:create_table(in_proj, [{type, bag}, + {attributes, record_info(fields, in_proj)}]). + +%0 + +%1 + +insert_emp(Emp, DeptId, ProjNames) -> + Ename = Emp#employee.name, + Fun = fun() -> + mnesia:write(Emp), + AtDep = #at_dep{emp = Ename, dept_id = DeptId}, + mnesia:write(AtDep), + mk_projs(Ename, ProjNames) + end, + mnesia:transaction(Fun). + + +mk_projs(Ename, [ProjName|Tail]) -> + mnesia:write(#in_proj{emp = Ename, proj_name = ProjName}), + mk_projs(Ename, Tail); +mk_projs(_, []) -> ok. + + +%1 + +%2 +females() -> + F = fun() -> + Q = qlc:q([E#employee.name || E <- mnesia:table(employee), + E#employee.sex == female]), + qlc:e(Q) + end, + mnesia:transaction(F). +%2 +%20 +all_females() -> + F = fun() -> + Female = #employee{sex = female, name = '$1', _ = '_'}, + mnesia:select(employee, [{Female, [], ['$1']}]) + end, + mnesia:transaction(F). +%20 + +g() -> l. + +%3 +female_bosses() -> + Q = qlc:q( [{E#employee.name, Boss#employee.name} || + E <- mnesia:table(employee), + Boss <- mnesia:table(employee), + Atdep <- mnesia:table(at_dep), + Mgr <- mnesia:table(manager), + E#employee.sex == female, + Atdep#at_dep.emp == E#employee.emp_no, + Mgr#manager.emp == Boss#employee.emp_no, + Atdep#at_dep.dept_id == Mgr#manager.dept] + ), + mnesia:transaction(fun() -> qlc:e(Q) end). +%3 + +%4 +raise_females(Amount) -> + F = fun() -> + Q = qlc:q([E || E <- mnesia:table(employee), + E#employee.sex == female]), + Fs = qlc:e(Q), + over_write(Fs, Amount) + end, + mnesia:transaction(F). + +over_write([E|Tail], Amount) -> + Salary = E#employee.salary + Amount, + New = E#employee{salary = Salary}, + mnesia:write(New), + 1 + over_write(Tail, Amount); +over_write([], _) -> + 0. +%4 + +%5 +raise(Eno, Raise) -> + F = fun() -> + [E] = mnesia:read(employee, Eno, write), + Salary = E#employee.salary + Raise, + New = E#employee{salary = Salary}, + mnesia:write(New) + end, + mnesia:transaction(F). +%5 + + +%6 +bad_raise(Eno, Raise) -> + F = fun() -> + [E] = mnesia:read({employee, Eno}), + Salary = E#employee.salary + Raise, + New = E#employee{salary = Salary}, + io:format("Trying to write ... ~n", []), + mnesia:write(New) + end, + mnesia:transaction(F). +%6 + +%9 +get_emps(Salary, Dep) -> + Q = qlc:q( + [E || E <- mnesia:table(employee), + At <- mnesia:table(at_dep), + E#employee.salary > Salary, + E#employee.emp_no == At#at_dep.emp, + At#at_dep.dept_id == Dep] + ), + F = fun() -> qlc:e(Q) end, + mnesia:transaction(F). +%9 +%10 +get_emps2(Salary, Dep) -> + Epat = mnesia:table_info(employee, wild_pattern), + Apat = mnesia:table_info(at_dep, wild_pattern), + F = fun() -> + All = mnesia:match_object(Epat), + High = filter(All, Salary), + Alldeps = mnesia:match_object(Apat), + filter_deps(High, Alldeps, Dep) + end, + mnesia:transaction(F). + + +filter([E|Tail], Salary) -> + if + E#employee.salary > Salary -> + [E | filter(Tail, Salary)]; + true -> + filter(Tail, Salary) + end; +filter([], _) -> + []. + +filter_deps([E|Tail], Deps, Dep) -> + case search_deps(E#employee.name, Deps, Dep) of + true -> + [E | filter_deps(Tail, Deps, Dep)]; + false -> + filter_deps(Tail, Deps, Dep) + end; +filter_deps([], _,_) -> + []. + + +search_deps(Name, [D|Tail], Dep) -> + if + D#at_dep.emp == Name, + D#at_dep.dept_id == Dep -> true; + true -> search_deps(Name, Tail, Dep) + end; +search_deps(_Name, _Tail, _Dep) -> + false. + +%10 + + + +%11 +bench1() -> + Me = #employee{emp_no= 104732, + name = klacke, + salary = 7, + sex = male, + phone = 99586, + room_no = {221, 015}}, + + F = fun() -> insert_emp(Me, 'B/DUR', [erlang, mnesia, otp]) end, + T1 = timer:tc(company, dotimes, [1000, F]), + mnesia:add_table_copy(employee, b@skeppet, ram_copies), + mnesia:add_table_copy(at_dep, b@skeppet, ram_copies), + mnesia:add_table_copy(in_proj, b@skeppet, ram_copies), + T2 = timer:tc(company, dotimes, [1000, F]), + {T1, T2}. + +dotimes(0, _) -> + ok; +dotimes(I, F) -> + F(), dotimes(I-1, F). + +%11 + + + + + +%12 + +dist_init() -> + mnesia:create_table(employee, + [{ram_copies, [a@gin, b@skeppet]}, + {attributes, record_info(fields, + employee)}]), + mnesia:create_table(dept, + [{ram_copies, [a@gin, b@skeppet]}, + {attributes, record_info(fields, dept)}]), + mnesia:create_table(project, + [{ram_copies, [a@gin, b@skeppet]}, + {attributes, record_info(fields, project)}]), + mnesia:create_table(manager, [{type, bag}, + {ram_copies, [a@gin, b@skeppet]}, + {attributes, record_info(fields, + manager)}]), + mnesia:create_table(at_dep, + [{ram_copies, [a@gin, b@skeppet]}, + {attributes, record_info(fields, at_dep)}]), + mnesia:create_table(in_proj, + [{type, bag}, + {ram_copies, [a@gin, b@skeppet]}, + {attributes, record_info(fields, in_proj)}]). +%12 + +%13 +remove_proj(ProjName) -> + F = fun() -> + Ip = qlc:e(qlc:q([X || X <- mnesia:table(in_proj), + X#in_proj.proj_name == ProjName] + )), + mnesia:delete({project, ProjName}), + del_in_projs(Ip) + end, + mnesia:transaction(F). + +del_in_projs([Ip|Tail]) -> + mnesia:delete_object(Ip), + del_in_projs(Tail); +del_in_projs([]) -> + done. +%13 + +%14 +sync() -> + case mnesia:wait_for_tables(tabs(), 10000) of + {timeout, RemainingTabs} -> + panic(RemainingTabs); + ok -> + synced + end. + +tabs() -> [employee, dept, project, at_dep, in_proj, manager]. + +%14 + + +find_male_on_second_floor() -> + Select = fun() -> +%21 + MatchHead = #employee{name='$1', sex=male, room_no={'$2', '_'}, _='_'}, + Guard = [{'>=', '$2', 220},{'<', '$2', 230}], + Result = '$1', + mnesia:select(employee,[{MatchHead, Guard, [Result]}]) +%21 + end, + mnesia:transaction(Select). + +panic(X) -> exit({panic, X}). + + +fill_tables() -> + Emps = + [ + {employee, 104465, "Johnson Torbjorn", 1, male, 99184, {242,038}}, + {employee, 107912, "Carlsson Tuula", 2, female,94556, {242,056}}, + {employee, 114872, "Dacker Bjarne", 3, male, 99415, {221,035}}, + {employee, 104531, "Nilsson Hans", 3, male, 99495, {222,026}}, + {employee, 104659, "Tornkvist Torbjorn", 2, male, 99514, {222,022}}, + {employee, 104732, "Wikstrom Claes", 2, male, 99586, {221,015}}, + {employee, 117716, "Fedoriw Anna", 1, female,99143, {221,031}}, + {employee, 115018, "Mattsson Hakan", 3, male, 99251, {203,348}} + ], + + Dept = [ + {dept, 'B/SF', "Open Telecom Platform"}, + {dept, 'B/SFP', "OTP - Product Development"}, + {dept, 'B/SFR', "Computer Science Laboratory"} + ], + + Projects = [ + {project, erlang, 1}, + {project, otp, 2}, + {project, beam, 3}, + {project, mnesia, 5}, + {project, wolf, 6}, + {project, documentation, 7}, + {project, www, 8} + ], + + Manager = [ + {manager, 104465, 'B/SF'}, + {manager, 104465, 'B/SFP'}, + {manager, 114872, 'B/SFR'} + ], + + At_dep = [ + {at_dep, 104465, 'B/SF'}, + {at_dep, 107912, 'B/SF'}, + {at_dep, 114872, 'B/SFR'}, + {at_dep, 104531, 'B/SFR'}, + {at_dep, 104659, 'B/SFR'}, + {at_dep, 104732, 'B/SFR'}, + {at_dep, 117716, 'B/SFP'}, + {at_dep, 115018, 'B/SFP'} + ], + + In_proj = [ + {in_proj, 104465, otp}, + {in_proj, 107912, otp}, + {in_proj, 114872, otp}, + {in_proj, 104531, otp}, + {in_proj, 104531, mnesia}, + {in_proj, 104545, wolf}, + {in_proj, 104659, otp}, + {in_proj, 104659, wolf}, + {in_proj, 104732, otp}, + {in_proj, 104732, mnesia}, + {in_proj, 104732, erlang}, + {in_proj, 117716, otp}, + {in_proj, 117716, documentation}, + {in_proj, 115018, otp}, + {in_proj, 115018, mnesia} + ], + + [mnesia:dirty_write(W) || W <- Emps], + [mnesia:dirty_write(W) || W <- Dept], + [mnesia:dirty_write(W) || W <- Projects], + %% Relations + [mnesia:dirty_write(W) || W <- Manager], + [mnesia:dirty_write(W) || W <- At_dep], + [mnesia:dirty_write(W) || W <- In_proj], + + ok. diff --git a/lib/mnesia/doc/src/company.fig b/lib/mnesia/doc/src/company.fig new file mode 100644 index 0000000000..9d5fcab041 --- /dev/null +++ b/lib/mnesia/doc/src/company.fig @@ -0,0 +1,88 @@ +#FIG 3.1 +Portrait +Center +Inches +1200 2 +6 8550 2700 10950 3150 +2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 8550 2700 10950 2700 10950 3150 8550 3150 8550 2700 +4 0 -1 0 0 0 12 0.0000 4 180 495 8850 3000 Project\001 +-6 +6 4950 2700 7350 3150 +2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 4950 2700 7350 2700 7350 3150 4950 3150 4950 2700 +4 0 -1 0 0 0 12 0.0000 4 180 705 5325 3000 Employee\001 +-6 +6 1275 2775 3675 3225 +2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 1275 2775 3675 2775 3675 3225 1275 3225 1275 2775 +4 0 -1 0 0 0 12 0.0000 4 180 345 1650 3075 Dept\001 +-6 +6 600 4500 2325 5700 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 600 5100 1425 5700 2325 5100 1425 4500 600 5100 +4 0 -1 0 0 0 12 0.0000 4 180 630 1125 5175 Manager\001 +-6 +6 9000 4500 10725 5700 +6 9600 5025 10125 5250 +6 9600 5025 10125 5250 +4 0 -1 0 0 0 12 0.0000 4 180 525 9600 5175 In_proj\001 +-6 +-6 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 9000 5100 9825 5700 10725 5100 9825 4500 9000 5100 +-6 +6 4125 2100 7950 2700 +1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 5287 2325 262 225 5025 2100 5550 2550 +1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 5812 2325 262 225 5550 2100 6075 2550 +1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 6337 2325 262 225 6075 2100 6600 2550 +1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 6862 2325 262 225 6600 2100 7125 2550 +1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 4612 2325 413 225 5025 2100 4200 2550 +1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 7537 2325 413 225 7950 2100 7125 2550 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 4800 2550 5925 2700 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 5475 2550 5850 2700 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 5850 2550 5850 2700 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 6300 2550 5850 2700 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 6750 2550 5850 2700 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 7275 2550 5925 2700 +4 0 -1 0 0 0 12 0.0000 4 180 3540 4350 2400 emp_no name salary sex phone room_no\001 +-6 +6 3300 4500 5100 5775 +6 3900 5025 4425 5250 +4 0 -1 0 0 0 12 0.0000 4 180 525 3900 5175 At_dep\001 +-6 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5 + 3323 5135 4148 5735 5048 5135 4148 4535 3323 5135 +-6 +1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 1875 2287 600 187 1275 2100 2475 2475 +1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 3075 2287 600 187 2475 2100 3675 2475 +1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 8850 2325 450 225 8400 2100 9300 2550 +1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 9750 2325 450 225 9300 2100 10200 2550 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 1575 3225 600 5100 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 2325 5100 5250 3150 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 5850 3150 5025 5175 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 3300 5100 2550 3225 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 7050 3150 9000 5100 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 10725 5100 9825 3150 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 1875 2475 2400 2775 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 3075 2475 2400 2775 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 9000 2550 9750 2700 +2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2 + 9750 2550 9675 2700 +4 0 -1 0 0 0 12 0.0000 4 135 1500 1575 2325 id name\001 +4 0 -1 0 0 0 12 0.0000 4 135 1635 8775 2400 Name number \001 diff --git a/lib/mnesia/doc/src/company.gif b/lib/mnesia/doc/src/company.gif Binary files differnew file mode 100644 index 0000000000..3cd0185e69 --- /dev/null +++ b/lib/mnesia/doc/src/company.gif diff --git a/lib/mnesia/doc/src/company.hrl b/lib/mnesia/doc/src/company.hrl new file mode 100644 index 0000000000..85e0e6ff12 --- /dev/null +++ b/lib/mnesia/doc/src/company.hrl @@ -0,0 +1,50 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + + +%0 +-record(employee, {emp_no, + name, + salary, + sex, + phone, + room_no}). + +-record(dept, {id, + name}). + +-record(project, {name, + number}). + + +-record(manager, {emp, + dept}). + +-record(at_dep, {emp, + dept_id}). + +-record(in_proj, {emp, + proj_name}). + +%0 + + + + + + diff --git a/lib/mnesia/doc/src/company.ps b/lib/mnesia/doc/src/company.ps new file mode 100644 index 0000000000..64a45d07f3 --- /dev/null +++ b/lib/mnesia/doc/src/company.ps @@ -0,0 +1,213 @@ +%!PS-Adobe-2.0 +%%Title: company.fig +%%Creator: fig2dev Version 3.1 Patchlevel 2 +%%CreationDate: Thu Oct 31 18:09:46 1996 +%%For: klacke@gin (Claes Wikstrom,EUA/SU) +%Magnification: 0.70 +%%Orientation: Portrait +%%BoundingBox: 79 343 516 498 +%%Pages: 1 +%%BeginSetup +%%IncludeFeature: *PageSize A4 +%%EndSetup +%%EndComments +/$F2psDict 200 dict def +$F2psDict begin +$F2psDict /mtrx matrix put +/col-1 {0 setgray} bind def +/col0 {0.000 0.000 0.000 srgb} bind def +/col1 {0.000 0.000 1.000 srgb} bind def +/col2 {0.000 1.000 0.000 srgb} bind def +/col3 {0.000 1.000 1.000 srgb} bind def +/col4 {1.000 0.000 0.000 srgb} bind def +/col5 {1.000 0.000 1.000 srgb} bind def +/col6 {1.000 1.000 0.000 srgb} bind def +/col7 {1.000 1.000 1.000 srgb} bind def +/col8 {0.000 0.000 0.560 srgb} bind def +/col9 {0.000 0.000 0.690 srgb} bind def +/col10 {0.000 0.000 0.820 srgb} bind def +/col11 {0.530 0.810 1.000 srgb} bind def +/col12 {0.000 0.560 0.000 srgb} bind def +/col13 {0.000 0.690 0.000 srgb} bind def +/col14 {0.000 0.820 0.000 srgb} bind def +/col15 {0.000 0.560 0.560 srgb} bind def +/col16 {0.000 0.690 0.690 srgb} bind def +/col17 {0.000 0.820 0.820 srgb} bind def +/col18 {0.560 0.000 0.000 srgb} bind def +/col19 {0.690 0.000 0.000 srgb} bind def +/col20 {0.820 0.000 0.000 srgb} bind def +/col21 {0.560 0.000 0.560 srgb} bind def +/col22 {0.690 0.000 0.690 srgb} bind def +/col23 {0.820 0.000 0.820 srgb} bind def +/col24 {0.500 0.190 0.000 srgb} bind def +/col25 {0.630 0.250 0.000 srgb} bind def +/col26 {0.750 0.380 0.000 srgb} bind def +/col27 {1.000 0.500 0.500 srgb} bind def +/col28 {1.000 0.630 0.630 srgb} bind def +/col29 {1.000 0.750 0.750 srgb} bind def +/col30 {1.000 0.880 0.880 srgb} bind def +/col31 {1.000 0.840 0.000 srgb} bind def + +end +save +55.0 585.5 translate +1 -1 scale + +/cp {closepath} bind def +/ef {eofill} bind def +/gr {grestore} bind def +/gs {gsave} bind def +/sa {save} bind def +/rs {restore} bind def +/l {lineto} bind def +/m {moveto} bind def +/rm {rmoveto} bind def +/n {newpath} bind def +/s {stroke} bind def +/sh {show} bind def +/slc {setlinecap} bind def +/slj {setlinejoin} bind def +/slw {setlinewidth} bind def +/srgb {setrgbcolor} bind def +/rot {rotate} bind def +/sc {scale} bind def +/sd {setdash} bind def +/ff {findfont} bind def +/sf {setfont} bind def +/scf {scalefont} bind def +/sw {stringwidth} bind def +/tr {translate} bind def +/tnt {dup dup currentrgbcolor + 4 -2 roll dup 1 exch sub 3 -1 roll mul add + 4 -2 roll dup 1 exch sub 3 -1 roll mul add + 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb} + bind def +/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul + 4 -2 roll mul srgb} bind def + /DrawEllipse { + /endangle exch def + /startangle exch def + /yrad exch def + /xrad exch def + /y exch def + /x exch def + /savematrix mtrx currentmatrix def + x y tr xrad yrad sc 0 0 1 startangle endangle arc + closepath + savematrix setmatrix + } def + +/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def +/$F2psEnd {$F2psEnteredState restore end} def +%%EndProlog + +$F2psBegin +10 setmiterlimit +n 0 842 m 0 0 l 595 0 l 595 842 l cp clip + 0.04200 0.04200 sc +%%Page: 1 1 +7.500 slw +% Polyline +n 8550 2700 m 10950 2700 l 10950 3150 l 8550 3150 l cp gs col-1 s gr +/Times-Roman ff 180.00 scf sf +8850 3000 m +gs 1 -1 sc (Project) col-1 sh gr +% Polyline +n 4950 2700 m 7350 2700 l 7350 3150 l 4950 3150 l cp gs col-1 s gr +/Times-Roman ff 180.00 scf sf +5325 3000 m +gs 1 -1 sc (Employee) col-1 sh gr +% Polyline +n 1275 2775 m 3675 2775 l 3675 3225 l 1275 3225 l cp gs col-1 s gr +/Times-Roman ff 180.00 scf sf +1650 3075 m +gs 1 -1 sc (Dept) col-1 sh gr +% Polyline +n 600 5100 m 1425 5700 l 2325 5100 l 1425 4500 l 600 5100 l gs col-1 s gr +/Times-Roman ff 180.00 scf sf +1125 5175 m +gs 1 -1 sc (Manager) col-1 sh gr +/Times-Roman ff 180.00 scf sf +9600 5175 m +gs 1 -1 sc (In_proj) col-1 sh gr +% Polyline +n 9000 5100 m 9825 5700 l 10725 5100 l 9825 4500 l 9000 5100 l gs col-1 s gr +% Ellipse +n 5287 2325 262 225 0 360 DrawEllipse gs col-1 s gr + +% Ellipse +n 5812 2325 262 225 0 360 DrawEllipse gs col-1 s gr + +% Ellipse +n 6337 2325 262 225 0 360 DrawEllipse gs col-1 s gr + +% Ellipse +n 6862 2325 262 225 0 360 DrawEllipse gs col-1 s gr + +% Ellipse +n 4612 2325 413 225 0 360 DrawEllipse gs col-1 s gr + +% Ellipse +n 7537 2325 413 225 0 360 DrawEllipse gs col-1 s gr + +% Polyline +n 4800 2550 m 5925 2700 l gs col-1 s gr +% Polyline +n 5475 2550 m 5850 2700 l gs col-1 s gr +% Polyline +n 5850 2550 m 5850 2700 l gs col-1 s gr +% Polyline +n 6300 2550 m 5850 2700 l gs col-1 s gr +% Polyline +n 6750 2550 m 5850 2700 l gs col-1 s gr +% Polyline +n 7275 2550 m 5925 2700 l gs col-1 s gr +/Times-Roman ff 180.00 scf sf +4350 2400 m +gs 1 -1 sc (emp_no name salary sex phone room_no) col-1 sh gr +/Times-Roman ff 180.00 scf sf +3900 5175 m +gs 1 -1 sc (At_dep) col-1 sh gr +% Polyline +n 3323 5135 m 4148 5735 l 5048 5135 l 4148 4535 l 3323 5135 l gs col-1 s gr +% Ellipse +n 1875 2287 600 187 0 360 DrawEllipse gs col-1 s gr + +% Ellipse +n 3075 2287 600 187 0 360 DrawEllipse gs col-1 s gr + +% Ellipse +n 8850 2325 450 225 0 360 DrawEllipse gs col-1 s gr + +% Ellipse +n 9750 2325 450 225 0 360 DrawEllipse gs col-1 s gr + +% Polyline +n 1575 3225 m 600 5100 l gs col-1 s gr +% Polyline +n 2325 5100 m 5250 3150 l gs col-1 s gr +% Polyline +n 5850 3150 m 5025 5175 l gs col-1 s gr +% Polyline +n 3300 5100 m 2550 3225 l gs col-1 s gr +% Polyline +n 7050 3150 m 9000 5100 l gs col-1 s gr +% Polyline +n 10725 5100 m 9825 3150 l gs col-1 s gr +% Polyline +n 1875 2475 m 2400 2775 l gs col-1 s gr +% Polyline +n 3075 2475 m 2400 2775 l gs col-1 s gr +% Polyline +n 9000 2550 m 9750 2700 l gs col-1 s gr +% Polyline +n 9750 2550 m 9675 2700 l gs col-1 s gr +/Times-Roman ff 180.00 scf sf +1575 2325 m +gs 1 -1 sc ( id name) col-1 sh gr +/Times-Roman ff 180.00 scf sf +8775 2400 m +gs 1 -1 sc (Name number ) col-1 sh gr +showpage +$F2psEnd +rs diff --git a/lib/mnesia/doc/src/company_o.erl b/lib/mnesia/doc/src/company_o.erl new file mode 100644 index 0000000000..3c7ad0d5e5 --- /dev/null +++ b/lib/mnesia/doc/src/company_o.erl @@ -0,0 +1,144 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(company_o). +-compile(export_all). + +-import(mnesia, [transaction/1]). + +%0 +-include_lib("stdlib/include/qlc.hrl"). +-include("company_o.hrl"). + + +sinit() -> + mnesia:create_schema([node()]). + +init() -> + mnesia:create_table(employee, + [{attributes, record_info(fields, employee)}]), + mnesia:create_table(dept, + [{attributes, record_info(fields, dept)}]), + mnesia:create_table(project, + [{attributes, record_info(fields, project)}]). + +%0 + + + +%1 + +insert_emp(Emp, DeptId, ProjNames) -> + Fun = fun() -> + mnesia:write(Emp#employee{dept = DeptId, + projects = ProjNames}) + end, + mnesia:transaction(Fun). + + +%1 + +%2 +females() -> + F = fun() -> + Q = qlc:q([E#employee.name || E <- mnesia:table(employee), + E#employee.sex == female]), + qlc:e(Q) + end, + mnesia:transaction(F). +%2 + +%3 +female_bosses() -> + F = fun() -> qlc:e(qlc:q( + [{E#employee.name, Boss#employee.name} || + E <- mnesia:table(employee), + Boss <- mnesia:table(employee), + Boss#employee.emp_no == E#employee.manager, + E#employee.sex == female] + )) + end, + mnesia:transaction(F). + + +%4 +raise_females(Amount) -> + F = fun() -> + Q = qlc:q([E || E <- mnesia:table(employee), + E#employee.sex == female]), + Fs = qlc:e(Q), + over_write(Fs, Amount) + end, + mnesia:transaction(F). + +over_write([E|Tail], Amount) -> + Salary = E#employee.salary + Amount, + New = E#employee{salary = Salary}, + mnesia:write(New), + 1 + over_write(Tail, Amount); +over_write([], _) -> + 0. +%4 + +%5 +raise(Eno, Raise) -> + F = fun() -> + [E] = mnesia:read({employee, Eno}), + Salary = E#employee.salary + Raise, + New = E#employee{salary = Salary}, + mnesia:write(New) + end, + mnesia:transaction(F). +%5 + + +%6 +bad_raise(Eno, Raise) -> + F = fun() -> + [E] = mnesia:read({employee, Eno}), + Salary = E#employee.salary + Raise, + New = E#employee{salary = Salary}, + io:format("Trying to write ... ~n", []), + mnesia:write(New) + end, + mnesia:transaction(F). +%6 + +%9 +get_emps(Salary, Dep) -> + Q = qlc:q( + [E || E <- mnesia:table(employee), + E#employee.salary > Salary, + E#employee.dept == Dep] + ), + F = fun() -> qlc:e(Q) end, + transaction(F). +%9 + +%10 +get_emps2(Salary, Dep) -> + Epat0 = mnesia:table_info(employee, wild_pattern), + Epat = Epat0#employee{dept = Dep}, + F = fun() -> + All = mnesia:match_object(Epat), + [E || E <-All, E#employee.salary > Salary ] + end, + mnesia:transaction(F). + + +%10 + diff --git a/lib/mnesia/doc/src/company_o.hrl b/lib/mnesia/doc/src/company_o.hrl new file mode 100644 index 0000000000..d8b584c296 --- /dev/null +++ b/lib/mnesia/doc/src/company_o.hrl @@ -0,0 +1,38 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% + +%0 +-record(employee, {emp_no, + name, + salary, + sex, + phone, + room_no, + dept, + projects, + manager}). + + +-record(dept, {id, + name}). + +-record(project, {name, + number, + location}). + +%0 diff --git a/lib/mnesia/doc/src/fascicules.xml b/lib/mnesia/doc/src/fascicules.xml new file mode 100644 index 0000000000..0678195e07 --- /dev/null +++ b/lib/mnesia/doc/src/fascicules.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE fascicules SYSTEM "fascicules.dtd"> + +<fascicules> + <fascicule file="part" href="part_frame.html" entry="no"> + User's Guide + </fascicule> + <fascicule file="ref_man" href="ref_man_frame.html" entry="yes"> + Reference Manual + </fascicule> + <fascicule file="part_notes" href="part_notes_frame.html" entry="no"> + Release Notes + </fascicule> + <fascicule file="" href="../../../../doc/print.html" entry="no"> + Off-Print + </fascicule> +</fascicules> + diff --git a/lib/mnesia/doc/src/make.dep b/lib/mnesia/doc/src/make.dep new file mode 100644 index 0000000000..6e79484cb3 --- /dev/null +++ b/lib/mnesia/doc/src/make.dep @@ -0,0 +1,46 @@ +# ---------------------------------------------------- +# >>>> Do not edit this file <<<< +# This file was automaticly generated by +# /home/otp/bin/docdepend +# ---------------------------------------------------- + + +# ---------------------------------------------------- +# TeX files that the DVI file depend on +# ---------------------------------------------------- + +book.dvi: Mnesia_App_A.tex Mnesia_App_B.tex Mnesia_App_C.tex \ + Mnesia_App_D.tex Mnesia_chap1.tex Mnesia_chap2.tex \ + Mnesia_chap3.tex Mnesia_chap4.tex Mnesia_chap5.tex \ + Mnesia_chap7.tex Mnesia_chap8.tex book.tex \ + mnesia.tex mnesia_frag_hash.tex mnesia_registry.tex \ + part.tex ref_man.tex + +# ---------------------------------------------------- +# Source inlined when transforming from source to LaTeX +# ---------------------------------------------------- + +Mnesia_App_B.tex: ../../src/mnesia_backup.erl + +Mnesia_App_C.tex: ../../src/mnesia_frag.erl + +Mnesia_App_D.tex: ../../src/mnesia_frag_hash.erl + +Mnesia_chap2.tex: company.erl company.hrl + +Mnesia_chap3.tex: company.erl + +Mnesia_chap4.tex: company.erl + +Mnesia_chap5.tex: FRUITS company.erl company_o.erl company_o.hrl + +Mnesia_chap7.tex: bup.erl + +book.tex: ref_man.xml + +# ---------------------------------------------------- +# Pictures that the DVI file depend on +# ---------------------------------------------------- + +book.dvi: company.ps + diff --git a/lib/mnesia/doc/src/mnesia.gif b/lib/mnesia/doc/src/mnesia.gif Binary files differnew file mode 100644 index 0000000000..fbbabee5aa --- /dev/null +++ b/lib/mnesia/doc/src/mnesia.gif diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml new file mode 100644 index 0000000000..3484cd104a --- /dev/null +++ b/lib/mnesia/doc/src/mnesia.xml @@ -0,0 +1,3100 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>1996</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>mnesia</title> + <prepared>Claes Wikström and Håkan Mattsson</prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date></date> + <rev></rev> + <file></file> + </header> + <module>mnesia</module> + <modulesummary>A Distributed Telecommunications DBMS </modulesummary> + <description> + <p><c>Mnesia</c> 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> + <p>Listed below are some of the most important and attractive capabilities, Mnesia provides: + </p> + <list type="bulleted"> + <item> + <p>A relational/object hybrid data model which is + suitable for telecommunications applications. + </p> + </item> + <item> + <p>A specifically designed DBMS query language, QLC (as an add-on library). + </p> + </item> + <item> + <p>Persistence. Tables may be coherently kept on disc as + well as in main memory. + </p> + </item> + <item> + <p>Replication. Tables may be replicated at several nodes. + </p> + </item> + <item> + <p>Atomic transactions. A series of table manipulation + operations can be grouped into a single atomic + transaction. + </p> + </item> + <item> + <p>Location transparency. Programs can be written without + knowledge of the actual location of data. + </p> + </item> + <item> + <p>Extremely fast real time data searches. + </p> + </item> + <item> + <p>Schema manipulation routines. It is possible to + reconfigure the DBMS at runtime without stopping the + system. + </p> + </item> + </list> + <p>This Reference Manual describes the Mnesia API. This includes + functions used to define and manipulate Mnesia tables. + </p> + <p>All functions documented in these pages can be used in any + combination with queries using the list comprehension notation. The + query notation is described in the QLC's man page. + </p> + <p>Data in Mnesia is organized as a set of tables. Each table + has a name which 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. Below + are some of the properties that are associated with each + table: + </p> + <list type="bulleted"> + <item> + <p><c>type</c>. Each table can either have 'set', + 'ordered_set' or 'bag' semantics. Note: currently 'ordered_set' + is not supported for 'disc_only_copies'. If a table is of type + 'set' it means that each key leads to either one or zero + records. <br></br> +If a new item is inserted with the same key as + an existing record, the old record is overwritten. On the + other hand, if a table is of type 'bag', each key can map to + several records. However, all records in type bag tables are + unique, only the keys may be duplicated. + </p> + </item> + <item> + <p><c>record_name</c>. All records stored in a table must + have the same name. You may say that the records must be + instances of the same record type. + </p> + </item> + <item> + <p><c>ram_copies</c> A table can be replicated on a number + of Erlang nodes. The <c>ram_copies</c> property specifies a + list of Erlang nodes where RAM copies are kept. These + copies can be dumped to disc at regular intervals. However, + updates to these copies are not written to disc on a + transaction basis. + </p> + </item> + <item> + <p><c>disc_copies</c> The <c>disc_copies</c> property + specifies a list of Erlang nodes where the table is kept in + RAM as well as on disc. All updates of the table are + performed on the actual table and are also logged to disc. + If a table is of type <c>disc_copies</c> at a certain node, + it means that the entire table is resident in RAM memory as + well as on disc. Each transaction performed on the table is + appended to a LOG file as well as written into the RAM + table. + </p> + </item> + <item> + <p><c>disc_only_copies</c> Some, or all, table replicas + can be kept on disc only. These replicas are considerably + slower than the RAM based replicas. + </p> + </item> + <item> + <p><c>index</c> This is a list of attribute names, or + integers, which specify the tuple positions on which + Mnesia shall build and maintain an extra index table. + </p> + </item> + <item> + <p><c>local_content</c> When an application requires + tables whose contents is local to each node, + <c>local_content</c> tables may be used. The name of the + table is known to all Mnesia nodes, but its contents is + unique on each node. This means that access to such a table + must be done locally. Set the <c>local_content</c> field to + <c>true</c> if you want to enable the <c>local_content</c> + behavior. The default is <c>false</c>. + </p> + </item> + <item> + <p><c>snmp</c> Each (set based) Mnesia table can be + automatically turned into an SNMP ordered table as well. + This property specifies the types of the SNMP keys. + </p> + </item> + <item> + <p><c>attributes</c>. The names of the attributes for the + records that are inserted in the table. + </p> + </item> + </list> + <p>See <c>mnesia:create_table/2</c> about the complete set of + table properties and their details. + </p> + <p>This document uses a table of persons to illustrate various + examples. The following record definition is assumed: + </p> + <code type="none"> +-record(person, {name, + age = 0, + address = unknown, + salary = 0, + children = []}), + </code> + <p>The first attribute of the record is the primary key, or key + for short. + </p> + <p>The function descriptions are sorted in alphabetic order. <em>Hint:</em> + start to read about <c>mnesia:create_table/2</c>, + <c>mnesia:lock/2</c> and <c>mnesia:activity/4</c> before you continue on + and learn about the rest. + </p> + <p>Writing or deleting in transaction context creates a local copy + of each modified record during the transaction. During iteration, + i.e. <c>mnesia:fold[lr]/4</c> <c>mnesia:next/2</c> <c>mnesia:prev/2</c> + <c>mnesia:snmp_get_next_index/2</c>, mnesia will compensate for + every written or deleted record, which may reduce the + performance. If possible avoid writing or deleting records in + the same transaction before iterating over the table. + </p> + </description> + <funcs> + <func> + <name>abort(Reason) -> transaction abort </name> + <fsummary>Abort the current transaction.</fsummary> + <desc> + <p>Makes the transaction silently + return the tuple <c>{aborted, Reason}</c>. + The abortion of a Mnesia transaction means that + an exception will be thrown to an enclosing <c>catch</c>. + Thus, the expression <c>catch mnesia:abort(x)</c> does + not abort the transaction. </p> + </desc> + </func> + <func> + <name>activate_checkpoint(Args) -> {ok,Name,Nodes} | {error,Reason}</name> + <fsummary>Activate a checkpoint.</fsummary> + <desc> + <p>A checkpoint is a consistent view of the system. + A checkpoint can be activated on a set of tables. + This checkpoint can then be traversed and will + present a view of the system as it existed at the time when + the checkpoint was activated, even if the tables are being or have been + manipulated. + </p> + <p><c>Args</c> is a list of the following tuples: + </p> + <list type="bulleted"> + <item> + <p><c>{name,Name}</c>. <c>Name</c> of checkpoint. Each + checkpoint must have a name which is unique to the + associated nodes. The name can be reused only once the + checkpoint has been deactivated. By default, a name + which is probably unique is generated. + </p> + </item> + <item> + <p><c>{max,MaxTabs}</c><c>MaxTabs</c> is a list of + tables that should be included in the checkpoint. The + default is []. For these tables, the redundancy will be + maximized and checkpoint information will be retained together + with all replicas. The checkpoint becomes more fault + tolerant if the tables have several replicas. When a new + replica is added by means of the schema manipulation + function <c>mnesia:add_table_copy/3</c>, a retainer will + also be attached automatically. + </p> + </item> + <item> + <p><c>{min,MinTabs}</c>. <c>MinTabs</c> is a list of + tables that should be included in the checkpoint. The + default is []. For these tables, the redundancy will be + minimized and the checkpoint information will only be retained + with one replica, preferably on the local node. + </p> + </item> + <item> + <p><c>{allow_remote,Bool}</c>. <c>false</c> means that + all retainers must be local. The checkpoint cannot be + activated if a table does not reside locally. + <c>true</c> allows retainers to be allocated on any + node. Default is set to <c>true</c>. + </p> + </item> + <item> + <p><c>{ram_overrides_dump,Bool} </c> Only applicable + for <c>ram_copies</c>. <c>Bool</c> allows you to choose + to backup the table state as it is in RAM, or as it is on + disc. <c>true</c> means that the latest committed + records in RAM should be included in the checkpoint. + These are the records that the application accesses. + <c>false</c> means that the records dumped to DAT files + should be included in the checkpoint. These are the + records that will be loaded at startup. Default is + <c>false</c>. + </p> + </item> + </list> + <p>Returns <c>{ok,Name,Nodes}</c> or <c>{error,Reason}</c>. + <c>Name</c> is the (possibly generated) name of the + checkpoint. <c>Nodes</c> are the nodes that + are involved in the checkpoint. Only nodes that keep a + checkpoint retainer know about the checkpoint. + </p> + </desc> + </func> + <func> + <name>activity(AccessContext, Fun [, Args]) -> ResultOfFun | exit(Reason)</name> + <fsummary>Execute <c>Fun</c>in <c>AccessContext</c>.</fsummary> + <desc> + <p>Invokes <c>mnesia:activity(AccessContext, Fun, Args, AccessMod)</c> where <c>AccessMod</c> is the default + access callback module obtained by + <c>mnesia:system_info(access_module)</c>. <c>Args</c> + defaults to the empty list <c>[]</c>.</p> + </desc> + </func> + <func> + <name>activity(AccessContext, Fun, Args, AccessMod) -> ResultOfFun | exit(Reason)</name> + <fsummary>Execute <c>Fun</c>in <c>AccessContext</c>.</fsummary> + <desc> + <p>This function executes the functional object <c>Fun</c> + with the arguments <c>Args</c>. + </p> + <p>The code which executes inside the activity can + consist of a series of table manipulation functions, which is + performed in a <c>AccessContext</c>. Currently, the following + access contexts are supported: + </p> + <taglist> + <tag><c>transaction</c></tag> + <item> + <p>Short for <c>{transaction, infinity}</c></p> + </item> + <tag><c>{transaction, Retries}</c></tag> + <item> + <p>Invokes <c>mnesia:transaction(Fun, Args, Retries)</c>. Note that the result from the <c>Fun</c> is + returned if the transaction was successful (atomic), + otherwise the function exits with an abort reason. + </p> + </item> + <tag><c>sync_transaction</c></tag> + <item> + <p>Short for <c>{sync_transaction, infinity}</c></p> + </item> + <tag><c>{sync_transaction, Retries}</c></tag> + <item> + <p>Invokes <c>mnesia:sync_transaction(Fun, Args, Retries)</c>. Note that the result from the <c>Fun</c> is + returned if the transaction was successful (atomic), + otherwise the function exits with an abort reason. + </p> + </item> + <tag><c>async_dirty</c></tag> + <item> + <p>Invokes <c>mnesia:async_dirty(Fun, Args)</c>. + </p> + </item> + <tag><c>sync_dirty</c></tag> + <item> + <p>Invokes <c>mnesia:sync_dirty(Fun, Args)</c>. + </p> + </item> + <tag><c>ets</c></tag> + <item> + <p>Invokes <c>mnesia:ets(Fun, Args)</c>. + </p> + </item> + </taglist> + <p>This function (<c>mnesia:activity/4</c>) differs in an + important aspect from the <c>mnesia:transaction</c>, + <c>mnesia:sync_transaction</c>, + <c>mnesia:async_dirty</c>, <c>mnesia:sync_dirty</c> and + <c>mnesia:ets</c> functions. The <c>AccessMod</c> argument + is the name of a callback module which implements the + <c>mnesia_access</c> behavior. + </p> + <p>Mnesia will forward calls to the following functions: + </p> + <list type="bulleted"> + <item> + <p>mnesia:write/3 (write/1, s_write/1)</p> + </item> + <item> + <p>mnesia:delete/3 (delete/1, s_delete/1)</p> + </item> + <item> + <p>mnesia:delete_object/3 (delete_object/1, s_delete_object/1)</p> + </item> + <item> + <p>mnesia:read/3 (read/1, wread/1)</p> + </item> + <item> + <p>mnesia:match_object/3 (match_object/1)</p> + </item> + <item> + <p>mnesia:all_keys/1</p> + </item> + <item> + <p>mnesia:first/1</p> + </item> + <item> + <p>mnesia:last/1</p> + </item> + <item> + <p>mnesia:prev/2</p> + </item> + <item> + <p>mnesia:next/2</p> + </item> + <item> + <p>mnesia:index_match_object/4 (index_match_object/2)</p> + </item> + <item> + <p>mnesia:index_read/3</p> + </item> + <item> + <p>mnesia:lock/2 (read_lock_table/1, write_lock_table/1)</p> + </item> + <item> + <p>mnesia:table_info/2</p> + </item> + </list> + <p>to the corresponding: + </p> + <list type="bulleted"> + <item> + <p>AccessMod:lock(ActivityId, Opaque, LockItem, LockKind)</p> + </item> + <item> + <p>AccessMod:write(ActivityId, Opaque, Tab, Rec, LockKind)</p> + </item> + <item> + <p>AccessMod:delete(ActivityId, Opaque, Tab, Key, LockKind)</p> + </item> + <item> + <p>AccessMod:delete_object(ActivityId, Opaque, Tab, RecXS, LockKind)</p> + </item> + <item> + <p>AccessMod:read(ActivityId, Opaque, Tab, Key, LockKind)</p> + </item> + <item> + <p>AccessMod:match_object(ActivityId, Opaque, Tab, Pattern, LockKind)</p> + </item> + <item> + <p>AccessMod:all_keys(ActivityId, Opaque, Tab, LockKind)</p> + </item> + <item> + <p>AccessMod:first(ActivityId, Opaque, Tab)</p> + </item> + <item> + <p>AccessMod:last(ActivityId, Opaque, Tab)</p> + </item> + <item> + <p>AccessMod:prev(ActivityId, Opaque, Tab, Key)</p> + </item> + <item> + <p>AccessMod:next(ActivityId, Opaque, Tab, Key)</p> + </item> + <item> + <p>AccessMod:index_match_object(ActivityId, Opaque, Tab, Pattern, Attr, LockKind)</p> + </item> + <item> + <p>AccessMod:index_read(ActivityId, Opaque, Tab, SecondaryKey, Attr, LockKind)</p> + </item> + <item> + <p>AccessMod:table_info(ActivityId, Opaque, Tab, InfoItem)</p> + </item> + </list> + <p>where <c>ActivityId</c> is a record which represents the + identity of the enclosing Mnesia activity. The first field + (obtained with <c>element(1, ActivityId)</c> contains an + atom which may be interpreted as the type of the activity: + <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 Mnesia. + </p> + <p><c>Opaque</c> is an opaque data structure which is internal + to Mnesia.</p> + </desc> + </func> + <func> + <name>add_table_copy(Tab, Node, Type) -> {aborted, R} | {atomic, ok}</name> + <fsummary>Copy a table to a remote node.</fsummary> + <desc> + <p>This function makes another copy of a table at the + node <c>Node</c>. The <c>Type</c> argument must be + either of the atoms <c>ram_copies</c>, <c>disc_copies</c>, + or + <c>disc_only_copies</c>. For example, the following call + ensures that a disc replica of the <c>person</c> table also + exists at node <c>Node</c>.</p> + <code type="none"> +mnesia:add_table_copy(person, Node, disc_copies) + </code> + <p>This function can also be used to add a replica of the + table named <c>schema</c>.</p> + </desc> + </func> + <func> + <name>add_table_index(Tab, AttrName) -> {aborted, R} | {atomic, ok}</name> + <fsummary>Create an index for a table. </fsummary> + <desc> + <p>Table indices can and should be used whenever the user + wants to frequently use some other field than the key field + to look up records. If this other field has an index + associated with it, these lookups can occur in constant time + and space. For example, if our application wishes to use + the age field of persons to efficiently find all person with + a specific age, it might be a good idea to have an index on + the age field. This can be accomplished with the following + call:</p> + <code type="none"> +mnesia:add_table_index(person, age) + </code> + <p>Indices do not come free, they occupy space which is + proportional to the size of the table. They also cause insertions + into the table to execute slightly slower. </p> + </desc> + </func> + <func> + <name>all_keys(Tab) -> KeyList | transaction abort</name> + <fsummary>Return all keys in a table.</fsummary> + <desc> + <p>This function returns a list of all keys in the table + named <c>Tab</c>. The semantics of this function is context + sensitive. See <c>mnesia:activity/4</c> for more information. In + transaction context it acquires a read lock on the entire + table.</p> + </desc> + </func> + <func> + <name>async_dirty(Fun, [, Args]) -> ResultOfFun | exit(Reason)</name> + <fsummary>Call the Fun in a context which is not protected by a transaction.</fsummary> + <desc> + <p>Call the <c>Fun</c> in a context which is not protected + by 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 + storage, or commit protocols involved. Checkpoint retainers + and indices are updated, but they will be updated dirty. As + for normal mnesia:dirty_* operations, the operations are + performed semi-asynchronously. See + <c>mnesia:activity/4</c> and the Mnesia User's Guide for + more details. + </p> + <p>It is possible to manipulate the Mnesia tables without + using transactions. This has some serious disadvantages, but + is considerably faster since the transaction manager is not + involved and no locks are set. A dirty operation does, + however, guarantee a certain level of consistency and it is + not possible for the dirty operations to return garbled + records. All dirty operations provide location transparency + to the programmer and a program does not have to be aware of + the whereabouts of a certain table in order to function. + </p> + <p><em>Note:</em>It is more than 10 times more efficient to read records dirty + than within a transaction. + </p> + <p>Depending on the application, it may be a good idea to use + the dirty functions for certain operations. Almost all + Mnesia functions which can be called within transactions + have a dirty equivalent which is much more + efficient. However, it must be noted that it is possible for + the database to be left in an inconsistent state if dirty + operations are used to update it. Dirty operations should + only be used for performance reasons when it is absolutely + necessary. </p> + <p><em>Note:</em> Calling (nesting) a <c>mnesia:[a]sync_dirty</c> + inside a transaction context will inherit the transaction semantics. + </p> + </desc> + </func> + <func> + <name>backup(Opaque [, BackupMod]) -> ok | {error,Reason}</name> + <fsummary>Back up all tables in the database.</fsummary> + <desc> + <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> + is obtained by <c>mnesia:system_info(backup_module)</c>.</p> + </desc> + </func> + <func> + <name>backup_checkpoint(Name, Opaque [, BackupMod]) -> ok | {error,Reason}</name> + <fsummary>Back up all tables in a checkpoint.</fsummary> + <desc> + <p>The tables are backed up to external media using the backup + module <c>BackupMod</c>. Tables with the local contents + property is being backed up as they exist on the current + node. <c>BackupMod</c> is the default backup callback + module obtained by + <c>mnesia:system_info(backup_module)</c>. See the User's + Guide about the exact callback interface (the + <c>mnesia_backup behavior</c>).</p> + </desc> + </func> + <func> + <name>change_config(Config, Value) -> {error, Reason} | {ok, ReturnValue}</name> + <fsummary>Change a configuration parameter.</fsummary> + <desc> + <p>The <c>Config</c> should be an atom of the following + configuration parameters: </p> + <taglist> + <tag><c>extra_db_nodes</c></tag> + <item> + <p><c>Value</c> is a list of nodes which Mnesia should try to connect to. + The <c>ReturnValue</c> will be those nodes in + <c>Value</c> that Mnesia are connected to. + <br></br> +Note: This function shall only be used to connect to newly started ram nodes + (N.D.R.S.N.) with an empty schema. If for example it is used after the network + have been partitioned it may lead to inconsistent tables. + <br></br> +Note: Mnesia may be connected to other nodes than those + returned in <c>ReturnValue</c>.</p> + </item> + <tag><c>dc_dump_limit</c></tag> + <item> + <p><c>Value</c> is a number. See description in + <c>Configuration Parameters</c> below. + The <c>ReturnValue</c> is the new value. Note this configuration parameter + is not persistent, it will be lost when mnesia stopped.</p> + </item> + </taglist> + </desc> + </func> + <func> + <name>change_table_access_mode(Tab, AccessMode) -> {aborted, R} | {atomic, ok}</name> + <fsummary>Change the access mode for the table.</fsummary> + <desc> + <p>The <c>AcccessMode</c> is by default the atom + <c>read_write</c> but it may also be set to the atom + <c>read_only</c>. If the <c>AccessMode</c> is set to + <c>read_only</c>, it means that it is not possible to perform + updates to the table. At startup Mnesia always loads + <c>read_only</c> tables locally regardless of when and if + Mnesia was terminated on other nodes.</p> + </desc> + </func> + <func> + <name>change_table_copy_type(Tab, Node, To) -> {aborted, R} | {atomic, ok}</name> + <fsummary>Change the storage type of a table.</fsummary> + <desc> + <p>For example:</p> + <code type="none"> +mnesia:change_table_copy_type(person, node(), disc_copies) + </code> + <p>Transforms our <c>person</c> table from a RAM table into + a disc based table at <c>Node</c>. + </p> + <p>This function can also be used to change the storage type of + the table named <c>schema</c>. The schema table can only + have <c>ram_copies</c> or <c>disc_copies</c> as the storage type. If the + storage type of the schema is <c>ram_copies</c>, no other table + can be disc resident on that node.</p> + </desc> + </func> + <func> + <name>change_table_load_order(Tab, LoadOrder) -> {aborted, R} | {atomic, ok}</name> + <fsummary>Change the load order priority for the table.</fsummary> + <desc> + <p>The <c>LoadOrder</c> priority is by default <c>0</c> (zero) + but may be set to any integer. The tables with the highest + <c>LoadOrder</c> priority will be loaded first at startup.</p> + </desc> + </func> + <func> + <name>clear_table(Tab) -> {aborted, R} | {atomic, ok}</name> + <fsummary>Deletes all entries in a table.</fsummary> + <desc> + <p>Deletes all entries in the table <c>Tab</c>.</p> + </desc> + </func> + <func> + <name>create_schema(DiscNodes) -> ok | {error,Reason}</name> + <fsummary>Create a brand new schema on the specified nodes.</fsummary> + <desc> + <p>Creates a new database on disc. Various files are + created in the local Mnesia directory of each node. Note + that the directory must be unique for each node. Two nodes + may never share the same directory. If possible, use a local + disc device in order 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 + Mnesia is running on anyone of the nodes, or if anyone of + the nodes already has a schema. Use + <c>mnesia:delete_schema/1</c> to get rid of old faulty + schemas. + </p> + <p><em>Note:</em> Only nodes with disc should be + included in <c>DiscNodes</c>. Disc-less nodes, that is nodes + where all tables including the schema only resides in RAM, + may not be included.</p> + </desc> + </func> + <func> + <name>create_table(Name, TabDef) -> {atomic, ok} | {aborted, Reason}</name> + <fsummary>Create a Mnesia table called <c>Name</c>with properties as described by the argument <c>TabDef</c>.</fsummary> + <desc> + <p>This function creates a Mnesia table called <c>Name</c> + according to the + argument <c>TabDef</c>. This list must be a list of + <c>{Item, Value}</c> tuples, where the following values are + allowed:</p> + <list type="bulleted"> + <item> + <p><c>{access_mode, Atom}</c>. The access mode is by + default the atom <c>read_write</c> but it may also be + set to the atom <c>read_only</c>. If the + <c>AccessMode</c> is set to <c>read_only</c>, it means + that it is not possible to perform updates to the table. + </p> + <p>At startup Mnesia always loads <c>read_only</c> tables + locally regardless of when and if Mnesia was terminated + on other nodes. This argument returns the access mode of + the table. The access mode may either be read_only or + read_write. + </p> + </item> + <item> + <p><c>{attributes, AtomList}</c> a list of the + attribute names for the records that are supposed to + populate the table. The default value is <c>[key, val]</c>. The table must have at least one extra + attribute in addition to the key. + </p> + <p>When accessing single attributes in a record, it is not + necessary, or even recommended, to hard code any + attribute names as atoms. Use the construct + <c>record_info(fields, RecordName)</c> instead. It can be + used for records of type <c>RecordName</c></p> + </item> + <item> + <p><c>{disc_copies, Nodelist}</c>, where + <c>Nodelist</c> is a list of the nodes where this table + is supposed to have disc copies. If a table replica is + of type <c>disc_copies</c>, all write operations on this + particular replica of the table are written to disc as + well as to the RAM copy of the table. + </p> + <p>It is possible + to have a replicated table of type <c>disc_copies</c> + on one node, and another type on another node. The + default value is <c>[]</c></p> + </item> + <item> + <p><c>{disc_only_copies, Nodelist}</c>, where + <c>Nodelist</c> is a list of the nodes where this table + is supposed to have <c>disc_only_copies</c>. A disc only + table replica is kept on disc only and unlike the other + replica types, the contents of the replica will not + reside in RAM. These replicas are considerably slower + than replicas held in RAM. + </p> + </item> + <item> + <p><c>{index, Intlist}</c>, where + <c>Intlist</c> is a list of attribute names (atoms) or + record fields for which Mnesia shall build and maintain + an extra index table. The <c>qlc</c> query compiler may + or may not utilize any additional indices while + processing queries on a table. + </p> + </item> + <item> + <p><c>{load_order, Integer}</c>. The load order + priority is by default <c>0</c> (zero) but may be set to + any integer. The tables with the highest load order + priority will be loaded first at startup. + </p> + </item> + <item> + <p><c>{ram_copies, Nodelist}</c>, where + <c>Nodelist</c> is a list of the nodes where this table + is supposed to have RAM copies. A table replica of type + <c>ram_copies</c> is obviously not written to disc on a + per transaction basis. It is possible to dump + <c>ram_copies</c> replicas to disc with the function + <c>mnesia:dump_tables(Tabs)</c>. The default value for + this attribute is <c>[node()]</c>. + </p> + </item> + <item> + <p><c>{record_name, Name}</c>, where <c>Name</c> must + be an atom. All records, stored in the table, must have + this name as the first element. It defaults to the same + name as the name of the table. + </p> + </item> + <item> + <p><c>{snmp, SnmpStruct}</c>. See + <c>mnesia:snmp_open_table/2</c> for a description of + <c>SnmpStruct</c>. If this attribute is present in the + <c>ArgList</c> to <c>mnesia:create_table/2</c>, the + table is immediately accessible by means of the Simple + Network Management Protocol (SNMP). This means that + applications which use SNMP to manipulate and control + the system can be designed easily, since Mnesia provides + a direct mapping between the logical tables that make up + an SNMP control application and the physical data which + makes up a Mnesia table. + </p> + </item> + <item> + <p><c>{type, Type}</c>, where <c>Type</c> must be + either of the atoms <c>set</c>, <c>ordered_set</c> or + <c>bag</c>. The default value is <c>set</c>. In a + <c>set</c> all records have unique keys and in a + <c>bag</c> several records may have the same key, but + the record content is unique. If a non-unique record is + stored the old, conflicting record(s) will simply be + overwritten. Note: currently 'ordered_set' + is not supported for 'disc_only_copies'. + </p> + </item> + <item> + <p><c>{local_content, Bool}</c>, where <c>Bool</c> must be + either <c>true</c> or <c>false</c>. The default value is <c>false</c>.\011 </p> + </item> + </list> + <p>For example, the following call creates the <c>person</c> table + previously defined and replicates it on 2 nodes: + </p> + <code type="none"> +mnesia:create_table(person, + [{ram_copies, [N1, N2]}, + {attributes, record_info(fields,person)}]). + </code> + <p>If it was required that Mnesia build and maintain an extra index + table on the <c>address</c> attribute of all the <c>person</c> + records that are inserted in the table, the following code would be issued: + </p> + <code type="none"> +mnesia:create_table(person, + [{ram_copies, [N1, N2]}, + {index, [address]}, + {attributes, record_info(fields,person)}]). + </code> + <p>The specification of <c>index</c> and <c>attributes</c> may be + hard coded as <c>{index, [2]}</c> and + <c>{attributes, [name, age, address, salary, children]}</c> + respectively. + </p> + <p><c>mnesia:create_table/2</c> writes records into the + <c>schema</c> table. This function, as well as all other + schema manipulation functions, are implemented with the + normal transaction management system. This guarantees that + schema updates are performed on all nodes in an atomic + manner.</p> + </desc> + </func> + <func> + <name>deactivate_checkpoint(Name) -> ok | {error, Reason}</name> + <fsummary>Deactivate a checkpoint.</fsummary> + <desc> + <p>The checkpoint is automatically deactivated when some of + the tables involved have no retainer attached to them. This may + happen when nodes go down or when a replica is deleted. + Checkpoints will also be deactivated with this function. + <c>Name</c> is the name of an active checkpoint.</p> + </desc> + </func> + <func> + <name>del_table_copy(Tab, Node) -> {aborted, R} | {atomic, ok}</name> + <fsummary>Delete the replica of table <c>Tab</c>at node <c>Node</c>.</fsummary> + <desc> + <p>Deletes the replica of table <c>Tab</c> at node <c>Node</c>. + When the last replica is deleted with this + function, the table disappears entirely. + </p> + <p>This function may also be used to delete a replica of + the table named <c>schema</c>. Then the mnesia node will be removed. + Note: Mnesia must be stopped on the node first.</p> + </desc> + </func> + <func> + <name>del_table_index(Tab, AttrName) -> {aborted, R} | {atomic, ok}</name> + <fsummary>Delete an index in a table. </fsummary> + <desc> + <p>This function deletes the index on attribute with name + <c>AttrName</c> in a table.</p> + </desc> + </func> + <func> + <name>delete({Tab, Key}) -> transaction abort | ok </name> + <fsummary>Delete all records in table <c>Tab</c>with the key <c>Key</c>.</fsummary> + <desc> + <p>Invokes <c>mnesia:delete(Tab, Key, write)</c></p> + </desc> + </func> + <func> + <name>delete(Tab, Key, LockKind) -> transaction abort | ok </name> + <fsummary>Delete all records in table <c>Tab</c>with the key <c>Key</c>.</fsummary> + <desc> + <p>Deletes all records in table <c>Tab</c> with the key + <c>Key</c>. + </p> + <p>The semantics of this function is context sensitive. See + <c>mnesia:activity/4</c> for more information. In transaction + context it acquires a lock of type <c>LockKind</c> in the + record. Currently the lock types <c>write</c> and + <c>sticky_write</c> are supported.</p> + </desc> + </func> + <func> + <name>delete_object(Record) -> transaction abort | ok </name> + <fsummary>Delete a record</fsummary> + <desc> + <p>Invokes <c>mnesia:delete_object(Tab, Record, write)</c> where + <c>Tab</c> is <c>element(1, Record)</c>.</p> + </desc> + </func> + <func> + <name>delete_object(Tab, Record, LockKind) -> transaction abort | ok </name> + <fsummary>Delete a record</fsummary> + <desc> + <p>If a table is of type <c>bag</c>, we may sometimes + want to delete only some of the records with a certain + key. This can be done with the <c>delete_object/3</c> + function. A complete record must be supplied to this + function. + </p> + <p>The semantics of this function is context sensitive. See + <c>mnesia:activity/4</c> for more information. In transaction + context it acquires a lock of type <c>LockKind</c> on the + record. Currently the lock types <c>write</c> and + <c>sticky_write</c> are supported.</p> + </desc> + </func> + <func> + <name>delete_schema(DiscNodes) -> ok | {error,Reason}</name> + <fsummary>Delete the schema on the given nodes</fsummary> + <desc> + <p>Deletes a database created with + <c>mnesia:create_schema/1</c>. + <c>mnesia:delete_schema/1</c> fails if any of the Erlang + nodes given as <c>DiscNodes</c> is not alive, or if Mnesia + is running on any of the nodes. + </p> + <p>After the database has been deleted, it may still be + possible to start Mnesia as a disc-less node. This depends on + how the configuration parameter <c>schema_location</c> is set. + </p> + <warning> + <p>This function must be used with extreme + caution since it makes existing persistent data + obsolete. Think twice before using it. </p> + </warning> + </desc> + </func> + <func> + <name>delete_table(Tab) -> {aborted, Reason} | {atomic, ok} </name> + <fsummary>Delete permanently all replicas of table <c>Tab</c>.</fsummary> + <desc> + <p>Permanently deletes all replicas of table <c>Tab</c>.</p> + </desc> + </func> + <func> + <name>dirty_all_keys(Tab) -> KeyList | exit({aborted, Reason}).</name> + <fsummary>Dirty search for all record keys in table.</fsummary> + <desc> + <p>This is the dirty equivalent of the + <c>mnesia:all_keys/1</c> function.</p> + </desc> + </func> + <func> + <name>dirty_delete({Tab, Key}) -> ok | exit({aborted, Reason}) </name> + <fsummary>Dirty delete of a record.</fsummary> + <desc> + <p>Invokes <c>mnesia:dirty_delete(Tab, Key)</c>.</p> + </desc> + </func> + <func> + <name>dirty_delete(Tab, Key) -> ok | exit({aborted, Reason}) </name> + <fsummary>Dirty delete of a record. </fsummary> + <desc> + <p>This is the dirty equivalent of the + <c>mnesia:delete/3</c> function.</p> + </desc> + </func> + <func> + <name>dirty_delete_object(Record) </name> + <fsummary>Dirty delete of a record.</fsummary> + <desc> + <p>Invokes <c>mnesia:dirty_delete_object(Tab, Record)</c> + where <c>Tab</c> is <c>element(1, Record)</c>.</p> + </desc> + </func> + <func> + <name>dirty_delete_object(Tab, Record) </name> + <fsummary>Dirty delete of a record. </fsummary> + <desc> + <p>This is the dirty equivalent of the + <c>mnesia:delete_object/3</c> function.</p> + </desc> + </func> + <func> + <name>dirty_first(Tab) -> Key | exit({aborted, Reason}) </name> + <fsummary>Return the key for the first record in a table.</fsummary> + <desc> + <p>Records in <c>set</c> or <c>bag</c> tables are not ordered. + However, there + is an ordering of the records which is not known + to the user. Accordingly, it is possible to traverse a table by means + of this function in conjunction with the <c>mnesia:dirty_next/2</c> + function. + </p> + <p>If there are no records at all in the table, this function + returns the atom <c>'$end_of_table'</c>. For this reason, it + is highly undesirable, but not disallowed, to use this atom + as the key for any user records.</p> + </desc> + </func> + <func> + <name>dirty_index_match_object(Pattern, Pos)</name> + <fsummary>Dirty pattern match using index.</fsummary> + <desc> + <p>Invokes <c>mnesia:dirty_index_match_object(Tab, Pattern, Pos)</c> where <c>Tab</c> is <c>element(1, Pattern)</c>.</p> + </desc> + </func> + <func> + <name>dirty_index_match_object(Tab, Pattern, Pos)</name> + <fsummary>Dirty pattern match using index.</fsummary> + <desc> + <p>This is the dirty equivalent of the + <c>mnesia:index_match_object/4</c> function.</p> + </desc> + </func> + <func> + <name>dirty_index_read(Tab, SecondaryKey, Pos)</name> + <fsummary>Dirty read using index.</fsummary> + <desc> + <p>This is the dirty equivalent of the + <c>mnesia:index_read/3</c> function.</p> + </desc> + </func> + <func> + <name>dirty_last(Tab) -> Key | exit({aborted, Reason}) </name> + <fsummary>Return the key for the last record in a table.</fsummary> + <desc> + <p>This function works exactly + <c>mnesia:dirty_first/1</c> but returns the last object in + Erlang term order for the <c>ordered_set</c> table type. For + all other table types, <c>mnesia:dirty_first/1</c> and + <c>mnesia:dirty_last/1</c> are synonyms.</p> + </desc> + </func> + <func> + <name>dirty_match_object(Pattern) -> RecordList | exit({aborted, Reason}).</name> + <fsummary>Dirty pattern match pattern.</fsummary> + <desc> + <p>Invokes <c>mnesia:dirty_match_object(Tab, Pattern)</c> + where <c>Tab</c> is <c>element(1, Pattern)</c>.</p> + </desc> + </func> + <func> + <name>dirty_match_object(Tab, Pattern) -> RecordList | exit({aborted, Reason}).</name> + <fsummary>Dirty pattern match pattern.</fsummary> + <desc> + <p>This is the dirty equivalent of the + <c>mnesia:match_object/3</c> function.</p> + </desc> + </func> + <func> + <name>dirty_next(Tab, Key) -> Key | exit({aborted, Reason}) </name> + <fsummary>Return the next key in a table. </fsummary> + <desc> + <p>This function makes it possible to traverse a table + and perform operations on all records in the table. When + the end of the table is reached, the special key + <c>'$end_of_table'</c> is returned. Otherwise, the function + returns a key which can be used to read the actual record.The + behavior is undefined if another Erlang process performs write + operations on the table while it is being traversed with the + <c>mnesia:dirty_next/2</c> function.</p> + </desc> + </func> + <func> + <name>dirty_prev(Tab, Key) -> Key | exit({aborted, Reason}) </name> + <fsummary>Return the previous key in a table. </fsummary> + <desc> + <p>This function works exactly + <c>mnesia:dirty_next/2</c> but returns the previous object in + Erlang term order for the ordered_set table type. For + all other table types, <c>mnesia:dirty_next/2</c> and + <c>mnesia:dirty_prev/2</c> are synonyms.\011 </p> + </desc> + </func> + <func> + <name>dirty_read({Tab, Key}) -> ValueList | exit({aborted, Reason}</name> + <fsummary>Dirty read of records.</fsummary> + <desc> + <p>Invokes <c>mnesia:dirty_read(Tab, Key)</c>.</p> + </desc> + </func> + <func> + <name>dirty_read(Tab, Key) -> ValueList | exit({aborted, Reason}</name> + <fsummary>Dirty read of records.</fsummary> + <desc> + <p>This is the dirty equivalent of the + <c>mnesia:read/3</c> function.</p> + </desc> + </func> + <func> + <name>dirty_select(Tab, MatchSpec) -> ValueList | exit({aborted, Reason}</name> + <fsummary>Dirty match the objects in <c>Tab</c>against <c>MatchSpec</c>.</fsummary> + <desc> + <p>This is the dirty equivalent of the + <c>mnesia:select/2</c> function.</p> + </desc> + </func> + <func> + <name>dirty_slot(Tab, Slot) -> RecordList | exit({aborted, Reason})</name> + <fsummary>Return the list of records that are associated with Slot in a table.</fsummary> + <desc> + <p>This function can be used to traverse a table in a + manner similar to the <c>mnesia:dirty_next/2</c> function. + A table has a number of slots which range from 0 (zero) to some + unknown upper bound. The function + <c>mnesia:dirty_slot/2</c> returns the special atom + <c>'$end_of_table'</c> when the end of the table is reached. + The behavior of this function is undefined if a write + operation is performed on the table while it is being + traversed.</p> + </desc> + </func> + <func> + <name>dirty_update_counter({Tab, Key}, Incr) -> NewVal | exit({aborted, Reason})</name> + <fsummary>Dirty update of a counter record.</fsummary> + <desc> + <p>Invokes <c>mnesia:dirty_update_counter(Tab, Key, Incr)</c>.</p> + </desc> + </func> + <func> + <name>dirty_update_counter(Tab, Key, Incr) -> NewVal | exit({aborted, Reason})</name> + <fsummary>Dirty update of a counter record.</fsummary> + <desc> + <p>There are no special counter records in Mnesia. 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 or negative number. However, counters can never become less + than zero. There are two significant differences between + this function and the action of first reading the record, + performing the arithmetics, and then writing the record:</p> + <list type="bulleted"> + <item>It is much more efficient</item> + <item><c>mnesia:dirty_update_counter/3</c> is + performed as an atomic operation despite the fact that it is not + protected by a transaction.</item> + </list> + <p>If two processes perform <c>mnesia:dirty_update_counter/3</c> + simultaneously, both updates will take effect without the + risk of loosing one of the updates. The new value + <c>NewVal</c> of the counter is returned.</p> + <p>If <c>Key</c> don't exits, a new record is created with the value + <c>Incr</c> if it is larger than 0, otherwise it is set to 0.</p> + </desc> + </func> + <func> + <name>dirty_write(Record) -> ok | exit({aborted, Reason})</name> + <fsummary>Dirty write of a record.</fsummary> + <desc> + <p>Invokes <c>mnesia:dirty_write(Tab, Record)</c> + where <c>Tab</c> is <c>element(1, Record)</c>.</p> + </desc> + </func> + <func> + <name>dirty_write(Tab, Record) -> ok | exit({aborted, Reason})</name> + <fsummary>Dirty write of a record.</fsummary> + <desc> + <p>This is the dirty equivalent of <c>mnesia:write/3</c>.</p> + </desc> + </func> + <func> + <name>dump_log() -> dumped</name> + <fsummary>Perform a user initiated dump of the local log file.</fsummary> + <desc> + <p>Performs a user initiated dump of the local log file. + This is usually not necessary since Mnesia, by default, + manages this automatically.</p> + </desc> + </func> + <func> + <name>dump_tables(TabList) -> {atomic, ok} | {aborted, Reason}</name> + <fsummary>Dump all RAM tables to disc.</fsummary> + <desc> + <p>This function dumps a set of <c>ram_copies</c> tables + to disc. The next time the system is started, these tables + are initiated with the data found in the files that are the + result of this dump. None of the tables may have disc + resident replicas.</p> + </desc> + </func> + <func> + <name>dump_to_textfile(Filename) </name> + <fsummary>Dump local tables into a text file.</fsummary> + <desc> + <p>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 be reloaded with + <c>mnesia:load_textfile/1</c>. Only use this function for + educational purposes. Use other functions to deal with real + backups.</p> + </desc> + </func> + <func> + <name>error_description(Error) -> String </name> + <fsummary>Return a string describing a particular Mnesia error.</fsummary> + <desc> + <p>All Mnesia transactions, including all the schema + update functions, either return the value <c>{atomic, Val}</c> or the tuple <c>{aborted, Reason}</c>. The + <c>Reason</c> can be either of the following atoms. The + <c>error_description/1</c> function returns a descriptive + string which describes the error. + </p> + <list type="bulleted"> + <item> + <p><c>nested_transaction</c>. Nested transactions are + not allowed in this context. + </p> + </item> + <item> + <p><c>badarg</c>. Bad or invalid argument, possibly + bad type. + </p> + </item> + <item> + <p><c>no_transaction</c>. Operation not allowed + outside transactions. + </p> + </item> + <item> + <p><c>combine_error</c>. Table options were illegally + combined. + </p> + </item> + <item> + <p><c>bad_index</c>. Index already exists or was out + of bounds. + </p> + </item> + <item> + <p><c>already_exists</c>. Schema option is already set. + </p> + </item> + <item> + <p><c>index_exists</c>. Some operations cannot be performed on + tabs with index. + </p> + </item> + <item> + <p><c>no_exists</c>. Tried to perform operation on + non-existing, or not alive, item. + </p> + </item> + <item> + <p><c>system_limit</c>. Some system_limit was exhausted. + </p> + </item> + <item> + <p><c>mnesia_down</c>. A transaction involving + records at some remote node which died while + transaction was executing. Record(s) are no longer + available elsewhere in the network. + </p> + </item> + <item> + <p><c>not_a_db_node</c>. A node which does not exist + in the schema was mentioned. + </p> + </item> + <item> + <p><c>bad_type</c>. Bad type on some arguments. + </p> + </item> + <item> + <p><c>node_not_running</c>. Node not running. + </p> + </item> + <item> + <p><c>truncated_binary_file</c>. Truncated binary in file. + </p> + </item> + <item> + <p><c>active</c>. Some delete operations require that + all active records are removed. + </p> + </item> + <item> + <p><c>illegal</c>. Operation not supported on record. + </p> + </item> + </list> + <p>The <c>Error</c> may be <c>Reason</c>, + <c>{error, Reason}</c>, or <c>{aborted, Reason}</c>. The + <c>Reason</c> may be an atom or a tuple with <c>Reason</c> + as an atom in the first field.</p> + </desc> + </func> + <func> + <name>ets(Fun, [, Args]) -> ResultOfFun | exit(Reason)</name> + <fsummary>Call the Fun in a raw context which is not protected by a transaction.</fsummary> + <desc> + <p>Call the <c>Fun</c> in a raw context which is not protected by + a transaction. The Mnesia function call is performed in the + <c>Fun</c> are performed directly on the local <c>ets</c> 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 + not updated, but it is extremely fast. This function can + also be applied to <c>disc_copies</c> tables if all + operations are read only. See <c>mnesia:activity/4</c> + and the Mnesia User's Guide for more details.</p> + <p><em>Note:</em> Calling (nesting) a <c>mnesia:ets</c> + inside a transaction context will inherit the transaction semantics.</p> + </desc> + </func> + <func> + <name>first(Tab) -> Key | transaction abort </name> + <fsummary>Return the key for the first record in a table.</fsummary> + <desc> + <p>Records in <c>set</c> or <c>bag</c> tables are not ordered. + However, there + is an ordering of the records which is not known + to the user. Accordingly, it is possible to traverse a table by means + of this function in conjunction with the <c>mnesia:next/2</c> + function. + </p> + <p>If there are no records at all in the table, this function + returns the atom <c>'$end_of_table'</c>. For this reason, it + is highly undesirable, but not disallowed, to use this atom + as the key for any user records.</p> + </desc> + </func> + <func> + <name>foldl(Function, Acc, Table) -> NewAcc | transaction abort </name> + <fsummary>Call Function for each record in Table </fsummary> + <desc> + <p>Iterates over the table <c>Table</c> and calls + <c>Function(Record, NewAcc)</c> for each <c>Record</c> in the table. + The term returned from <c>Function</c> will be used as the second + argument in the next call to the <c>Function</c>. + </p> + <p><c>foldl</c> returns the same term as the last call to + <c>Function</c> returned.</p> + </desc> + </func> + <func> + <name>foldr(Function, Acc, Table) -> NewAcc | transaction abort </name> + <fsummary>Call Function for each record in Table </fsummary> + <desc> + <p>This function works exactly as + <c>foldl/3</c> but iterates the table in the opposite order + for the <c>ordered_set</c> table type. For + all other table types, <c>foldr/3</c> and + <c>foldl/3</c> are synonyms.</p> + </desc> + </func> + <func> + <name>force_load_table(Tab) -> yes | ErrorDescription </name> + <fsummary>Force a table to be loaded into the system </fsummary> + <desc> + <p>The Mnesia algorithm for table load might lead to a + situation where a table cannot be loaded. This situation + 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 due to a system crash. + </p> + <p>If this situation is not acceptable, this function can be + used to override the strategy of the Mnesia table load + algorithm. This could lead to a situation where some + transaction effects are lost with a inconsistent database as + result, but for some applications high availability is more + important than consistent data.</p> + </desc> + </func> + <func> + <name>index_match_object(Pattern, Pos) -> transaction abort | ObjList</name> + <fsummary>Match records and utilizes index information.</fsummary> + <desc> + <p>Invokes <c>mnesia:index_match_object(Tab, Pattern, Pos, read)</c> where <c>Tab</c> is <c>element(1, Pattern)</c>.</p> + </desc> + </func> + <func> + <name>index_match_object(Tab, Pattern, Pos, LockKind) -> transaction abort | ObjList</name> + <fsummary>Match records and utilizes index information.</fsummary> + <desc> + <p>In a manner similar to the <c>mnesia:index_read/3</c> + function, we can also utilize any index information when we + try to match records. This function takes a pattern which + obeys the same rules as the <c>mnesia:match_object/3</c> + function with the exception that this function requires the + following conditions: + </p> + <list type="bulleted"> + <item> + <p>The table <c>Tab</c> must have an index on + position <c>Pos</c>. + </p> + </item> + <item> + <p>The element in position <c>Pos</c> in + <c>Pattern</c> must be bound. <c>Pos</c> may either be + an integer (#record.Field), or an attribute name.</p> + </item> + </list> + <p>The two index search functions described here are + automatically invoked when searching tables with <c>qlc</c> + list comprehensions and also when using the low level + <c>mnesia:[dirty_]match_object</c> functions. + </p> + <p></p> + <p>The semantics of this function is context sensitive. See + <c>mnesia:activity/4</c> for more information. In transaction + context it acquires a lock of type <c>LockKind</c> on the + entire table or on a single record. Currently, the lock type + <c>read</c> is supported. + </p> + </desc> + </func> + <func> + <name>index_read(Tab, SecondaryKey, Pos) -> transaction abort | RecordList </name> + <fsummary>Read records via index table. </fsummary> + <desc> + <p>Assume there is an index on position <c>Pos</c> for a + certain record type. This function can be used to read the + records without knowing the actual key for the record. For + example, with an index in position 1 of the <c>person</c> + table, the call <c>mnesia:index_read(person, 36, #person.age)</c> returns a list of all persons with age + equal to 36. <c>Pos</c> may also be an attribute name + (atom), but if the notation <c>mnesia:index_read(person, 36, age)</c> is used, the field position will be searched for in + runtime, for each call. + </p> + <p>The semantics of this function is context sensitive. See + <c>mnesia:activity/4</c> for more information. In transaction + context it acquires a read lock on the entire table.</p> + </desc> + </func> + <func> + <name>info() -> ok </name> + <fsummary>Print some information about the system on the tty.</fsummary> + <desc> + <p>Prints some information about the system on the tty. + This function may be used even if Mnesia is not started. + However, more information will be displayed if Mnesia is + started.</p> + </desc> + </func> + <func> + <name>install_fallback(Opaque) -> ok | {error,Reason}</name> + <fsummary>Install a backup as fallback.</fsummary> + <desc> + <p>Invokes <c>mnesia:install_fallback(Opaque, Args)</c> where + <c>Args</c> is <c>[{scope, global}]</c>.</p> + </desc> + </func> + <func> + <name>install_fallback(Opaque), BackupMod) -> ok | {error,Reason}</name> + <fsummary>Install a backup as fallback.</fsummary> + <desc> + <p>Invokes <c>mnesia:install_fallback(Opaque, Args)</c> where + <c>Args</c> is <c>[{scope, global}, {module, BackupMod}]</c>.</p> + </desc> + </func> + <func> + <name>install_fallback(Opaque, Args) -> ok | {error,Reason}</name> + <fsummary>Install a backup as fallback.</fsummary> + <desc> + <p>This function is used to install a backup as fallback. The + fallback will be used to restore the database at the next + start-up. Installation of fallbacks requires Erlang to be up + and running on all the involved nodes, but it does not + matter if Mnesia is running or not. The installation of the + fallback will fail if the local node is not one of the disc + resident nodes in the backup. + </p> + <p><c>Args</c> is a list of the following tuples: + </p> + <list type="bulleted"> + <item> + <p><c>{module, BackupMod}</c>. + All accesses of the backup media is performed via a + callback module named <c>BackupMod</c>. The + <c>Opaque</c> argument is forwarded to the callback + module which may interpret it as it wish. The default + callback module is called <c>mnesia_backup</c> and it + interprets the <c>Opaque</c> argument as a local + filename. The default for this module is also + configurable via the <c>-mnesia mnesia_backup</c> + configuration parameter. </p> + </item> + <item> + <p><c>{scope, Scope}</c> + The <c>Scope</c> of a fallback may either be + <c>global</c> for the entire database or <c>local</c> + for one node. By default, the installation of a fallback + is a global operation which either is performed all + nodes with disc resident schema or none. Which nodes + that are disc resident or not, is determined from the + schema info in the backup.</p> + <p>If the <c>Scope</c> of the operation is <c>local</c> + the fallback will only be installed on the local node.</p> + </item> + <item> + <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 towards the Mnesia directory + as configured with the <c>-mnesia dir</c> configuration + parameter. But by explicitly supplying an + <c>AlternateDir</c> the fallback will be installed there + regardless of the Mnesia directory configuration + parameter setting. After installation of a fallback on + an alternate Mnesia directory that directory is fully + prepared for usage as an active Mnesia directory. + </p> + <p>This is a somewhat dangerous feature which must be + used with care. By unintentional mixing of directories + you may easily end up with a inconsistent database, if + the same backup is installed on more than one directory.</p> + </item> + </list> + </desc> + </func> + <func> + <name>is_transaction() -> boolean </name> + <fsummary>Check if code is running in a transaction.</fsummary> + <desc> + <p>When this function is executed inside a transaction context + it returns <c>true</c>, otherwise <c>false</c>.</p> + </desc> + </func> + <func> + <name>last(Tab) -> Key | transaction abort </name> + <fsummary>Return the key for the last record in a table.</fsummary> + <desc> + <p>This function works exactly + <c>mnesia:first/1</c> but returns the last object in + Erlang term order for the <c>ordered_set</c> table type. For + all other table types, <c>mnesia:first/1</c> and + <c>mnesia:last/1</c> are synonyms.</p> + </desc> + </func> + <func> + <name>load_textfile(Filename)</name> + <fsummary>Load tables from a text file.</fsummary> + <desc> + <p>Loads a series of definitions and data found in the + text file (generated with <c>mnesia:dump_to_textfile/1</c>) + into Mnesia. This function also starts Mnesia and possibly + creates a new schema. This function is intended for + educational purposes only and using other functions to deal + with real backups, is recommended.</p> + </desc> + </func> + <func> + <name>lock(LockItem, LockKind) -> Nodes | ok | transaction abort</name> + <fsummary>Explicit grab lock.</fsummary> + <desc> + <p>Write locks are normally acquired on all nodes where a + replica of the table resides (and is active). Read locks are + acquired on one node (the local node if a local + replica exists). Most of the context sensitive access functions + acquire an implicit lock if they are invoked in a + transaction context. The granularity of a lock may either + be a single record or an entire table. + </p> + <p>The normal usage is to call the function without checking + the return value since it exits if it fails and the + transaction is restarted by the transaction manager. It + returns all the locked nodes if a write lock is acquired, and + <c>ok</c> if it was a read lock. + </p> + <p>This function <c>mnesia:lock/2</c> is intended to support + explicit locking on tables but also intended for situations + when locks need to be acquired regardless of how tables are + replicated. Currently, two <c>LockKind</c>'s are supported: + </p> + <taglist> + <tag><c>write</c></tag> + <item> + <p>Write locks are exclusive, which means that if one + transaction manages to acquire a write lock on an item, + no other transaction may acquire any kind of lock on the + same item. + </p> + </item> + <tag><c>read</c></tag> + <item> + <p>Read locks may be shared, which means that if one + transaction manages to acquire a read lock on an item, + other transactions may also acquire a read lock on the + same item. However, if someone has a read lock no one can + acquire a write lock at the same item. If some one has a + write lock no one can acquire a read lock nor + a write lock at the same item.</p> + </item> + </taglist> + <p>Conflicting lock requests are automatically queued if there + is no risk of a deadlock. Otherwise the transaction must be + aborted and executed again. Mnesia does this automatically + as long as the upper limit of maximum <c>retries</c> is not + reached. See <c>mnesia:transaction/3</c> for the details. + </p> + <p>For the sake of completeness sticky write locks will also + be described here even if a sticky write lock is not + supported by this particular function: + </p> + <taglist> + <tag><c>sticky_write</c></tag> + <item> + <p>Sticky write locks are a mechanism which can be used + to optimize write lock acquisition. If your application + uses replicated tables mainly for fault tolerance (as + opposed to read access optimization purpose), sticky + locks may be the best option available. + </p> + <p>When a sticky write lock is acquired, all nodes will be + informed which node is locked. Subsequently, + sticky lock requests from the same node will be + performed as a local operation without any + communication with other nodes. The sticky lock + lingers on the node even after the transaction has + ended. See the Mnesia User's Guide for more information.</p> + </item> + </taglist> + <p>Currently, two kinds of <c>LockItem</c>'s are supported by + this function: + </p> + <taglist> + <tag><c>{table, Tab}</c></tag> + <item> + <p>This acquires a lock of type <c>LockKind</c> on the + entire table <c>Tab</c>. + </p> + </item> + <tag><c>{global, GlobalKey, Nodes}</c></tag> + <item> + <p>This acquires a lock of type <c>LockKind</c> on the + global resource <c>GlobalKey</c>. The lock is acquired + on all active nodes in the <c>Nodes</c> list. </p> + </item> + </taglist> + <p>Locks are released when the outermost transaction ends. + </p> + <p>The semantics of this function is context sensitive. See + <c>mnesia:activity/4</c> for more information. In transaction + context it acquires locks otherwise it just ignores the + request.</p> + </desc> + </func> + <func> + <name>match_object(Pattern) ->transaction abort | RecList </name> + <fsummary>Match <c>Pattern</c>for records. </fsummary> + <desc> + <p>Invokes <c>mnesia:match_object(Tab, Pattern, read)</c> where + <c>Tab</c> is <c>element(1, Pattern)</c>.</p> + </desc> + </func> + <func> + <name>match_object(Tab, Pattern, LockKind) ->transaction abort | RecList </name> + <fsummary>Match <c>Pattern</c>for records. </fsummary> + <desc> + <p>This function takes a pattern with 'don't care' variables + denoted as a '_' parameter. This function returns a list of + records which matched the pattern. Since the second element + of a record in a table is considered to be the key for the + record, the performance of this function depends on whether + this key is bound or not. + </p> + <p>For example, the call <c>mnesia:match_object(person, {person, '_', 36, '_', '_'}, read)</c> returns a list of all person records with an + age field of thirty-six (36). + </p> + <p>The function <c>mnesia:match_object/3</c> + automatically uses indices if these exist. However, no + heuristics are performed in order to select the best + index. + </p> + <p>The semantics of this function is context sensitive. See + <c>mnesia:activity/4</c> for more information. In transaction + context it acquires a lock of type <c>LockKind</c> on the + entire table or a single record. Currently, the lock type + <c>read</c> is supported.</p> + </desc> + </func> + <func> + <name>move_table_copy(Tab, From, To) -> {aborted, Reason} | {atomic, ok}</name> + <fsummary>Move the copy of table <c>Tab</c>from node<c>From</c>to node <c>To</c>.</fsummary> + <desc> + <p>Moves the copy of table <c>Tab</c> from node + <c>From</c> to node <c>To</c>. + </p> + <p>The storage type is preserved. For example, a RAM table + moved from one node remains a RAM on the new node. It is + still possible for other transactions to read and write in + the table while it is being moved. + </p> + <p>This function cannot be used on <c>local_content</c> tables.</p> + </desc> + </func> + <func> + <name>next(Tab, Key) -> Key | transaction abort </name> + <fsummary>Return the next key in a table. </fsummary> + <desc> + <p>This function makes it possible to traverse a table + and perform operations on all records in the table. When + the end of the table is reached, the special key + <c>'$end_of_table'</c> is returned. Otherwise, the function + returns a key which can be used to read the actual record.</p> + </desc> + </func> + <func> + <name>prev(Tab, Key) -> Key | transaction abort </name> + <fsummary>Return the previous key in a table. </fsummary> + <desc> + <p>This function works exactly + <c>mnesia:next/2</c> but returns the previous object in + Erlang term order for the ordered_set table type. For + all other table types, <c>mnesia:next/2</c> and + <c>mnesia:prev/2</c> are synonyms.\011 </p> + </desc> + </func> + <func> + <name>read({Tab, Key}) -> transaction abort | RecordList </name> + <fsummary>Read records(s) with a given key. </fsummary> + <desc> + <p>Invokes <c>mnesia:read(Tab, Key, read)</c>.</p> + </desc> + </func> + <func> + <name>read(Tab, Key) -> transaction abort | RecordList </name> + <fsummary>Read records(s) with a given key. </fsummary> + <desc> + <p>Invokes <c>mnesia:read(Tab, Key, read)</c>.</p> + </desc> + </func> + <func> + <name>read(Tab, Key, LockKind) -> transaction abort | RecordList </name> + <fsummary>Read records(s) with a given key. </fsummary> + <desc> + <p>This function reads all records from table <c>Tab</c> with + key <c>Key</c>. This function has the same semantics + regardless of the location of <c>Tab</c>. If the table is + of type <c>bag</c>, the <c>mnesia:read(Tab, Key)</c> can + return an arbitrarily long list. If the table is of type + <c>set</c>, the list is either of length 1, or <c>[]</c>. + </p> + <p>The semantics of this function is context sensitive. See + <c>mnesia:activity/4</c> for more information. In transaction + context it acquires a lock of type + <c>LockKind</c>. Currently, the lock types <c>read</c>, + <c>write</c> and <c>sticky_write</c> are supported. + </p> + <p>If the user wants to update the record it is more efficient to + use <c>write/sticky_write</c> as the LockKind. + </p> + </desc> + </func> + <func> + <name>read_lock_table(Tab) -> ok | transaction abort</name> + <fsummary>Set a read lock on an entire table.</fsummary> + <desc> + <p>Invokes <c>mnesia:lock({table, Tab}, read)</c>.</p> + </desc> + </func> + <func> + <name>report_event(Event) -> ok</name> + <fsummary>Report a user event to Mnesia's event handler.</fsummary> + <desc> + <p>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. + </p> + <p>Whenever the application begins a + new and demanding Mnesia task, or if it is entering a new + interesting phase in its execution, it may be a good idea to + use <c>mnesia:report_event/1</c>. The <c>Event</c> may be + any term and generates a <c>{mnesia_user, Event}</c> event + for any processes that subscribe to Mnesia system + events.</p> + </desc> + </func> + <func> + <name>restore(Opaque, Args) -> {atomic, RestoredTabs} |{aborted, Reason}</name> + <fsummary>Online restore of backup.</fsummary> + <desc> + <p>With this function, tables may be restored online from a + 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"> + <item> + <p><c>{module,BackupMod}</c> The backup module + <c>BackupMod</c> will be used to access the backup + media. If omitted, the default backup module will be + used. + </p> + </item> + <item><c>{skip_tables, TabList}</c> Where <c>TabList</c> + is a list of tables which should not be read from the + backup. + </item> + <item><c>{clear_tables, TabList}</c> Where + <c>TabList</c> is a list of tables which should be + cleared, before the records from the backup are inserted, + ie. all records in the tables are deleted before the + tables are restored. Schema information about the tables + is not cleared or read from backup. + </item> + <item><c>{keep_tables, TabList}</c> Where <c>TabList</c> + is a list of tables which should be not be cleared, before + the records from the backup are inserted, i.e. the records + in the backup will be added to the records in the table. + Schema information about the tables is not cleared or read + from backup. + </item> + <item><c>{recreate_tables, TabList}</c> Where + <c>TabList</c> is a list of tables which should be + re-created, before the records from the backup are + inserted. The tables are first deleted and then created with + the schema information from the backup. All the nodes in the + backup needs to be up and running. + </item> + <item><c>{default_op, Operation}</c> Where <c>Operation</c> is + one of the following operations <c>skip_tables</c>, + <c>clear_tables</c>, <c>keep_tables</c> or + <c>recreate_tables</c>. The default operation specifies + which operation should be used on tables from the backup + which are not specified in any of the lists above. If + omitted, the operation <c>clear_tables</c> will be used. + </item> + </list> + <p>The affected tables are write locked during the + restoration, but regardless of the lock conflicts caused by + this, the applications can continue to do their work while + the restoration is being performed. The restoration is + performed as one single transaction. + </p> + <p>If the database is + huge, it may not be possible to restore it online. In such + cases, the old database must be restored by installing a + fallback and then restart.</p> + </desc> + </func> + <func> + <name>s_delete({Tab, Key}) -> ok | transaction abort </name> + <fsummary>Set sticky lock and delete records.</fsummary> + <desc> + <p>Invokes <c>mnesia:delete(Tab, Key, sticky_write)</c></p> + </desc> + </func> + <func> + <name>s_delete_object(Record) -> ok | transaction abort </name> + <fsummary>Set sticky lock and delete record.</fsummary> + <desc> + <p>Invokes <c>mnesia:delete_object(Tab, Record, sticky_write)</c> where <c>Tab</c> is <c>element(1, Record)</c>.</p> + </desc> + </func> + <func> + <name>s_write(Record) -> ok | transaction abort </name> + <fsummary>Write <c>Record</c>and sets stick lock.</fsummary> + <desc> + <p>Invokes <c>mnesia:write(Tab, Record, sticky_write)</c> + where <c>Tab</c> is <c>element(1, Record)</c>.</p> + </desc> + </func> + <func> + <name>schema() -> ok </name> + <fsummary>Print information about all table definitions on the tty. </fsummary> + <desc> + <p>Prints information about all table definitions on the tty.</p> + </desc> + </func> + <func> + <name>schema(Tab) -> ok </name> + <fsummary>Print information about one table definition on the tty.</fsummary> + <desc> + <p>Prints information about one table definition on the tty.</p> + </desc> + </func> + <func> + <name>select(Tab, MatchSpec [, Lock]) -> transaction abort | [Object] </name> + <fsummary>Match the objects in <c>Tab</c>against <c>MatchSpec</c>.</fsummary> + <desc> + <p>Matches the objects in the table <c>Tab</c> using a + match_spec as described in the ERTS Users Guide. Optionally a lock + <c>read</c> or <c>write</c> can be given as the third + argument, default is <c>read</c>. The return value depends + on the <c>MatchSpec</c>.</p> + <p><em>Note:</em> for best performance <c>select</c> should + be used before any modifying operations are done on that table + in the same transaction, i.e. don't use <c>write</c> or <c>delete</c> + before a <c>select</c>.</p> + <p>In its simplest forms the match_spec's look like this:</p> + <list type="bulleted"> + <item>MatchSpec = [MatchFunction]</item> + <item>MatchFunction = {MatchHead, [Guard], [Result]}</item> + <item>MatchHead = tuple() | record()</item> + <item>Guard = {"Guardtest name", ...}</item> + <item>Result = "Term construct"</item> + </list> + <p>See the ERTS Users Guide and <c>ets</c> documentation for a + complete description of the select.</p> + <p>For example to find the names of all male persons with an age over 30 in table + Tab do:</p> + <code type="none"> +\011 MatchHead = #person{name='$1', sex=male, age='$2', _='_'}, +\011 Guard = {'>', '$2', 30}, +\011 Result = '$1', +\011 mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]), + </code> + </desc> + </func> + <func> + <name>select(Tab, MatchSpec, NObjects, Lock) -> transaction abort | {[Object],Cont} | '$end_of_table'</name> + <fsummary>Match the objects in <c>Tab</c>against <c>MatchSpec</c>.</fsummary> + <desc> + <p>Matches the objects in the table <c>Tab</c> using a + match_spec as described in ERTS users guide, and returns + a chunk of terms and a continuation, the wanted number + of returned terms is specified by the <c>NObjects</c> argument. + The lock argument can be <c>read</c> or <c>write</c>. + The continuation should be used as argument to <c>mnesia:select/1</c>, + if more or all answers are needed.</p> + <p><em>Note:</em> for best performance <c>select</c> should + be used before any modifying operations are done on that + table in the same transaction, i.e. don't use + <c>mnesia:write</c> or <c>mnesia:delete</c> before a + <c>mnesia:select</c>. For efficiency the <c>NObjects</c> is + a recommendation only and the result may contain anything + from an empty list to all available results. </p> + </desc> + </func> + <func> + <name>select(Cont) -> transaction abort | {[Object],Cont} | '$end_of_table'</name> + <fsummary>Continues selecting objects. </fsummary> + <desc> + <p>Selects more objects with the match specification initiated + by <c>mnesia:select/4</c>. + </p> + <p><em>Note:</em> Any modifying operations, i.e. <c>mnesia:write</c> + or <c>mnesia:delete</c>, that are done between the <c>mnesia:select/4</c> + and <c>mnesia:select/1</c> calls will not be visible in the result.</p> + </desc> + </func> + <func> + <name>set_debug_level(Level) -> OldLevel</name> + <fsummary>Change the internal debug level of Mnesia</fsummary> + <desc> + <p>Changes the internal debug level of Mnesia. See the + chapter about configuration parameters for details.</p> + </desc> + </func> + <func> + <name>set_master_nodes(MasterNodes) -> ok | {error, Reason} </name> + <fsummary>Set the master nodes for all tables</fsummary> + <desc> + <p>For each table Mnesia will determine its replica nodes + (<c>TabNodes</c>) and invoke <c>mnesia:set_master_nodes(Tab, TabMasterNodes)</c> where <c>TabMasterNodes</c> is the + intersection of <c>MasterNodes</c> and <c>TabNodes</c>. See + <c>mnesia:set_master_nodes/2</c> about the semantics.</p> + </desc> + </func> + <func> + <name>set_master_nodes(Tab, MasterNodes) -> ok | {error, Reason} </name> + <fsummary>Set the master nodes for a table</fsummary> + <desc> + <p>If the application detects that there has been a + communication failure (in a potentially partitioned network) which + may have caused an inconsistent database, it may use the + function <c>mnesia:set_master_nodes(Tab, MasterNodes)</c> to + define from which nodes each table will be loaded. + At startup Mnesia's normal table load algorithm will be + bypassed and the table will be loaded from one of the master + nodes defined for the table, regardless of when and if Mnesia + was terminated on other nodes. The <c>MasterNodes</c> may only + contain nodes where the table has a replica and if the + <c>MasterNodes</c> list is empty, the master node recovery + mechanism for the particular table will be reset and the + normal load mechanism will be used at next restart. + </p> + <p>The master node setting is always local and it may be + changed regardless of whether Mnesia is started or not. + </p> + <p>The database may also become inconsistent if the + <c>max_wait_for_decision</c> configuration parameter is used + or if <c>mnesia:force_load_table/1</c> is used.</p> + </desc> + </func> + <func> + <name>snmp_close_table(Tab) -> {aborted, R} | {atomic, ok}</name> + <fsummary>Remove the possibility for SNMP to manipulate the table.</fsummary> + <desc> + <p>Removes the possibility for SNMP to manipulate the + table.</p> + </desc> + </func> + <func> + <name>snmp_get_mnesia_key(Tab, RowIndex) -> {ok, Key} | undefined</name> + <fsummary>Get the corresponding Mnesia key from an SNMP index.</fsummary> + <type> + <v>Tab ::= atom()</v> + <v>RowIndex ::= [integer()]</v> + <v>Key ::= key() | {key(), key(), ...}</v> + <v>key() ::= integer() | string() | [integer()]</v> + </type> + <desc> + <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> + </func> + <func> + <name>snmp_get_next_index(Tab, RowIndex) -> {ok, NextIndex} | endOfTable</name> + <fsummary>Get the index of the next lexicographical row.</fsummary> + <type> + <v>Tab ::= atom()</v> + <v>RowIndex ::= [integer()]</v> + <v>NextIndex ::= [integer()]</v> + </type> + <desc> + <p>The <c>RowIndex</c> may specify a non-existing row. + Specifically, it might be the empty list. Returns the index + of the next lexicographical row. If <c>RowIndex</c> is the + empty list, this function will return the index of the first row + in the table.</p> + </desc> + </func> + <func> + <name>snmp_get_row(Tab, RowIndex) -> {ok, Row} | undefined</name> + <fsummary>Retrieve a row indexed by an SNMP index.</fsummary> + <type> + <v>Tab ::= atom()</v> + <v>RowIndex ::= [integer()]</v> + <v>Row ::= record(Tab)</v> + </type> + <desc> + <p>Makes it possible to read a row by its SNMP index. This + index is specified as an SNMP OBJECT IDENTIFIER, a list of + integers.</p> + </desc> + </func> + <func> + <name>snmp_open_table(Tab, SnmpStruct) -> {aborted, R} | {atomic, ok}</name> + <fsummary>Organize a Mnesia table as an SNMP table.</fsummary> + <type> + <v>Tab ::= atom()</v> + <v>SnmpStruct ::= [{key, type()}]</v> + <v>type() ::= type_spec() | {type_spec(), type_spec(), ...}</v> + <v>type_spec() ::= fix_string | string | integer</v> + </type> + <desc> + <p>It is possible to establish a direct one to one mapping + between Mnesia tables and SNMP tables. Many + telecommunication applications are controlled and monitored + by the SNMP protocol. This connection between Mnesia and + SNMP makes it simple and convenient to achieve this. + </p> + <p>The <c>SnmpStruct</c> argument is a list of SNMP + information. Currently, the only information needed is + information about the key types in the table. It is not + possible to handle multiple keys 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 + the key types is specified as a tuple of atoms describing + the types. The only significant type is + <c>fix_string</c>. This means that a string has fixed + size. For example: + </p> + <code type="none"> +mnesia:snmp_open_table(person, [{key, string}]) + </code> + <p>causes the <c>person</c> table to be ordered as an SNMP + table. + </p> + <p>Consider the following schema for a table of company + employees. Each employee is identified by department number + and name. The other table column stores the telephone number: + </p> + <code type="none"> +mnesia:create_table(employee, + [{snmp, [{key, {integer, string}}]}, + {attributes, record_info(fields, employees)}]), + </code> + <p>The corresponding SNMP table would have three columns; + <c>department</c>, <c>name</c> and <c>telno</c>. + </p> + <p>It is possible to have table columns that are not visible + through the SNMP protocol. These columns must be the last + columns of the table. In the previous example, the SNMP + table could have columns <c>department</c> and <c>name</c> + only. The application could then use the <c>telno</c> column + internally, but it would not be visible to the SNMP + managers. + </p> + <p>In a table monitored by SNMP, all elements must be + integers, strings, or lists of integers. + </p> + <p>When a table is SNMP ordered, modifications are more + expensive than usual, O(logN). And more memory is used. + </p> + <p><em>Note:</em>Only the lexicographical SNMP ordering is + implemented in Mnesia, not the actual SNMP monitoring.</p> + </desc> + </func> + <func> + <name>start() -> ok | {error, Reason} </name> + <fsummary>Start a local Mnesia system.</fsummary> + <desc> + <p>The start-up 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 Mnesia files are written. This directory will be + referred to as the Mnesia directory. Mnesia may also be + started on disc-less nodes. See <c>mnesia:create_schema/1</c> + and the Mnesia User's Guide for more information about disc-less + nodes. + </p> + <p>The set of nodes which makes up a Mnesia system is kept in + a schema and it is possible to add and remove Mnesia nodes + 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 + Mnesia is started. During the start-up procedure, Mnesia + will exchange schema information between the nodes in order + to verify that the table definitions are compatible. + </p> + <p>Each schema has a unique cookie which may be regarded as a + unique schema identifier. The cookie must be the same on all + nodes where Mnesia is supposed to run. See the Mnesia + User's Guide for more information about these details. + </p> + <p>The schema file, as well as all other files which 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 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> may also be used.</p> + </desc> + </func> + <func> + <name>stop() -> stopped </name> + <fsummary>Stop Mnesia locally.</fsummary> + <desc> + <p>Stops Mnesia locally on the current node. + </p> + <p><c>application:stop(mnesia)</c> may also be used.</p> + </desc> + </func> + <func> + <name>subscribe(EventCategory)</name> + <fsummary>Subscribe to events of type <c>EventCategory</c>.</fsummary> + <desc> + <p>Ensures that a copy of all events of type + <c>EventCategory</c> are sent to the caller. The event + types available are described in the Mnesia User's Guide.</p> + </desc> + </func> + <func> + <name>sync_dirty(Fun, [, Args]) -> ResultOfFun | exit(Reason) </name> + <fsummary>Call the Fun in a context which is not protected by a transaction.</fsummary> + <desc> + <p>Call the <c>Fun</c> in a context which is not protected + by 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 + operations are performed synchronously. The caller waits for + the updates to be performed on all active replicas before + the <c>Fun</c> returns. See <c>mnesia:activity/4</c> and the + Mnesia User's Guide for more details.</p> + </desc> + </func> + <func> + <name>sync_transaction(Fun, [[, Args], Retries]) -> {aborted, Reason} | {atomic, ResultOfFun} </name> + <fsummary>Synchronously execute a transaction.</fsummary> + <desc> + <p>This function waits until data have been committed and + logged to disk (if disk is used) on every involved node before + it returns, otherwise it behaves as + <c>mnesia:transaction/[1,2,3]</c>.</p> + <p>This functionality can be used to avoid that one process may overload + a database on another node.</p> + </desc> + </func> + <func> + <name>system_info(InfoKey) -> Info | exit({aborted, Reason})</name> + <fsummary>Return information about the Mnesia system</fsummary> + <desc> + <p>Returns information about the Mnesia system, such as + transaction statistics, db_nodes, and configuration parameters. + Valid keys are:</p> + <list type="bulleted"> + <item> + <p><c>all</c>. This argument returns a list of all + local system information. Each element is a + <c>{InfoKey, InfoVal}</c> tuples.<em>Note:</em> New <c>InfoKey</c>'s may + be added and old undocumented <c>InfoKey</c>'s may be removed without + notice.</p> + </item> + <item> + <p><c>access_module</c>. This argument returns the name of + the module which is configured to be the activity access + callback module. + </p> + </item> + <item> + <p><c>auto_repair</c>. This argument returns + <c>true</c> or <c>false</c> to indicate if Mnesia is + configured to invoke the auto repair facility on corrupted + disc files. + </p> + </item> + <item> + <p><c>backup_module</c>. This argument returns the name of + the module which is configured to be the backup + callback module. + </p> + </item> + <item> + <p><c>checkpoints</c>. This argument + returns a list of the names of the + checkpoints currently active on this node. + </p> + </item> + <item> + <p><c>event_module</c>. This argument returns the name of + the module which is the event handler callback module. + </p> + </item> + <item> + <p><c>db_nodes</c>. This argument returns + the nodes which make up the persistent database. Disc + less nodes will only be included in the list of nodes if + they explicitly has been added to the schema, e.g. with + <c>mnesia:add_table_copy/3</c>. The function can be + invoked even if Mnesia is not yet running. + </p> + </item> + <item> + <p><c>debug</c>. This argument returns the current + debug level of Mnesia. + </p> + </item> + <item> + <p><c>directory</c>. This argument returns the name of + the Mnesia directory. It can be invoked even if Mnesia is + not yet running. + </p> + </item> + <item> + <p><c>dump_log_load_regulation</c>. This argument + returns a boolean which tells whether Mnesia is + configured to load regulate the dumper process or not. + This feature is temporary and will disappear in future + releases. + </p> + </item> + <item> + <p><c>dump_log_time_threshold</c>. This argument + returns the time threshold for transaction log dumps in + milliseconds. + </p> + </item> + <item> + <p><c>dump_log_update_in_place</c>. This argument + returns a boolean which tells whether Mnesia is + configured to perform the updates in the dets files + directly or if the updates should be performed in a copy + of the dets files. + </p> + </item> + <item> + <p><c>dump_log_write_threshold</c>. This argument + returns the write threshold for transaction log dumps as + the number of writes to the transaction log. + </p> + </item> + <item> + <p><c>extra_db_nodes</c>. This argument returns a list + of extra db_nodes to be contacted at start-up. + </p> + </item> + <item> + <p><c>fallback_activated</c>. This argument returns + true if a fallback is activated, otherwise false. + </p> + </item> + <item> + <p><c>held_locks</c>. This argument returns a list of + all locks held by the local Mnesia lock manager. + </p> + </item> + <item> + <p><c>is_running</c>. This argument returns <c>yes</c> + or <c>no</c> to indicate if Mnesia is running. It may + also return <c>starting</c> or <c>stopping</c>. Can be + invoked even if Mnesia is not yet running. + </p> + </item> + <item> + <p><c>local_tables</c>. This argument returns a list + of all tables which are configured to reside locally. + </p> + </item> + <item> + <p><c>lock_queue</c>. This argument returns a list of + all transactions that are queued for execution by the + local lock manager. + </p> + </item> + <item> + <p><c>log_version</c>. This argument returns the + version number of the Mnesia transaction log format. + </p> + </item> + <item> + <p><c>master_node_tables</c>. This argument returns a + list of all tables with at least one master node. + </p> + </item> + <item> + <p><c>protocol_version</c>. This argument + returns the version number + of the Mnesia inter-process communication protocol. + </p> + </item> + <item> + <p><c>running_db_nodes</c>. This argument returns a + list of nodes where Mnesia currently is running. This + function can be invoked even if Mnesia is not yet + running, but it will then have slightly different + semantics. If Mnesia is down on the local node, the + function will return those other <c>db_nodes</c> and + <c>extra_db_nodes</c> that for the moment are up and + running. If Mnesia is started, the function will return + 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 Mnesia system is fully operable and applications + may perform access of remote replicas. Before the schema + merge Mnesia will only operate locally. Sometimes there + may be 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> + </item> + <item> + <p><c>schema_location</c>. This argument returns the + initial schema location. + </p> + </item> + <item> + <p><c>subscribers</c>. This argument returns a list of + local processes currently subscribing to system events. + </p> + </item> + <item> + <p><c>tables</c>. This argument returns a list of all + locally known tables. + </p> + </item> + <item> + <p><c>transactions</c>. This argument returns a list + of all currently active local transactions. + </p> + </item> + <item> + <p><c>transaction_failures</c>. This argument returns + a number which indicates how many transactions have + failed since Mnesia was started. + </p> + </item> + <item> + <p><c>transaction_commits</c>. This argument returns a + number which indicates how many transactions have + terminated successfully since Mnesia was started. + </p> + </item> + <item> + <p><c>transaction_restarts</c>. This argument returns + a number which indicates how many transactions have been + restarted since Mnesia was started. + </p> + </item> + <item> + <p><c>transaction_log_writes</c>. This argument + returns a number which indicates the number of write + operation that have been performed to the transaction + log since start-up. + </p> + </item> + <item> + <p><c>use_dir</c>. This argument returns a boolean + which indicates whether the Mnesia directory is used or + not. Can be invoked even if Mnesia is not yet running. + </p> + </item> + <item> + <p><c>version</c>. This argument returns the current + version number of Mnesia. + </p> + </item> + </list> + </desc> + </func> + <func> + <name>table(Tab [,[Option]]) -> QueryHandle </name> + <fsummary>Return a QLC query handle.</fsummary> + <desc> + <p> <marker id="qlc_table"></marker> +Returns a QLC (Query List Comprehension) query handle, see + <seealso marker="stdlib:qlc">qlc(3)</seealso>.The module <c>qlc</c> implements a query language, it + 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>The list of Options may contain mnesia options or QLC + options, the following options are recognized by Mnesia: + <c>{traverse, SelectMethod},{lock, Lock},{n_objects,Number}</c>, any other option is forwarded + to QLC. The <c>lock</c> option may be <c>read</c> or + <c>write</c>, default is <c>read</c>. The option + <c>n_objects</c> specifies (roughly) the number of objects + returned from mnesia to QLC. Queries to remote tables may + need a larger chunks to reduce network overhead, default + <c>100</c> objects at a time are returned. The option + <c>traverse</c> determines the method to traverse the whole + table (if needed), the default method is <c>select</c>:</p> + <list type="bulleted"> + <item> + <p><c>select</c>. The table is traversed by calling + <c>mnesia:select/4</c> and <c>mnesia:select/1</c>. The + match specification (the second argument of <c>select/3</c>) + is assembled by QLC: simple filters are + translated into equivalent match specifications while + more complicated filters have to be applied to all + objects returned by <c>select/3</c> given a match + specification that matches all objects.</p> + </item> + <item> + <p><c>{select, MatchSpec}</c>. As for <c>select</c> + the table is traversed by calling <c>mnesia:select/3</c> and + <c>mnesia:select/1</c>. The difference is that the match + specification is explicitly given. This is how to state + match specifications that cannot easily be expressed + within the syntax provided by QLC.</p> + </item> + </list> + </desc> + </func> + <func> + <name>table_info(Tab, InfoKey) -> Info | exit({aborted, Reason})</name> + <fsummary>Return local information about table.</fsummary> + <desc> + <p>The <c>table_info/2</c> function takes two arguments. + The first is the name of a Mnesia table, the second is one of + the following keys: + </p> + <list type="bulleted"> + <item> + <p><c>all</c>. This argument returns a list of all + local table information. Each element is a <c>{InfoKey, ItemVal}</c> tuples. <em>Note:</em> New <c>InfoItem</c>'s may be + added and old undocumented <c>InfoItem</c>'s may be removed without + notice.</p> + </item> + <item> + <p><c>access_mode</c>. This argument returns the + access mode of the table. The access mode may either be + read_only or read_write. + </p> + </item> + <item> + <p><c>arity</c>. This argument returns the arity of + records in the table as specified in the schema. + </p> + </item> + <item> + <p><c>attributes</c>. This argument returns the table + attribute names which are specified in the schema. + </p> + </item> + <item> + <p><c>checkpoints</c>. This argument returns the names + of the currently active checkpoints which involves this + table on this node. + </p> + </item> + <item> + <p><c>cookie</c>. This argument returns a table cookie + which is a unique system generated identifier for the + table. The cookie is used internally to ensure that two + different table definitions using the same table name + cannot accidentally be intermixed. The cookie is + generated when the table is initially created. + </p> + </item> + <item> + <p><c>disc_copies</c>. This argument returns the nodes + where a disc_copy of the table resides according to the + schema. + </p> + </item> + <item> + <p><c>disc_only_copies </c>. This argument returns the + nodes where a disc_only_copy of the table resides + according to the schema. + </p> + </item> + <item> + <p><c>index</c>. This argument returns the list of + index position integers for the table. + </p> + </item> + <item> + <p><c>load_node</c>. This argument returns the name of + the node that Mnesia loaded the table from. The + structure of the returned value is unspecified but may + be useful for debugging purposes. + </p> + </item> + <item> + <p><c>load_order</c>. This argument returns the load + order priority of the table. It is an integer and + defaults to <c>0</c> (zero). + </p> + </item> + <item> + <p><c>load_reason</c>. This argument returns the + reason of why Mnesia decided to load the table. The + structure of the returned value is unspecified but may + be useful for debugging purposes. + </p> + </item> + <item> + <p><c>local_content</c>. This argument returns + <c>true</c> or <c>false</c> to indicate whether the + table is configured to have locally unique content on + each node. + </p> + </item> + <item> + <p><c>master_nodes</c>. This argument returns the + master nodes of a table. + </p> + </item> + <item> + <p><c>memory</c>. This argument returns the number of + words allocated to the table on this node. + </p> + </item> + <item> + <p><c>ram_copies</c>. This argument returns the nodes + where a ram_copy of the table resides according to the + schema. + </p> + </item> + <item> + <p><c>record_name</c>. This argument returns the + record name, common for all records in the table + </p> + </item> + <item> + <p><c>size</c>. This argument returns the number of + records inserted in the table. + </p> + </item> + <item> + <p><c>snmp</c>. This argument returns the SNMP struct. + <c>[]</c>meaning that the table currently has no SNMP + properties. + </p> + </item> + <item> + <p><c>storage_type</c>.This argument returns the local + storage type of the table. It can be <c>disc_copies</c>, + <c>ram_copies</c>, <c>disc_only_copies</c>, or the atom + <c>unknown</c>. <c>unknown</c> is returned for all + tables which only reside remotely. + </p> + </item> + <item> + <p><c>subscribers</c>. This argument returns a list + of local processes currently subscribing to local table + events which involve this table on this node. + </p> + </item> + <item> + <p><c>type</c>. This argument returns the table type, + which is either <c>bag</c>, <c>set</c> or <c>ordered_set</c>.. + </p> + </item> + <item> + <p><c>user_properties</c>. This argument returns the + user associated table properties of the table. It is a + list of the stored property records. + </p> + </item> + <item> + <p><c>version</c>. This argument returns the current + version of the table definition. The table version is + incremented when the table definition is changed. The + table definition may be incremented directly when the + table definition has been changed in a schema + transaction, or when a committed table definition is + merged with table definitions from other nodes during + start-up. + </p> + </item> + <item> + <p><c>where_to_read</c>.This argument returns the node + where the table can be read. If the value <c>nowhere</c> + is returned, the table is not loaded, or it resides at a + remote node which is not running. + </p> + </item> + <item> + <p><c>where_to_write</c>. This argument returns a list + of the nodes that currently hold an active replica of + the table. + </p> + </item> + <item> + <p><c>wild_pattern</c>. This argument returns a + structure which can be given to the various match + functions for a certain table. A record tuple is where all + record fields have the value <c>'_'</c>. + </p> + </item> + </list> + </desc> + </func> + <func> + <name>transaction(Fun [[, Args], Retries]) -> {aborted, Reason} | {atomic, ResultOfFun}</name> + <fsummary>Execute a transaction.</fsummary> + <desc> + <p>This function executes the functional object <c>Fun</c> + with arguments <c>Args</c> as a transaction. + </p> + <p>The code which executes inside the transaction + can consist of a series of table manipulation functions. + If something goes wrong inside the transaction as a result of a + user error or a certain table not being available, the + entire transaction is aborted and the function + <c>transaction/1</c> returns the tuple + <c>{aborted, Reason}</c>. + </p> + <p>If all is well, <c>{atomic, ResultOfFun}</c> is returned where + <c>ResultOfFun</c> is the value of the last expression in + <c>Fun</c>. + </p> + <p>A function which adds a family to the database can be + written as follows if we have a structure <c>{family, Father, Mother, ChildrenList}</c>: + </p> + <code type="none"> +add_family({family, F, M, Children}) -> + ChildOids = lists:map(fun oid/1, Children), + Trans = fun() -> + mnesia:write(F#person{children = ChildOids}, + mnesia:write(M#person{children = ChildOids}, + Write = fun(Child) -> mnesia:write(Child) end, + lists:foreach(Write, Children) + end, + mnesia:transaction(Trans). + +oid(Rec) -> {element(1, Rec), element(2, Rec)}. + </code> + <p>This code adds a set of people to the database. Running this code + within one transaction will ensure that either the whole + family is added to the database, or the whole transaction + aborts. For example, if the last child is badly formatted, + or the executing process terminates due to an + <c>'EXIT'</c> signal while executing the family code, the + transaction aborts. Accordingly, the situation where half a + family is added can never occur. + </p> + <p>It is also useful to update the database within a transaction + if several processes concurrently update the same records. + For example, the function <c>raise(Name, Amount)</c>, which + adds <c>Amount</c> to the salary field of a person, should + be implemented as follows: + </p> + <code type="none"> +raise(Name, Amount) -> + mnesia:transaction(fun() -> + case mnesia:wread({person, Name}) of + [P] -> + Salary = Amount + P#person.salary, + P2 = P#person{salary = Salary}, + mnesia:write(P2); + _ -> + mnesia:abort("No such person") + end + end). + </code> + <p>When this function executes within a transaction, + several processes running on different nodes can concurrently + execute the <c>raise/2</c> function without interfering + with each other. + </p> + <p>Since Mnesia detects deadlocks, a transaction can be + restarted any number of times. This function will attempt a restart as specified in + <c>Retries</c>. <c>Retries</c> must + be an integer greater than 0 or the atom <c>infinity</c>. Default is + <c>infinity</c>.</p> + </desc> + </func> + <func> + <name>transform_table(Tab, Fun, NewAttributeList, NewRecordName) -> {aborted, R} | {atomic, ok} </name> + <fsummary>Change format on all records in table. <c>Tab</c></fsummary> + <desc> + <p>This function applies the argument <c>Fun</c> to all + records in the table. <c>Fun</c> is a function which takes a + record of the old type and returns a transformed record of the + new type. The <c>Fun</c> argument can also be the atom + <c>ignore</c>, it indicates that only the meta data about the table will + be updated. Usage of <c>ignore</c> is not recommended but included + as a possibility for the user do to his own transform. + <c>NewAttributeList</c> and <c>NewRecordName</c> + specifies the attributes and the new record type of converted + table. Table name will always remain unchanged, if the + record_name is changed only the mnesia functions which + uses table identifiers will work, e.g. <c>mnesia:write/3</c> + will work but <c>mnesia:write/1</c> will not.</p> + </desc> + </func> + <func> + <name>transform_table(Tab, Fun, NewAttributeList) -> {aborted, R} | {atomic, ok} </name> + <fsummary>Change format on all records in table. <c>Tab</c></fsummary> + <desc> + <p>Invokes <c>mnesia:transform_table(Tab, Fun, NewAttributeList, RecName)</c> + where <c>RecName</c> is <c>mnesia:table_info(Tab, record_name)</c>.</p> + </desc> + </func> + <func> + <name>traverse_backup(Source, [SourceMod,] Target, [TargetMod,] Fun, Acc) -> {ok, LastAcc} | {error, Reason}</name> + <fsummary>Traversal of a backup.</fsummary> + <desc> + <p>With this function it is possible to iterate over a backup, + either for the purpose of transforming it into a new backup, + or just reading it. The arguments are explained briefly + below. See the Mnesia User's Guide for additional + details. + </p> + <list type="bulleted"> + <item><c>SourceMod</c> and <c>TargetMod</c> are the names of + the modules which actually access the backup + media. + </item> + <item><c>Source</c> and <c>Target</c> are opaque data used + exclusively by the modules <c>SourceMod</c> and + <c>TargetMod</c> for the purpose of initializing the + backup media. + </item> + <item><c>Acc</c> is an initial accumulator value. + </item> + <item><c>Fun(BackupItems, Acc)</c> is applied to each item in + the backup. The Fun must return a tuple + <c>{BackupItems,NewAcc}</c>, where <c>BackupItems</c> is + a list of valid backup items, and <c>NewAcc</c> is a new + accumulator value. The returned backup items are written + in the target backup. + </item> + <item><c>LastAcc</c> is the last accumulator value. This is + the last <c>NewAcc</c> value that was returned by <c>Fun</c>. + </item> + </list> + </desc> + </func> + <func> + <name>uninstall_fallback() -> ok | {error,Reason}</name> + <fsummary>Uninstall a fallback.</fsummary> + <desc> + <p>Invokes <c>mnesia:uninstall_fallback([{scope, global}])</c>.</p> + </desc> + </func> + <func> + <name>uninstall_fallback(Args) -> ok | {error,Reason}</name> + <fsummary>Uninstall a fallback.</fsummary> + <desc> + <p>This function is used to de-install a fallback before it + has been used to restore the database. This is normally a + distributed operation that is either performed on all + nodes with disc resident schema or none. Uninstallation of + fallbacks requires Erlang to be up and running on all + 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 info in the local + fallback. + </p> + <p><c>Args</c> is a list of the following tuples: + </p> + <list type="bulleted"> + <item> + <p><c>{module, BackupMod}</c>. + See <c>mnesia:install_fallback/2</c> about the + semantics.</p> + </item> + <item> + <p><c>{scope, Scope}</c> + See <c>mnesia:install_fallback/2</c> about the + semantics.</p> + </item> + <item> + <p><c>{mnesia_dir, AlternateDir}</c> + See <c>mnesia:install_fallback/2</c> about the + semantics.</p> + </item> + </list> + </desc> + </func> + <func> + <name>unsubscribe(EventCategory)</name> + <fsummary>Subscribe to events of type <c>EventCategory</c>.</fsummary> + <desc> + <p>Stops sending events of type + <c>EventCategory</c> to the caller.</p> + </desc> + </func> + <func> + <name>wait_for_tables(TabList,Timeout) -> ok | {timeout, BadTabList} | {error, Reason} </name> + <fsummary>Wait for tables to be accessible.</fsummary> + <desc> + <p>Some applications need to wait for certain tables to + be accessible in order to do useful work. + <c>mnesia:wait_for_tables/2</c> hangs until all tables in the + <c>TabList</c> are accessible, or until <c>timeout</c> is + reached.</p> + </desc> + </func> + <func> + <name>wread({Tab, Key}) -> transaction abort | RecordList </name> + <fsummary>Read records with given key.</fsummary> + <desc> + <p>Invoke <c>mnesia:read(Tab, Key, write)</c>.</p> + </desc> + </func> + <func> + <name>write(Record) -> transaction abort | ok </name> + <fsummary>Writes a record into the database.</fsummary> + <desc> + <p>Invoke <c>mnesia:write(Tab, Record, write)</c> where + <c>Tab</c> is <c>element(1, Record)</c>.</p> + </desc> + </func> + <func> + <name>write(Tab, Record, LockKind) -> transaction abort | ok </name> + <fsummary>Write an record into the database.</fsummary> + <desc> + <p>Writes the record <c>Record</c> to the table <c>Tab</c>. + </p> + <p>The function returns <c>ok</c>, or aborts if an error + occurs. For example, the transaction aborts if no + <c>person</c> table exists. + </p> + <p>The semantics of this function is context sensitive. See + <c>mnesia:activity/4</c> for more information. In transaction + context it acquires a lock of type <c>LockKind</c>. The + following lock types are supported: <c>write</c> and + <c>sticky_write</c>.</p> + </desc> + </func> + <func> + <name>write_lock_table(Tab) -> ok | transaction abort</name> + <fsummary>Set write lock on an entire table.</fsummary> + <desc> + <p>Invokes <c>mnesia:lock({table, Tab}, write)</c>.</p> + </desc> + </func> + </funcs> + + <section> + <title>Configuration Parameters</title> + <p>Mnesia reads the following application configuration + parameters:</p> + <list type="bulleted"> + <item> + <p><c>-mnesia access_module Module</c>. The + name of the Mnesia activity access callback module. The default is + <c>mnesia</c>. + </p> + </item> + <item> + <p><c>-mnesia auto_repair true | false</c>. This flag controls + whether Mnesia will try to automatically repair + files that have not been properly closed. The default is + <c>true</c>. + </p> + </item> + <item> + <p><c>-mnesia backup_module Module</c>. The + name of the Mnesia backup callback module. The default is + <c>mnesia_backup</c>. + </p> + </item> + <item> + <p><c>-mnesia debug Level</c> + Controls the debug level of Mnesia. + Possible values are:</p> + <taglist> + <tag><c>none</c></tag> + <item> + <p>No trace outputs at all. This is the default setting. + </p> + </item> + <tag><c>verbose</c></tag> + <item> + <p>Activates tracing of important debug events. These + debug events 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> + <tag><c>debug</c></tag> + <item> + <p>Activates all events at the verbose level plus full + trace of all debug events. These debug events 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 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 level debug. On this + debug level, the Mnesia event handler starts subscribing to + updates on all Mnesia tables. This level is only intended + for debugging small toy systems since many large + events may be generated. + </p> + </item> + <tag><c>false</c></tag> + <item> + <p>An alias for none. + </p> + </item> + <tag><c>true</c></tag> + <item> + <p>An alias for debug. + </p> + </item> + </taglist> + </item> + <item> + <p><c>-mnesia core_dir Directory</c>. The name of the + directory where Mnesia core files is stored or + false. Setting it implies that also ram only nodes, will + generate a core file if a crash occurs. </p> + </item> + <item> + <p><c>-mnesia dc_dump_limit Number</c>. + Controls how often <c>disc_copies</c> tables are dumped from memory. + Tables are dumped when + <c>filesize(Log) > (filesize(Tab)/Dc_dump_limit)</c>. + Lower values reduces cpu overhead but increases disk space and + startup times. The default is 4.</p> + </item> + <item> + <p><c>-mnesia dir Directory</c>. The name of the directory + where all Mnesia data is stored. The name of the directory must + be unique for the current node. Two nodes may, under no + circumstances, share the same Mnesia directory. The results are + totally unpredictable.</p> + </item> + <item> + <p><c>-mnesia dump_log_load_regulation true | false</c>. + Controls if the log dumps should be performed as fast as + possible or if the dumper should do its own load + regulation. This feature is temporary and will disappear in a + future release. The default is <c>false</c>. + </p> + </item> + <item> + <p><c>-mnesia dump_log_update_in_place true | false</c>. + Controls if log dumps are performed on a copy of + the original data file, or if the log dump is + performed on the original data file. The default is <c>true</c></p> + </item> + <item> + <p><c>-mnesia dump_log_write_threshold Max</c>, where + <c>Max</c> is an integer which specifies the maximum number of writes + allowed to the transaction log before a new dump of the log + is performed. It defaults to 100 log writes. + </p> + </item> + <item> + <p><c>-mnesia dump_log_time_threshold Max</c>, + where <c>Max</c> is an integer which + specifies the dump log interval in milliseconds. It defaults + to 3 minutes. If a dump has not been performed within + <c>dump_log_time_threshold</c> milliseconds, then a new dump is + performed regardless of how many writes have been + performed. + </p> + </item> + <item> + <p><c>-mnesia event_module Module</c>. The + name of the Mnesia event handler callback module. The 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 + Mnesia should also establish contact. The default value + is the empty list <c>[]</c>. + </p> + </item> + <item> + <p><c>-mnesia fallback_error_function {UserModule, UserFunc}</c> + specifies a user supplied callback function + which will be called if a fallback is installed and mnesia + goes down on another node. Mnesia will call the function + with one argument the name of the dying node, e.g. + <c>UserModule:UserFunc(DyingNode)</c>. + Mnesia should be restarted or else + the database could be inconsistent. + The default behaviour is to terminate mnesia. + </p> + </item> + <item> + <p><c>-mnesia max_wait_for_decision Timeout</c>. Specifies + how long Mnesia will wait for other nodes to share their + knowledge regarding the outcome of an unclear transaction. By + default the <c>Timeout</c> is set to the atom + <c>infinity</c>, which implies that if Mnesia upon startup + encounters a "heavyweight transaction" whose outcome is + unclear, the local Mnesia will wait until Mnesia is started + on some (in worst cases all) of the other nodes that were + involved in the interrupted transaction. This is a very rare + situation, but when/if it happens, Mnesia does not guess if + the transaction on the other nodes was committed or aborted. + Mnesia will wait until it knows the outcome and then act + accordingly. + </p> + <p>If <c>Timeout</c> is set to an integer value in + milliseconds, Mnesia will force "heavyweight transactions" + to be finished, even if the outcome of the transaction for + the moment is unclear. After <c>Timeout</c> milliseconds, + Mnesia will commit/abort the transaction and continue with + the startup. This may lead to a situation where the + transaction is committed on some nodes and aborted on other + nodes. If the transaction was a schema transaction, the + inconsistency may be fatal. + </p> + </item> + <item> + <p><c>-mnesia no_table_loaders NUMBER</c> specifies the number of + parallel table loaders during start. More loaders can be good if the + network latency is high or if many tables contains few records. + The default value is <c>2</c>. + </p> + </item> + <item> + <p><c>-mnesia schema_location Loc</c> controls where + Mnesia will look for its schema. The parameter + <c>Loc</c> may 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 Mnesia directory. If the schema cannot be found, + Mnesia refuses to start. This is the old behavior. + </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 just contains 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 based nodes.) + </p> + </item> + <tag><c>opt_disc</c></tag> + <item> + <p>Optional disc. The schema may reside either on disc + or in 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, Mnesia starts + as a disc-less node and the storage type of the schema table is + <c>ram_copies</c>. The default value for the application parameter + is <c>opt_disc</c>. + </p> + </item> + </taglist> + </item> + </list> + <p>First the SASL application parameters are checked, then + the command line flags are checked, and finally, the default + value is chosen. + </p> + </section> + + <section> + <title>See Also</title> + <p>mnesia_registry(3), mnesia_session(3), qlc(3), + dets(3), ets(3), disk_log(3), application(3) + </p> + </section> + +</erlref> + diff --git a/lib/mnesia/doc/src/mnesia_frag_hash.xml b/lib/mnesia/doc/src/mnesia_frag_hash.xml new file mode 100644 index 0000000000..ca03327994 --- /dev/null +++ b/lib/mnesia/doc/src/mnesia_frag_hash.xml @@ -0,0 +1,166 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>2002</year> + <year>2007</year> + <holder>Ericsson AB, All Rights Reserved</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + + <title>mnesia_frag_hash</title> + <prepared>Håkan Mattsson</prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date>2002-09-10</date> + <rev>A</rev> + <file>mnesia_frag_hash.sgml</file> + </header> + <module>mnesia_frag_hash</module> + <modulesummary>Defines mnesia_frag_hash callback behaviour</modulesummary> + <description> + <p>The module <c>mnesia_frag_hash</c> defines a callback + behaviour for user defined hash functions of fragmented tables.</p> + <p>Which module that is selected to implement the <c>mnesia_frag_hash</c> + behaviour for a particular fragmented table is specified together + with the other <c>frag_properties</c>. The <c>hash_module</c> defines + the module name. The <c>hash_state</c> defines the initial hash state.</p> + <p>It implements dynamic hashing which is a kind of hashing + that grows nicely when new fragments are added. It is well + suited for scalable hash tables</p> + </description> + <funcs> + <func> + <name>init_state(Tab, State) -> NewState | abort(Reason)</name> + <fsummary>Initiate the hash state for a new table</fsummary> + <type> + <v>Tab = atom()</v> + <v>State = term()</v> + <v>NewState = term()</v> + <v>Reason = term()</v> + </type> + <desc> + <p>This function is invoked when a fragmented table is + created with <c>mnesia:create_table/2</c> or when a + normal (un-fragmented) table is converted to be a + fragmented table with <c>mnesia:change_table_frag/2</c>.</p> + <p>Note that the <c>add_frag/2</c> function will be invoked + one time each for the rest of the fragments (all but number 1) + as a part of the table creation procedure.</p> + <p><c>State</c> is the initial value of the <c>hash_state</c><c>frag_property</c>. The <c>NewState</c> will be stored as + <c>hash_state</c> among the other <c>frag_properties</c>. + </p> + </desc> + </func> + <func> + <name>add_frag(State) -> {NewState, IterFrags, AdditionalLockFrags} | abort(Reason)</name> + <fsummary>This function is invoked when a new fragment is added to a fragmented table</fsummary> + <type> + <v>State = term()</v> + <v>NewState = term()</v> + <v>IterFrags = [integer()]</v> + <v>AdditionalLockFrags = [integer()]</v> + <v>Reason = term()</v> + </type> + <desc> + <p>In order to scale well, it is a good idea ensure that the + records are evenly distributed over all fragments including + the new one.</p> + <p>The <c>NewState</c> will be 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, Mnesia will iterate + over all fragments corresponding to the <c>IterFrags</c> numbers + and invoke <c>key_to_frag_number(NewState,RecordKey)</c> for + each record. If the new fragment differs from the old + fragment, the record will be moved to the new fragment.</p> + <p>As the <c>add_frag</c> procedure is a part of a schema + transaction Mnesia will acquire a write locks on the + affected tables. That is both the fragments corresponding + to <c>IterFrags</c> and those corresponding to + <c>AdditionalLockFrags</c>.</p> + </desc> + </func> + <func> + <name>del_frag(State) -> {NewState, IterFrags, AdditionalLockFrags} | abort(Reason)</name> + <fsummary>This function is invoked when a fragment is deleted from a fragmented table</fsummary> + <type> + <v>State = term()</v> + <v>NewState = term()</v> + <v>IterFrags = [integer()]</v> + <v>AdditionalLockFrags = [integer()]</v> + <v>Reason = term()</v> + </type> + <desc> + <p>The <c>NewState</c> will be 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, Mnesia will iterate + over all fragments corresponding to the <c>IterFrags</c> numbers + and invoke <c>key_to_frag_number(NewState,RecordKey)</c> for + each record. If the new fragment differs from the old + fragment, the record will be moved to the new fragment.</p> + <p>Note that all records in the last fragment must be moved to + another fragment as the entire fragment will be deleted.</p> + <p>As the <c>del_frag</c> procedure is a part of a schema + transaction Mnesia will acquire a write locks on the + affected tables. That is both the fragments corresponding + to <c>IterFrags</c> and those corresponding to + <c>AdditionalLockFrags</c>.</p> + </desc> + </func> + <func> + <name>key_to_frag_number(State, Key) -> FragNum | abort(Reason)</name> + <fsummary>Resolves the key of a record into a fragment number</fsummary> + <type> + <v>FragNum = integer()()</v> + <v>Reason = term()</v> + </type> + <desc> + <p>This function is invoked whenever Mnesia needs to determine + which fragment a certain record belongs to. It is typically + invoked at read, write and delete.</p> + </desc> + </func> + <func> + <name>match_spec_to_frag_numbers(State, MatchSpec) -> FragNums | abort(Reason)</name> + <fsummary>Resolves a MatchSpec into a list of fragment numbers</fsummary> + <type> + <v>MatcSpec = ets_select_match_spec()</v> + <v>FragNums = [FragNum]</v> + <v>FragNum = integer()</v> + <v>Reason = term()</v> + </type> + <desc> + <p>This function is invoked whenever Mnesia needs to determine + which fragments that needs to be searched for a MatchSpec. + It is typically invoked at select and match_object.</p> + </desc> + </func> + </funcs> + + <section> + <title>See Also</title> + <p>mnesia(3) + </p> + </section> + +</erlref> + diff --git a/lib/mnesia/doc/src/mnesia_registry.xml b/lib/mnesia/doc/src/mnesia_registry.xml new file mode 100644 index 0000000000..966134d508 --- /dev/null +++ b/lib/mnesia/doc/src/mnesia_registry.xml @@ -0,0 +1,104 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>1998</year> + <year>2007</year> + <holder>Ericsson AB, All Rights Reserved</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + + <title>mnesia_registry</title> + <prepared>Dan Gudmundsson and Håkan Mattsson</prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date>98-04-24</date> + <rev>A</rev> + <file>mnesia_registry.sgml</file> + </header> + <module>mnesia_registry</module> + <modulesummary>Dump support for registries in erl_interface. </modulesummary> + <description> + <p>The module <c>mnesia_registry</c> is usually part of + <c>erl_interface</c>, but for the time being, it is a part of the + Mnesia application. + </p> + <p><c>mnesia_registry</c> is mainly an module intended for + internal usage 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 reside in RAM on the C-node but they may also be + dumped into Mnesia tables. By default, the dumping of registry + tables via <c>erl_interface</c> causes a corresponding Mnesia + table to be created with <c>mnesia_registry:create_table/1</c> + if necessary. + </p> + <p>The tables that are created with these functions can be + administered as all other Mnesia tables. They may be included in + backups or replicas may be added etc. The tables are in fact + normal Mnesia tables owned by the user of the corresponding + <c>erl_interface</c> registries. + </p> + </description> + <funcs> + <func> + <name>create_table(Tab) -> ok | exit(Reason)</name> + <fsummary>Creates a registry table in Mnesia.</fsummary> + <desc> + <p>This is a wrapper function for + <c>mnesia:create_table/2</c> which creates a table (if there is no existing table) + with an appropriate set of <c>attributes</c>. The table will + only reside on the local node and its storage type will be + the same as the <c>schema</c> table on the local + node, ie. <c>{ram_copies,[node()]}</c> or + <c>{disc_copies,[node()]}</c>. + </p> + <p>It is this function that is used by <c>erl_interface</c> to + create the Mnesia table if it did not already exist.</p> + </desc> + </func> + <func> + <name>create_table(Tab, TabDef) -> ok | exit(Reason)</name> + <fsummary>Creates a customized registry table in Mnesia. </fsummary> + <desc> + <p>This is a wrapper function for + <c>mnesia:create_table/2</c> which creates a table (if there is no existing table) + with an appropriate set of <c>attributes</c>. The attributes + and <c>TabDef</c> are forwarded to + <c>mnesia:create_table/2</c>. For example, if the table should + reside as <c>disc_only_copies</c> on all nodes a call would + look like:</p> + <code type="none"> + TabDef = [{{disc_only_copies, node()|nodes()]}], + mnesia_registry:create_table(my_reg, TabDef) + </code> + </desc> + </func> + </funcs> + + <section> + <title>See Also</title> + <p>mnesia(3), erl_interface(3) + </p> + </section> + +</erlref> + diff --git a/lib/mnesia/doc/src/note.gif b/lib/mnesia/doc/src/note.gif Binary files differnew file mode 100644 index 0000000000..6fffe30419 --- /dev/null +++ b/lib/mnesia/doc/src/note.gif diff --git a/lib/mnesia/doc/src/notes.gif b/lib/mnesia/doc/src/notes.gif Binary files differnew file mode 100644 index 0000000000..e000cca26a --- /dev/null +++ b/lib/mnesia/doc/src/notes.gif diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml new file mode 100644 index 0000000000..69f2185cd8 --- /dev/null +++ b/lib/mnesia/doc/src/notes.xml @@ -0,0 +1,383 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>1996</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Mnesia Release Notes</title> + <prepared>Dan Gudmundsson and Håkan Mattsson</prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date>05-01-26</date> + <rev>AE</rev> + <file>notes.xml</file> + </header> + <p>This document describes the changes made to the Mnesia system + from version to version. The intention of this document is to + list all incompatibilities as well as all enhancements and + bugfixes for every release of Mnesia. Each release of Mnesia + thus constitutes one section in this document. The title of each + section is the version number of Mnesia.</p> + + <section><title>Mnesia 4.4.12</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The documentation is now built with open source tools + (xsltproc and fop) that exists on most platforms. One + visible change is that the frames are removed.</p> + <p> + Own Id: OTP-8250</p> + </item> + </list> + </section> + + </section> + <section><title>Mnesia 4.4.11</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Fixed duplicate results with mnesia:index_read() on + ordered_set tables. Reported by Sam Bobroff. </p> + <p> + Fixed locking in mnesia:index_read() which now grabs a read + table lock to ensure correctness, this may slow down the + operation or block other processes trying to reach the + same table. </p> + <p> + Calling mnesia:dump_log() could crash mnesia, + Reported by Igor Ribeiro Sucupira.</p> + <p> Own Id: OTP-8074</p> + </item> + </list> + </section> + + </section> + <section><title>Mnesia 4.4.10</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Mnesia crashed if a qlc query was running inside a + transaction when mnesia stopped at another node. Thanks + Teemu Antti-Poika.</p> + <p> + Own Id: OTP-7968</p> + </item> + <item> + <p> + Mnesia could crash when loading local_content tables.</p> + <p> + Own Id: OTP-8002 Aux Id: seq11277 </p> + </item> + </list> + </section> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Minor (smp) optimizations.</p> + <p> + Own Id: OTP-7928</p> + </item> + </list> + </section> + + </section> + + + <section><title>Mnesia 4.4.9</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + mnesia:clear_table/1 crashed instead of returning + <c>{aborted,..}</c> if it was called inside a + transaction.</p> + <p> + Own Id: OTP-7911</p> + </item> + </list> + </section> + + </section> + + <section><title>Mnesia 4.4.8</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + With bad timing several api functions could return or + exit with a bad error message when mnesia was shutting + down.</p> + <p> + Own Id: OTP-7753 Aux Id: seq11179 </p> + </item> + <item> + <p> + <c>mnesia:clear_table/1</c> cleared all nodes table + content even if the table was <c>local_content</c> only + type.</p> + <p> + Own Id: OTP-7835</p> + </item> + </list> + </section> + + </section> + + <section><title>Mnesia 4.4.7</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Disallowed match patterns ('_', and '$n') as argument to + <c>mnesia:delete_object/1</c> and friends.</p> + <p> + Own Id: OTP-7524</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Introduced a few new functions in Mnesia: <c>mnesia:read/2</c>, + <c>mnesia:first/3</c>, <c>mnesia:last/3</c>, <c>mnesia:prev/4</c>, + <c>mnesia:next/4</c>, <c>mnesia_frag:first/1</c>, <c>mnesia_frag:last/1</c>, + <c>mnesia_frag:prev/2</c>, <c>mnesia_frag:next/2</c>.</p> + <p> + Own Id: OTP-7625</p> + </item> + </list> + </section> + +</section> + + <section><title>Mnesia 4.4.6</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + <c>mnesia:restore/2</c> aborted if a <c>EXIT</c> message + appeared in the client message queue.</p> + <p> + Own Id: OTP-7585 Aux Id: seq11046 </p> + </item> + </list> + </section> + </section> + + <section><title>Mnesia 4.4.5</title> + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + mnesia:clear_table/1 does not require that all + replicas of the table are available anymore.</p> + <p> + Own Id: OTP-7466 Aux Id: seq11015</p> + </item> + </list> + </section> + </section> + + <section><title>Mnesia 4.4.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Mnesia did not garbage collect transaction decisions on + disk based nodes if no transactions where made on the + local node.</p> + <p> + Own Id: OTP-7419</p> + </item> + </list> + </section> + + </section> + + <section><title>Mnesia 4.4.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Table referred to by foreign key did not have node_pool + properly cleaned up when a node was removed from the + schema. Thanks Paul Mineiro.</p> + <p> + Own Id: OTP-7340</p> + </item> + <item> + <p> + Mnesia crashed and generated a core dump if a + schema_transaction was running when mnesia stopped.</p> + <p> + Own Id: OTP-7378 Aux Id: seq10964 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + It is now possible to delete a db node even when other + disk resident nodes are down. Thanks Paul Mineiro.</p> + <p> + Own Id: OTP-7383</p> + </item> + </list> + </section> + +</section> + + <section><title>Mnesia 4.4.2</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Sticky locks could lead to hanging transactions.</p> + <p> + Own Id: OTP-7205 Aux Id: seq10793 </p> + </item> + <item> + <p> + <c>mnesia:snmp_get_next_index/2</c> didn't work with + partial index keys. Argument checking is now done + according to documentation, in functions + <c>mnesia:snmp_get_row/2</c>, + <c>mnesia:snmp_get_mnesia_key/2</c> and + <c>mnesia:snmp_get_next_index/2</c>. These functions now + require that <c>RowIndex</c> is a list.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-7208</p> + </item> + </list> + </section> + </section> + + <section><title>Mnesia 4.4.1</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Snmp index tables was not initialized correctly in + <c>mnesia-4.4</c>.</p> + <p> + Own Id: OTP-7170 Aux Id: seq10870 </p> + </item> + </list> + </section> + <section><title>Known Bugs and Problems</title> + <list> + <item> + <p> + Rearranging fragmented tables is an O(N^2) operation.</p> + <p> + Own Id: OTP-6300</p> + </item> + </list> + </section> + </section> + + <section><title>Mnesia 4.4</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Mnesia ignored the module argument to + <c>mnesia:restore/2</c>. Thanks Paul Minerio.</p> + <p> + Own Id: OTP-6981</p> + </item> + </list> + </section> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Mnesia's snmp operations <c>snmp_get_row/2</c>, + <c>snmp_get_next_index/2</c> and + <c>snmp_get_mnesia_key/2</c> have been made context + aware, i.e. inside a transaction they will compensate for + table updates made in earlier in the same transaction. + This might cause a performance drop if a lot of updates + have been made before the invocation of these functions.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-6856 Aux Id: seq10671 </p> + </item> + <item> + <p> + Introduced erlang:phash/2 as new default for fragmented + tables. Already existing tables will continue to use + whatever hash function they where using.</p> + <p> + Own Id: OTP-6923</p> + </item> + <item> + <p> + Introduced <c>mnesia:is_transaction/0</c>.</p> + <p> + Own Id: OTP-6995 Aux Id: seq10812 </p> + </item> + </list> + </section> + + <section><title>Known Bugs and Problems</title> + <list> + <item> + <p> + Rearranging fragmented tables is an O(N^2) operation.</p> + <p> + Own Id: OTP-6300</p> + </item> + </list> + </section> + </section> + + <!-- section> + <title>Previous Notes</title> + <p>For information about older versions see <url href="part_notes_history_frame.html">release notes history</url>.</p> + </section --> +</chapter> + diff --git a/lib/mnesia/doc/src/notes_history.xml b/lib/mnesia/doc/src/notes_history.xml new file mode 100644 index 0000000000..0984e33376 --- /dev/null +++ b/lib/mnesia/doc/src/notes_history.xml @@ -0,0 +1,322 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2004</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Mnesia Release Notes</title> + <prepared>Dan Gudmundsson and Håkan Mattsson</prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date>04-08-22</date> + <rev>AE</rev> + <file>notes_history.sgml</file> + </header> + <p>This document describes the changes made to the Mnesia system + from version to version. The intention of this document is to + list all incompatibilities as well as all enhancements and + bugfixes for every release of Mnesia. Each release of Mnesia + thus constitutes one section in this document. The title of each + section is the version number of Mnesia.</p> + + <section><title>Mnesia 4.3.7</title> + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Removed a memory leak on ram-only nodes, introduced in + <c>mnesia-4.3.6</c>.</p> + <p> + Own Id: OTP-6936 Aux Id: seq10786 </p> + </item> + </list> + </section> + <section> + <title>Known Bugs and Problems</title> + <list type="bulleted"> + <item> + <p>Rearranging fragmented tables is an O(N^2) + operation.</p> + <p>Own Id: OTP-6300</p> + </item> + </list> + </section> + + </section> + + + <section> + <title>Mnesia 4.3.6</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + A bug causing lots of records to be lost at startup from + an installed fallback has been fixed. The bug did however + not show up when a backup file generated with + <c>mnesia:backup/1</c> or + <c>mnesia:backup_checkpoint/2</c> was installed as + fallback. In order to trigger the bug, the items in the + backup file had to be rearranged in such an order that + records from different tables were interleaved with each + other.</p> + <p> + Own Id: OTP-6903 Aux Id: seq10763 </p> + </item> + <item> + <p> + Mnesia sometimes failed to commit schema operations on + all nodes, this have been seen on smp machines but could + happen on single processor as well with some bad timing.</p> + <p> + Own Id: OTP-6904</p> + </item> + <item> + <p> + <c>mnesia:select/1</c> failed to return all matches on + remote nodes if something was written to the table + earlier in the same transaction.</p> + <p> + Own Id: OTP-6908</p> + </item> + </list> + </section> + + <section> + <title>Known Bugs and Problems</title> + <list type="bulleted"> + <item> + <p>Rearranging fragmented tables is an O(N^2) + operation.</p> + <p>Own Id: OTP-6300</p> + </item> + </list> + </section> + </section> + + <section> + <title>Mnesia 4.3.5</title> + + <section> + <title>Fixed Bugs and Malfunctions</title> + <list type="bulleted"> + <item> + <p>The internal index tables on bag tables where not always + cleaned correctly. Thanks Christopher Faulet and Salazard + Remy.</p> + <p>Own Id: OTP-6587</p> + </item> + <item> + <p>Changing the copy type with + <c>mnesia:change_table_copy/3</c> on a node which was down + was not handled correctly, that caused an eternal table + lock on the alive nodes. Thanks Hal Snyder.</p> + <p>Own + Id: OTP-6709</p> + </item> + </list> + </section> + + <section> + <title>Known Bugs and Problems</title> + <list type="bulleted"> + <item> + <p>Rearranging fragmented tables is an O(N^2) + operation.</p> + <p>Own Id: OTP-6300</p> + </item> + </list> + </section> + </section> + + <section> + <title>Mnesia 4.3.4</title> + + <section> + <title>Fixed Bugs and Malfunctions</title> + <list type="bulleted"> + <item> + <p>Adding fragments to ram_copies tables was allowed on + nodes that where down.</p> + <p>Own Id: OTP-6367</p> + </item> + <item> + <p>Mnesia leaked transaction decisions (memory and disk + space).</p> + <p>Own Id: OTP-6464</p> + </item> + <item> + <p><c>dirty_update_counter/3</c> did not work properly on + disc tables when the counter was not initiated (Thanks to + Sebastien Saint-Sevin).</p> + <p>Own Id: OTP-6545</p> + </item> + <item> + <p>Chunked <c>mnesia:select</c> on fragmented tables could + crash (Thanks to Primanathan Reddy).</p> + <p>Own Id: + OTP-6548</p> + </item> + </list> + </section> + + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> + <item> + <p>Introduced a new configure parameter dc_dump_limit.</p> + <p>Removed dead code (dialyzer warnings) and debugging + features that called interpreter commands.</p> + <p>Minor + performance increase when a lot of simultaneous + transactions where active. </p> + <p>Thank you Scott Lystig + Fritchie for debugging and bug reports.</p> + <p>Own Id: + OTP-6478</p> + </item> + </list> + </section> + + <section> + <title>Known Bugs and Problems</title> + <list type="bulleted"> + <item> + <p>Rearranging fragmented tables is an O(N^2) + operation.</p> + <p>Own Id: OTP-6300</p> + </item> + </list> + </section> + </section> + + <section> + <title>Mnesia 4.3.3</title> + + <section> + <title>Fixed Bugs and Malfunctions</title> + <list type="bulleted"> + <item> + <p>Mnesia could crash during startup when loading tables + from remote node. </p> + <p>Own Id: OTP-6298 Aux Id: seq10402 </p> + </item> + <item> + <p>Mnesia could fail to update all copies during + del_table_copy. </p> + <p>Own Id: OTP-6299</p> + </item> + </list> + </section> + + <section> + <title>Known Bugs and Problems</title> + <list type="bulleted"> + <item> + <p>Rearranging fragmented tables is an O(N^2) operation.</p> + <p>Own Id: OTP-6300</p> + </item> + </list> + </section> + </section> + + <section> + <title>Mnesia 4.3.2</title> + + <section> + <title>Fixed Bugs and Malfunctions</title> + <list type="bulleted"> + <item> + <p>Mnesia sometimes failed to remove [d]ets table fixation, + when using <c>mnesia:first/1</c>,<c>mnesia:next/2</c> or + <c>qlc</c> this could cause that deleted records are not + actually deleted in the [d]ets table and that + <c>mnesia:[dirty_]first/1</c> reported the wrong key. </p> + <p>Own Id: OTP-6193 Aux Id: seq10376</p> + </item> + </list> + </section> + </section> + + <section> + <title>Mnesia 4.3.1</title> + + <section> + <title>Fixed Bugs and Malfunctions</title> + <list type="bulleted"> + <item> + <p>Mnesia could crash (bad match in mnesia_controller) + during start. </p> + <p>Own Id: OTP-6116 Aux Id: seq10305 </p> + </item> + </list> + </section> + </section> + + <section> + <title>Mnesia 4.3</title> + + <section> + <title>Fixed Bugs and Malfunctions</title> + <list type="bulleted"> + <item> + <p>Deleting tables during the start of mnesia on another + node caused problems. </p> + <p>Own Id: OTP-5928 Aux Id: + seq10111 </p> + </item> + <item> + <p>Killing processes that runs nested transactions could + crash mnesia. </p> + <p>Own Id: OTP-6027 Aux Id: seq10244 </p> + </item> + <item> + <p>Creating or deleting tables with a checkpoint activated + could crash mnesia </p> + <p>Own Id: OTP-6064</p> + </item> + <item> + <p>Table loading could be mixed with schema operations + which could cause troubles. </p> + <p>Own Id: OTP-6065 Aux Id: + seq10291 </p> + </item> + </list> + </section> + + <section> + <title>Improvements and New Features</title> + <list type="bulleted"> + <item> + <p>Added parallel table loaders to increase startup + performance when the system have many small tables. The + configuration variable <c>no_table_loaders</c> configures + the number of loaders, default is two. </p> + <p>Own Id: + OTP-6087</p> + </item> + </list> + </section> + </section> +</chapter> + diff --git a/lib/mnesia/doc/src/part.xml b/lib/mnesia/doc/src/part.xml new file mode 100644 index 0000000000..b9654a4207 --- /dev/null +++ b/lib/mnesia/doc/src/part.xml @@ -0,0 +1,49 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE part SYSTEM "part.dtd"> + +<part xmlns:xi="http://www.w3.org/2001/XInclude"> + <header> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Mnesia User's Guide</title> + <prepared>Claes Wikström, Hans Nilsson and Håkan Mattsson</prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>part.sgml</file> + </header> + <description> + <p><em>Mnesia</em> 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> + </description> + <xi:include href="Mnesia_chap1.xml"/> + <xi:include href="Mnesia_chap2.xml"/> + <xi:include href="Mnesia_chap3.xml"/> + <xi:include href="Mnesia_chap4.xml"/> + <xi:include href="Mnesia_chap5.xml"/> + <xi:include href="Mnesia_chap7.xml"/> + <xi:include href="Mnesia_chap8.xml"/> + <xi:include href="Mnesia_App_A.xml"/> + <xi:include href="Mnesia_App_B.xml"/> + <xi:include href="Mnesia_App_C.xml"/> + <xi:include href="Mnesia_App_D.xml"/> +</part> + diff --git a/lib/mnesia/doc/src/part_notes.xml b/lib/mnesia/doc/src/part_notes.xml new file mode 100644 index 0000000000..caa155585d --- /dev/null +++ b/lib/mnesia/doc/src/part_notes.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE part SYSTEM "part.dtd"> + +<part xmlns:xi="http://www.w3.org/2001/XInclude"> + <header> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>MNESIA Release Notes</title> + <prepared>Claes Wikström, Hans Nilsson and Håkan Mattsson</prepared> + <docno></docno> + <date>1997-05-27</date> + <rev>1.2</rev> + <file>part_notes.xml</file> + </header> + <description> + <p><em>Mnesia</em> 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> + <p>For information about older versions see + <url href="part_notes_history_frame.html">release notes history</url>.</p> + </description> + <xi:include href="notes.xml"/> +</part> + diff --git a/lib/mnesia/doc/src/part_notes_history.xml b/lib/mnesia/doc/src/part_notes_history.xml new file mode 100644 index 0000000000..177738623c --- /dev/null +++ b/lib/mnesia/doc/src/part_notes_history.xml @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE part SYSTEM "part.dtd"> + +<part> + <header> + <copyright> + <year>2004</year> + <year>2007</year> + <holder>Ericsson AB, All Rights Reserved</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + + <title>MNESIA Release Notes</title> + <prepared>Claes Wikström, Hans Nilsson and Håkan Mattsson</prepared> + <docno></docno> + <date>1997-05-27</date> + <rev>1.2</rev> + <file>part_notes_history.sgml</file> + </header> + <description> + <p><em>Mnesia</em> 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> + </description> + <include file="notes_history"></include> +</part> + diff --git a/lib/mnesia/doc/src/ref_man.gif b/lib/mnesia/doc/src/ref_man.gif Binary files differnew file mode 100644 index 0000000000..b13c4efd53 --- /dev/null +++ b/lib/mnesia/doc/src/ref_man.gif diff --git a/lib/mnesia/doc/src/ref_man.xml b/lib/mnesia/doc/src/ref_man.xml new file mode 100644 index 0000000000..417423641d --- /dev/null +++ b/lib/mnesia/doc/src/ref_man.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE application SYSTEM "application.dtd"> + +<application xmlns:xi="http://www.w3.org/2001/XInclude"> + <header> + <copyright> + <year>1997</year><year>2009</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Mnesia Reference Manual</title> + <prepared>Håkan Mattsson, Hans Nilsson, Claes Wikström</prepared> + <responsible></responsible> + <docno></docno> + <approved></approved> + <checked></checked> + <date>1998-04-24</date> + <rev>A</rev> + <file>refman.sgml</file> + </header> + <description> + <p><em>Mnesia</em> 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> + </description> + <xi:include href="mnesia.xml"/> + <xi:include href="mnesia_frag_hash.xml"/> + <xi:include href="mnesia_registry.xml"/> +</application> + diff --git a/lib/mnesia/doc/src/summary.html.src b/lib/mnesia/doc/src/summary.html.src new file mode 100644 index 0000000000..2941a2f46a --- /dev/null +++ b/lib/mnesia/doc/src/summary.html.src @@ -0,0 +1 @@ +A heavy duty real-time distributed database
\ No newline at end of file diff --git a/lib/mnesia/doc/src/user_guide.gif b/lib/mnesia/doc/src/user_guide.gif Binary files differnew file mode 100644 index 0000000000..e6275a803d --- /dev/null +++ b/lib/mnesia/doc/src/user_guide.gif diff --git a/lib/mnesia/doc/src/warning.gif b/lib/mnesia/doc/src/warning.gif Binary files differnew file mode 100644 index 0000000000..96af52360e --- /dev/null +++ b/lib/mnesia/doc/src/warning.gif |