Mnesia, mnesia_frag, and table locks using index_match_object et al.
Scott Lystig Fritchie
fritchie@REDACTED
Tue Feb 28 06:14:00 CET 2006
Greetings. I just spent some time experimenting with a Mnesia table
like this one:
-record(thing, {
primary_key,
secondary_key,
other_attribute_1,
other_attribute_2
}).
ok = mnesia:start(),
{atomic, ok} =
mnesia:create_table(thing, [{attributes, record_info(fields, thing)},
{ram_copies, [node()]}, {type, set}]),
{atomic, ok} = mnesia:add_table_index(thing, secondary_key).
{atomic, ok} = mnesia:change_table_frag(thing, {activate, []}),
[{atomic, ok} = mnesia:change_table_frag(thing, {add_frag, [node()]}) ||
_I <- lists:seq(1, 4)],
MatchHead = #thing{secondary_key = bound_atom_value, _ = '_'},
Thing = #thing{primary_key = blah_key, secondary_key = bound_atom_value},
F = fun() -> mnesia:match_object(thing, MatchHead, write),
timer:sleep(10*1000),
end,
mnesia:activity(transaction, F, [], mnesia_frag).
If I run this snippet of code, then sneak over onto another shell on
the same node and run mnesia:info(), I see this:
(foo1@REDACTED)2> mnesia:info().
---> Processes holding locks <---
Lock: {{thing,'______WHOLETABLE_____'},write,{tid,10,<4491.36.0>}}
Lock: {{thing_frag4,'______WHOLETABLE_____'},write,{tid,10,<4491.36.0>}}
Lock: {{thing_frag2,'______WHOLETABLE_____'},write,{tid,10,<4491.36.0>}}
Lock: {{thing_frag5,'______WHOLETABLE_____'},write,{tid,10,<4491.36.0>}}
Lock: {{thing_frag3,'______WHOLETABLE_____'},write,{tid,10,<4491.36.0>}}
---> Processes waiting for locks <---
[...]
The mnesia:index_read() function doesn't have an option to specify a
write lock. The mnesia:index_match_object() function only support
'read' locking, but it also takes whole table locks. Also,
mnesia:select() (using a full "match spec" using a guard list of []
and a result list of ['$_']) and mnesia:match_object() take whole
table locks, too.
After recovering from shock, I looked at mnesia_frag.erl. Sure
enough, mnesia_frag:do_select/5 unconditionally takes a whole table
lock.(*) Bummer.
The search element in MatchHead is indeed bound. I was expecting that
I'd see something like this instead:
---> Processes holding locks <---
Lock: {{thing,bound_atom_value},write,{tid,91994,<0.2828.0>}}
Lock: {{thing_frag2,bound_atom_value},write,{tid,91994,<0.2828.0>}}
[... one for each table fragment ...]
Question: Is it really necessary to take whole table locks in cases
like this?
For my purpose, I would like to insert a single record into the
'thing' table *and* guarantee that both primary_key and secondary_key
are unique. The former is trivial: the table type is 'set'. I was
hoping to do the latter by the following (simplifed!) without whole
table locks:
%% Borrow the bindings for Thing and MatchHead from above
[] = mnesia:read({thing, blah_key}),
[] = mnesia:match_object(thing, MatchHead, write),
ok = mnesia:write(Thing).
I don't want to be forced to create a separate Mnesia table based on:
-record(secondary_to_primary, {
secondary_key, % matches secondary_key in 'thing' table
primary_key % matches primary_key in 'thing' table
}).
... where I take a write lock on secondary_to_primary's primary key.
So ... Am I smoking crack, wishing to do something crazy?
-Scott
(*) I'm assuming that all the Mnesia functions mentioned above, when
used with mnesia_frag, eventually get funnelled into
mnesia_frag:do_select/5.
More information about the erlang-questions
mailing list