Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:23
erlang
0933-mnesia-Optimize-locker.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0933-mnesia-Optimize-locker.patch of Package erlang
From 8988ef4e091b2ba5aa72a1dd951020f6e19ea290 Mon Sep 17 00:00:00 2001 From: Dan Gudmundsson <dgud@erlang.org> Date: Mon, 14 Mar 2022 16:05:37 +0100 Subject: [PATCH 2/2] mnesia: Optimize locker Many read locks on the same record was kept in a list, which caused very long lookup and insert times. Optimize by using a tuple of size 1, and use select instead of lookup, this works since it is ordered_set. --- lib/mnesia/src/mnesia_locker.erl | 146 ++++++++++++------------------- 1 file changed, 54 insertions(+), 92 deletions(-) diff --git a/lib/mnesia/src/mnesia_locker.erl b/lib/mnesia/src/mnesia_locker.erl index d6eafd75b3..ccf9b7a06c 100644 --- a/lib/mnesia/src/mnesia_locker.erl +++ b/lib/mnesia/src/mnesia_locker.erl @@ -67,10 +67,10 @@ -record(queue, {oid, tid, op, pid, lucky}). -%% mnesia_held_locks: contain {Oid, MaxLock, [{Op, Tid}]} entries --define(match_oid_held_locks(Oid), {Oid, '_', '_'}). +%% mnesia_held_locks: contain {{Oid, Lock, Tid}} entries +-define(match_oid_held_locks(Oid), {{Oid, '_', '_'}}). %% mnesia_tid_locks: contain {Tid, Oid, Op} entries (bag) --define(match_oid_tid_locks(Tid), {Tid, '_', '_'}). +-define(match_oid_tid_locks(Tid), {{Tid, '_', '_'}}). %% mnesia_sticky_locks: contain {Oid, Node} entries and {Tab, Node} entries (set) -define(match_oid_sticky_locks(Oid),{Oid, '_'}). %% mnesia_lock_queue: contain {queue, Oid, Tid, Op, ReplyTo, WaitForTid} entries (bag) @@ -238,7 +238,7 @@ loop(State) -> loop(State); {From, {is_locked, Oid}} -> - Held = ?ets_lookup(mnesia_held_locks, Oid), + Held = ?ets_match_object(mnesia_held_locks, ?match_oid_held_locks(Oid)), reply(From, Held), loop(State); @@ -259,17 +259,9 @@ loop(State) -> loop(State) end. -set_lock(Tid, Oid, Op, []) -> +set_lock(Tid, Oid, Op) -> ?ets_insert(mnesia_tid_locks, {{Tid, Oid, Op}}), - ?ets_insert(mnesia_held_locks, {Oid, Op, [{Op, Tid}]}); -set_lock(Tid, Oid, read, [{Oid, Prev, Items}]) -> - ?ets_insert(mnesia_tid_locks, {{Tid, Oid, read}}), - ?ets_insert(mnesia_held_locks, {Oid, Prev, [{read, Tid}|Items]}); -set_lock(Tid, Oid, write, [{Oid, _Prev, Items}]) -> - ?ets_insert(mnesia_tid_locks, {{Tid, Oid, write}}), - ?ets_insert(mnesia_held_locks, {Oid, write, [{write, Tid}|Items]}); -set_lock(Tid, Oid, Op, undefined) -> - set_lock(Tid, Oid, Op, ?ets_lookup(mnesia_held_locks, Oid)). + ?ets_insert(mnesia_held_locks, {{Oid, Op, Tid}}). %%%%%%%%%%%%%%%%%%%%%%%%%%% %% Acquire locks @@ -293,14 +285,14 @@ try_lock(Tid, Op, Pid, Oid) -> try_lock(Tid, Op, SimpleOp, Lock, Pid, Oid) -> case can_lock(Tid, Lock, Oid, {no, bad_luck}) of - {yes, Default} -> - Reply = grant_lock(Tid, SimpleOp, Lock, Oid, Default), + yes -> + Reply = grant_lock(Tid, SimpleOp, Lock, Oid), reply(Pid, Reply); - {{no, Lucky},_} -> + {no, Lucky} -> C = #cyclic{op = SimpleOp, lock = Lock, oid = Oid, lucky = Lucky}, ?dbg("Rejected ~p ~p ~p ~p ~n", [Tid, Oid, Lock, Lucky]), reply(Pid, {not_granted, C}); - {{queue, Lucky},_} -> + {queue, Lucky} -> ?dbg("Queued ~p ~p ~p ~p ~n", [Tid, Oid, Lock, Lucky]), %% Append to queue: Nice place for trace output ?ets_insert(mnesia_lock_queue, @@ -309,16 +301,16 @@ try_lock(Tid, Op, SimpleOp, Lock, Pid, Oid) -> ?ets_insert(mnesia_tid_locks, {{Tid, Oid, {queued, Op}}}) end. -grant_lock(Tid, read, Lock, Oid = {Tab, Key}, Default) +grant_lock(Tid, read, Lock, Oid = {Tab, Key}) when Key /= ?ALL, Tab /= ?GLOBAL -> case node(Tid#tid.pid) == node() of true -> - set_lock(Tid, Oid, Lock, Default), + set_lock(Tid, Oid, Lock), {granted, lookup_in_client}; false -> try Val = mnesia_lib:db_get(Tab, Key), %% lookup as well - set_lock(Tid, Oid, Lock, Default), + set_lock(Tid, Oid, Lock), {granted, Val} catch _:_Reason -> %% Table has been deleted from this node, @@ -328,19 +320,19 @@ grant_lock(Tid, read, Lock, Oid = {Tab, Key}, Default) {not_granted, C} end end; -grant_lock(Tid, {ix_read,IxKey,Pos}, Lock, Oid = {Tab, _}, Default) -> +grant_lock(Tid, {ix_read,IxKey,Pos}, Lock, Oid = {Tab, _}) -> try Res = ix_read_res(Tab, IxKey,Pos), - set_lock(Tid, Oid, Lock, Default), + set_lock(Tid, Oid, Lock), {granted, Res, [?ALL]} catch _:_ -> {not_granted, {no_exists, Tab, {index, [Pos]}}} end; -grant_lock(Tid, read, Lock, Oid, Default) -> - set_lock(Tid, Oid, Lock, Default), +grant_lock(Tid, read, Lock, Oid) -> + set_lock(Tid, Oid, Lock), {granted, ok}; -grant_lock(Tid, write, Lock, Oid, Default) -> - set_lock(Tid, Oid, Lock, Default), +grant_lock(Tid, write, Lock, Oid) -> + set_lock(Tid, Oid, Lock), granted. %% 1) Impose an ordering on all transactions favour old (low tid) transactions @@ -351,35 +343,28 @@ grant_lock(Tid, write, Lock, Oid, Default) -> %% handle tablocks in queue as they had locks on unlocked records. can_lock(Tid, read, Oid = {Tab, Key}, AlreadyQ) when Key /= ?ALL -> - ObjLocks = ?ets_lookup(mnesia_held_locks, Oid), - TabLocks = ?ets_lookup(mnesia_held_locks, {Tab, ?ALL}), - {check_lock(Tid, Oid, - filter_write(ObjLocks), - filter_write(TabLocks), - yes, AlreadyQ, read), - ObjLocks}; + ObjLocks = ets:select(mnesia_held_locks, [{ {{Oid, write, '$1'}}, [], ['$1'] }]), + TabLocks = ets:select(mnesia_held_locks, [{ {{{Tab, ?ALL}, write, '$1'}}, [], ['$1'] }]), + check_lock(Tid, Oid, TabLocks, ObjLocks, yes, AlreadyQ, read); can_lock(Tid, read, Oid, AlreadyQ) -> % Whole tab Tab = element(1, Oid), - ObjLocks = ?ets_match_object(mnesia_held_locks, {{Tab, '_'}, write, '_'}), - {check_lock(Tid, Oid, ObjLocks, [], yes, AlreadyQ, read), undefined}; + ObjLocks = ets:select(mnesia_held_locks, [{ {{{Tab, '_'}, write, '$1'}}, [], ['$1'] }]), + check_lock(Tid, Oid, ObjLocks, [], yes, AlreadyQ, read); can_lock(Tid, write, Oid = {Tab, Key}, AlreadyQ) when Key /= ?ALL -> - ObjLocks = ?ets_lookup(mnesia_held_locks, Oid), - TabLocks = ?ets_lookup(mnesia_held_locks, {Tab, ?ALL}), - {check_lock(Tid, Oid, ObjLocks, TabLocks, yes, AlreadyQ, write), ObjLocks}; + ObjLocks = ets:select(mnesia_held_locks, [{ {{Oid, '_', '$1'}}, [], ['$1'] }]), + TabLocks = ets:select(mnesia_held_locks, [{ {{{Tab, ?ALL}, '_', '$1'}}, [], ['$1'] }]), + check_lock(Tid, Oid, TabLocks, ObjLocks, yes, AlreadyQ, write); can_lock(Tid, write, Oid, AlreadyQ) -> % Whole tab Tab = element(1, Oid), - ObjLocks = ?ets_match_object(mnesia_held_locks, ?match_oid_held_locks({Tab, '_'})), - {check_lock(Tid, Oid, ObjLocks, [], yes, AlreadyQ, write), undefined}. - -filter_write([{_, read, _}]) -> []; -filter_write(Res) -> Res. + Locks = ets:select(mnesia_held_locks, [{ {{{Tab, '_'}, '_', '$1'}}, [], ['$1'] }]), + check_lock(Tid, Oid, Locks, [], yes, AlreadyQ, write). %% Check held locks for conflicting locks -check_lock(Tid, Oid, [{_, _, Lock} | Locks], TabLocks, _X, AlreadyQ, Type) -> - case can_queue(Lock, Tid, Oid, _X) of +check_lock(Tid, Oid, [LockTid | Locks], TabLocks, _X, AlreadyQ, Type) -> + case can_queue(LockTid, Tid, Oid, _X) of {no, _} = Res -> Res; Res -> @@ -419,25 +404,24 @@ check_lock(Tid, Oid = {Tab, Key}, [], [], X, AlreadyQ, Type) -> check_lock(Tid, Oid, [], TabLocks, X, AlreadyQ, Type) -> check_lock(Tid, Oid, TabLocks, [], X, AlreadyQ, Type). -can_queue([{_Op, Tid}|Locks], Tid, Oid, Res) -> - can_queue(Locks, Tid, Oid, Res); -can_queue([{Op, WaitForTid}|Locks], Tid, Oid = {Tab, _}, _) -> +can_queue(Tid, Tid, _Oid, Res) -> + Res; +can_queue(WaitForTid, Tid, Oid = {Tab, _}, _) -> case allowed_to_be_queued(WaitForTid,Tid) of true when Tid#tid.pid == WaitForTid#tid.pid -> - dbg_out("Spurious lock conflict ~w ~w: ~w -> ~w~n", - [Oid, Op, Tid, WaitForTid]), + dbg_out("Spurious lock conflict ~w: ~w -> ~w~n", + [Oid, Tid, WaitForTid]), HaveQ = (ets:lookup(mnesia_lock_queue, Oid) /= []) orelse (ets:lookup(mnesia_lock_queue,{Tab,?ALL}) /= []), case HaveQ of - true -> {no, WaitForTid}; - false -> can_queue(Locks, Tid, Oid, {queue, WaitForTid}) + true -> {no, WaitForTid}; + false -> {queue, WaitForTid} end; true -> - can_queue(Locks, Tid, Oid, {queue, WaitForTid}); + {queue, WaitForTid}; false -> {no, WaitForTid} - end; -can_queue([], _, _, Res) -> Res. + end. %% True if WaitForTid > Tid -> % Important order allowed_to_be_queued(WaitForTid, Tid) -> @@ -492,14 +476,14 @@ set_read_lock_on_all_keys(Tid, From, Tab, IxKey, Pos) -> Op = {ix_read,IxKey, Pos}, Lock = read, case can_lock(Tid, Lock, Oid, {no, bad_luck}) of - {yes, Default} -> - Reply = grant_lock(Tid, Op, Lock, Oid, Default), + yes -> + Reply = grant_lock(Tid, Op, Lock, Oid), reply(From, Reply); - {{no, Lucky},_} -> + {no, Lucky} -> C = #cyclic{op = Op, lock = Lock, oid = Oid, lucky = Lucky}, ?dbg("Rejected ~p ~p ~p ~p ~n", [Tid, Oid, Lock, Lucky]), reply(From, {not_granted, C}); - {{queue, Lucky},_} -> + {queue, Lucky} -> ?dbg("Queued ~p ~p ~p ~p ~n", [Tid, Oid, Lock, Lucky]), %% Append to queue: Nice place for trace output ?ets_insert(mnesia_lock_queue, @@ -521,7 +505,7 @@ release_remote_non_pending(Node, Pending) -> %% running at the failed node and also simply remove all %% queue'd requests back to the failed node - AllTids0 = ?ets_match(mnesia_tid_locks, {{'$1', '_', '_'}}), + AllTids0 = ?ets_match(mnesia_tid_locks, ?match_oid_held_locks('$1')), AllTids = lists:usort(AllTids0), Tids = [T || [T] <- AllTids, Node == node(T#tid.pid), not lists:member(T, Pending)], do_release_tids(Tids). @@ -558,23 +542,8 @@ release_locks([]) -> release_lock({Tid, Oid, {queued, _}}) -> ?ets_match_delete(mnesia_lock_queue, #queue{oid=Oid, tid = Tid, op = '_', pid = '_', lucky = '_'}); -release_lock({_Tid, Oid, write}) -> - ?ets_delete(mnesia_held_locks, Oid); -release_lock({Tid, Oid, read}) -> - case ?ets_lookup(mnesia_held_locks, Oid) of - [{Oid, Prev, Locks0}] -> - case remove_tid(Locks0, Tid, []) of - [] -> ?ets_delete(mnesia_held_locks, Oid); - Locks -> ?ets_insert(mnesia_held_locks, {Oid, Prev, Locks}) - end; - [] -> ok - end. - -remove_tid([{_Op, Tid}|Ls], Tid, Acc) -> - remove_tid(Ls,Tid, Acc); -remove_tid([Keep|Ls], Tid, Acc) -> - remove_tid(Ls,Tid, [Keep|Acc]); -remove_tid([], _, Acc) -> Acc. +release_lock({Tid, Oid, Op}) -> + ?ets_delete(mnesia_held_locks, {Oid, Op, Tid}). rearrange_queue([{_Tid, {Tab, Key}, _} | Locks]) -> if @@ -640,18 +609,18 @@ try_waiter({queue, Oid, Tid, Op, ReplyTo, _}) -> try_waiter(Oid, Op, SimpleOp, Lock, ReplyTo, Tid) -> case can_lock(Tid, Lock, Oid, {queue, bad_luck}) of - {yes, Default} -> + yes -> %% Delete from queue: Nice place for trace output ?ets_match_delete(mnesia_lock_queue, #queue{oid=Oid, tid = Tid, op = Op, pid = ReplyTo, lucky = '_'}), - Reply = grant_lock(Tid, SimpleOp, Lock, Oid, Default), + Reply = grant_lock(Tid, SimpleOp, Lock, Oid), reply(ReplyTo,Reply), locked; - {{queue, _Why}, _} -> + {queue, _Why} -> ?dbg("Keep ~p ~p ~p ~p~n", [Tid, Oid, Lock, _Why]), queued; % Keep waiter in queue - {{no, Lucky}, _} -> + {no, Lucky} -> C = #cyclic{op = SimpleOp, lock = Lock, oid = Oid, lucky = Lucky}, verbose("** WARNING ** Restarted transaction, possible deadlock in lock queue ~w: cyclic = ~w~n", [Tid, C]), @@ -1167,22 +1136,15 @@ get_held_locks(Tab) when is_atom(Tab) -> {?MODULE, _Node, Locks} -> case Locks of [] -> []; - [{Oid, _Prev, What}] -> What + _ -> [{Op, Tid} || {{_Oid, Op, Tid}} <- Locks] end end. - -rewrite_locks([{Oid, _, Ls}|Locks], Acc0) -> - Acc = rewrite_locks(Ls, Oid, Acc0), - rewrite_locks(Locks, Acc); +rewrite_locks([{{Oid, Op, Tid}}|Locks], Acc) -> + rewrite_locks(Locks, [{Oid, Op, Tid}|Acc]); rewrite_locks([], Acc) -> lists:reverse(Acc). -rewrite_locks([{Op, Tid}|Ls], Oid, Acc) -> - rewrite_locks(Ls, Oid, [{Oid, Op, Tid}|Acc]); -rewrite_locks([], _, Acc) -> - Acc. - get_lock_queue() -> ?MODULE ! {get_table, self(), mnesia_lock_queue}, Q = receive {mnesia_lock_queue, Locks} -> Locks after 5000 -> [] end, -- 2.34.1
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor