Mnesia patch, was: Re: Mnesia and partitioning big tables: Doc, it hurts when...
Scott Lystig Fritchie
fritchie@REDACTED
Sat Sep 2 07:55:35 CEST 2006
Following up on my earlier message...
... I'd mentioned that I'd had a Mnesia 'disc_copies' table with 800K
entries in it. size(term_to_binary(Record)) = 308, on average. The
inital attempt to add a fragment via:
mnesia:change_table_frag('Profile', {add_frag, [node()]})
... well, I aborted it after over an hour. The VM was unusable: each
line of output by mnesia:info() took 15-20 seconds to appear.
Using the patch below, the first mnesia:change_table_frag/2 call takes
24 seconds. Adding the 20th fragment takes 1.6 seconds.
The patch is relative to the R10B-9 release. It patches R11B-1 with
only a few "offset X lines" warnings, but I've only tested it with
R10B-9.
It would be really, really nice if:
* Another set of eyes can find any bugs?
* There's a possible table handle leak, I guess, if you're
only 1 table away from the limit on # of ETS tables....
* If no bugs, inclusion into the Mnesia source distribution
-Scott
--- snip --- snip --- snip --- snip --- snip --- snip ---
diff -ur ./mnesia.hrl /home/fritchie/src/mnesia-exp/src/mnesia.hrl
--- ./mnesia.hrl 2006-01-09 13:48:16.000000000 -0600
+++ /home/fritchie/src/mnesia-exp/src/mnesia.hrl 2006-09-02 00:06:59.004182832 -0500
@@ -20,7 +20,7 @@
-define(ets_lookup(Tab, Key), ets:lookup(Tab, Key)).
-define(ets_lookup_element(Tab, Key, Pos), ets:lookup_element(Tab, Key, Pos)).
--define(ets_insert(Tab, Rec), ets:insert(Tab, Rec)).
+-define(ets_insert(Tab, Rec), mnesia_lib:redirect_ets_insert(Rec, Tab)).
-define(ets_delete(Tab, Key), ets:delete(Tab, Key)).
-define(ets_match_delete(Tab, Pat), ets:match_delete(Tab, Pat)).
-define(ets_match_object(Tab, Pat), ets:match_object(Tab, Pat)).
@@ -33,8 +33,9 @@
-define(ets_prev(Tab, Key), ets:prev(Tab, Key)).
-define(ets_slot(Tab, Pos), ets:slot(Tab, Pos)).
-define(ets_new_table(Tab, Props), ets:new(Tab, Props)).
--define(ets_delete_table(Tab), ets:delete(Tab)).
+-define(ets_delete_table(Tab), mnesia_lib:redirect_ets_delete(Tab)).
-define(ets_fixtable(Tab, Bool), ets:fixtable(Tab, Bool)).
+-define(ets_tab2list(Tab), ets:tab2list(Tab)).
-define(catch_val(Var), (catch ?ets_lookup_element(mnesia_gvar, Var, 2))).
diff -ur ./mnesia_lib.erl /home/fritchie/src/mnesia-exp/src/mnesia_lib.erl
--- ./mnesia_lib.erl 2006-01-09 13:48:16.000000000 -0600
+++ /home/fritchie/src/mnesia-exp/src/mnesia_lib.erl 2006-09-02 00:19:53.790397424 -0500
@@ -151,6 +151,9 @@
view/2,
warning/2,
+ redirect_ets_insert/2,
+ redirect_ets_delete/1,
+
is_debug_compiled/0,
activate_debug_fun/5,
deactivate_debug_fun/3,
@@ -1304,4 +1307,25 @@
is_debug_compiled() -> false.
-endif.
-
+redirect_ets_insert(Rec, Tab) ->
+ TabName = ets:info(Tab, name),
+ if TabName == mnesia_trans_store, is_tuple(Rec), element(1, Rec) == op ->
+ [{etab_set, Etab_Set}] = ets:lookup(Tab, etab_set),
+ %% TODO: Is it worth a test if {op, look_elsewhere} is already
+ %% in Tab and insert only if it doesn't exist?
+ ets:insert(Tab, {op, look_elsewhere}),
+ %% Need a unique key, make_ref() is perfect.
+ ets:insert(Etab_Set, {make_ref(), Rec});
+ true ->
+ ets:insert(Tab, Rec)
+ end.
+
+redirect_ets_delete(Tab) ->
+ TabName = ets:info(Tab, name),
+ if TabName == mnesia_trans_store ->
+ [{etab_set, Etab_Set}] = ?ets_lookup(Tab, etab_set),
+ ets:delete(Etab_Set);
+ true ->
+ ok
+ end,
+ ets:delete(Tab).
diff -ur ./mnesia_tm.erl /home/fritchie/src/mnesia-exp/src/mnesia_tm.erl
--- ./mnesia_tm.erl 2006-01-09 13:48:16.000000000 -0600
+++ /home/fritchie/src/mnesia-exp/src/mnesia_tm.erl 2006-09-02 00:19:43.331987344 -0500
@@ -219,15 +219,17 @@
end;
{From, start_outer} -> %% Create and associate ets_tab with Tid
- case catch ?ets_new_table(mnesia_trans_store, [bag, public]) of
+ case catch ({?ets_new_table(mnesia_trans_store, [bag, public]),
+ ?ets_new_table(mnesia_trans_store_Set, [ordered_set, public])}) of
{'EXIT', Reason} -> %% system limit
Msg = "Cannot create an ets table for the "
"local transaction store",
reply(From, {error, {system_limit, Msg, Reason}}, State);
- Etab ->
+ {Etab, Etab_Set} ->
tmlink(From),
C = mnesia_recover:incr_trans_tid_serial(),
?ets_insert(Etab, {nodes, node()}),
+ ?ets_insert(Etab, {etab_set, Etab_Set}),
Tid = #tid{pid = tmpid(From), counter = C},
A2 = [{Tid , [Etab]} | Coordinators],
S2 = State#state{coordinators = A2},
@@ -334,12 +336,14 @@
end;
{From, {add_store, Tid}} -> %% new store for nested transaction
- case catch ?ets_new_table(mnesia_trans_store, [bag, public]) of
+ case catch ({?ets_new_table(mnesia_trans_store, [bag, public]),
+ ?ets_new_table(mnesia_trans_store_Set, [ordered_set, public])}) of
{'EXIT', Reason} -> %% system limit
Msg = "Cannot create an ets table for a nested "
"local transaction store",
reply(From, {error, {system_limit, Msg, Reason}}, State);
- Etab ->
+ {Etab, Etab_Set} ->
+ ?ets_insert(Etab, {etab_set, Etab_Set}),
A2 = add_coord_store(Coordinators, Tid, Etab),
reply(From, {new_store, Etab},
State#state{coordinators = A2})
@@ -1187,7 +1191,10 @@
P2 = prepare_items(Tid, Tab, Key, Items, Prep),
do_arrange(Tid, Store, ?ets_next(Store, Oid), P2, N + 1);
do_arrange(Tid, Store, SchemaKey, Prep, N) when SchemaKey == op ->
- Items = ?ets_lookup(Store, SchemaKey), %% Store is a bag
+ %% Get the list of 'op' tuples that used to be stored in the Store bag,
+ %% then strip off each 2-tuple wrapper.
+ [{etab_set, Etab_Set}] = ?ets_lookup(Store, etab_set),
+ Items = [Val || {_Ref, Val} <- ?ets_tab2list(Etab_Set)],
P2 = prepare_schema_items(Tid, Items, Prep),
do_arrange(Tid, Store, ?ets_next(Store, SchemaKey), P2, N + 1);
do_arrange(Tid, Store, RestoreKey, Prep, N) when RestoreKey == restore_op ->
More information about the erlang-questions
mailing list