Erlang: Mnesia: search and update based on non-key fields

I have a table in mnesia and I need to update individual fields in the records in it. According to Erlang: Mnesia: updating a single field value in a string if I do something like:

update_a(Tab, Key, Value) -> fun() -> [P] = mnesia:wread({Tab, Key}), mnesia:write(Tab, P#rec{a=Value}, write) end. 

Now, as I understand it, the above code reads the Key based P record, acquiring the record of the record in the record, so that no other transactions change this record while reading and writing (or, in short, updated). So far so good.

Now my requirement is that I need to read records based on both Key and another field in the table, and then update. The function that will do this is mnesia:match_object . The problem is that the function only supports read locks, not write locks, according to http://www.erlang.org/doc/man/mnesia.html#match_object-3 .

The consequence of this is that in the above function I had to use mnesia: match_object, I will get (group) of records (s), all with read locks. After I read the records, I need to perform some checks on the received data, and then write the updated record only if the condition is met. Now suppose that there are two parallel transactions, T1 and T2, initiated by two different sources. Both T1 and T2 simultaneously use the same record. Since they are considered locked, both T1 and T2 can read records in parallel. Both T1 and T2 will perform the same check on the same record, and if the condition is met, both will continue to perform the update. But in my code, if T1 and T2 were executed in series, T1 would make changes to the record in T2 as well, he would read these changed records, and the condition would not get the result, and the update would not be done.

In short, I need to write lock records that are returned by mnesia: match_object. The documentation clearly states that only read lock is supported. Are there any alternatives?

UPDATE: I was experimenting a bit, and a possible solution that I thought might be to use complex keys. Suppose I have data written to a table, for example:

 mnesia:transaction(fun() -> mnesia:write(mytable, #rec{i={1,2}, a=2, b=3}, write) end). 

Is there a way to search for entries using not caring?

I tried them, but both returned empty results:

 mnesia:transaction(fun()-> mnesia:read(mytable, {1,'_'}, read) end). mnesia:transaction(fun()-> mnesia:read(mytable, {1,_}, read) end). 
+6
database erlang mnesia
source share
4 answers

You have nothing to worry about. From the mnesia documentation:

Reading locks can be general, which means that if one transaction succeeds in obtaining a read lock on an item, other transactions can also get a read lock on the same item. However, if someone has a read lock, no one can get a write lock on the same element. If someone has a write lock, no one can get a read lock and write lock in the same element.

If a transaction has a read lock on an object, this object cannot be edited by another transaction.

Let's say you have two transactions: T1 and T2, which are executed in parallel:

  • T1 does mnesia:match_object and gets a read lock for all returned objects.
  • T2 does the equivalent of mnesia:match_object and gets a read lock on the same objects.
  • T2 tries to get a write lock on objects (for editing it).
  • Mnesia will automatically abort T2 to try again later.
  • T1 gets a write lock for objects and edits them.
  • T1 ends.
  • Mnesia repeats T2.

Note that T2 can be repeated several times, depending on how long T1 takes (i.e. release its locks).

According to my tests, the mnesia:match_object blocking behavior is incompatible. For example, mnesia:match_object(mytable, {mytable, 2, '_'}, LockType) will only lock the record with key 2, but mnesia:match_object(mytable, {mytable, '_', test}, LockType) block all table.

Also note that the documentation is incorrect, mnesia:match_object(Table, Pattern, write) works, and seems to follow the same pattern as read, i.e. if you specify a key, only the corresponding record will be locked; if you do not specify a key, the entire table will be locked for writing.

You can verify this yourself by doing something like this:

 test() -> mnesia:transaction(fun()-> Table = mytable, Matches = mnesia:match_object(Table, {Table, 2, '_'}, write), io:format("matched: ~p~n", [Matches]), spawn(fun()->mnesia:transaction(fun()-> io:format("trying to read~n",[]), io:format("read: ~p~n", [mnesia:read(Table, 2, read)]) end) end), timer:sleep(1000), RereadMatches = lists:map(fun(#mytable{id=Id}) -> mnesia:read(Table, Id, write) end, Matches), io:format("reread matches: ~p~n", [RereadMatches]) end). 

By changing the pattern and lock type passed to match_object and the key number and lock type passed to mnesia:read in the spawned process (or using mnesia:write ), you can test various locking actions.

Addendum: See this post by Ulf Wieger on the same topic.

Appendix 2: See the โ€œInsulationโ€ section of the Mnesia User Guide.

Edit: Above everything was done in the set table, match_object locking behavior may be different in the bag type table.

+1
source share

Although it returns them under a read lock, you can update them after writing. All read / wonderful thing was just an optimization.

You can use the ordered_set table to implement this shadow connection using compound keys .

+2
source share

Like this,

  case mnesia:match_object(#rec{name=Name, _='_'}) of [] -> not_found; [First|Rest] -> something end, 
+2
source share

Can't you just lock the table in your transaction in advance with mnesia:write_lock_table(Tab) ?

0
source share

All Articles