diff options
| author | Michael Davis <mcarsondavis@gmail.com> | 2023-01-31 12:30:23 -0600 |
|---|---|---|
| committer | Diana Parra Corbacho <dparracorbac@vmware.com> | 2023-05-12 11:31:16 +0200 |
| commit | cfeea2622de3d413cba26534087024d6f3680914 (patch) | |
| tree | 1719c7b3fd6718778493e1f9285d55333326aa6b | |
| parent | e0c535ae7dd491dca2bad2e952489b34fd231524 (diff) | |
| download | rabbitmq-server-git-cfeea2622de3d413cba26534087024d6f3680914.tar.gz | |
projections: Refactor rabbit_khepri_bindings table layout
The old shape used the source exchange as the key in a bag table. This
made it cheaper to list all bindings for an exchange but it caused a
bad regression for inserting many bindings, for example in a definition
import. If all bindings are for the same exchange, inserting N bindings
has exponential time complexity when using a bag compared to the
roughly linear time complexity using a set.
This reflects the shape of the `rabbit_route` table from mnesia: the
whole `#binding{}` record is the key of the table and the table is
a set. With this change, the time taken to import definitions roughly
matches mnesia.
| -rw-r--r-- | deps/rabbit/src/rabbit_db_binding.erl | 34 | ||||
| -rw-r--r-- | deps/rabbit/src/rabbit_khepri.erl | 4 |
2 files changed, 21 insertions, 17 deletions
diff --git a/deps/rabbit/src/rabbit_db_binding.erl b/deps/rabbit/src/rabbit_db_binding.erl index 3e3779bb92..b75fdf4f9f 100644 --- a/deps/rabbit/src/rabbit_db_binding.erl +++ b/deps/rabbit/src/rabbit_db_binding.erl @@ -431,10 +431,11 @@ get_all_in_mnesia(VHost) -> [B || #route{binding = B} <- rabbit_db:list_in_mnesia(?MNESIA_TABLE, Match)]. get_all_in_khepri(VHost) -> - Path = khepri_routes_path() ++ [VHost, rabbit_khepri:if_has_data_wildcard()], - lists:foldl(fun(SetOfBindings, Acc) -> - sets:to_list(SetOfBindings) ++ Acc - end, [], rabbit_db:list_in_khepri(Path)). + VHostResource = rabbit_misc:r(VHost, '_'), + Match = #route{binding = #binding{source = VHostResource, + destination = VHostResource, + _ = '_'}}, + [B || #route{binding = B} <- ets:match_object(rabbit_khepri_bindings, Match)]. -spec get_all(Src, Dst, Reverse) -> [Binding] when Src :: rabbit_types:binding_source(), @@ -462,10 +463,10 @@ get_all_in_mnesia(SrcName, DstName, Reverse) -> mnesia:async_dirty(Fun). get_all_in_khepri(SrcName, DstName) -> - Match = #binding{source = SrcName, - destination = DstName, - _ = '_'}, - ets:match_object(rabbit_khepri_bindings, Match). + MatchHead = #route{binding = #binding{source = SrcName, + destination = DstName, + _ = '_'}}, + [B || #route{binding = B} <- ets:match_object(rabbit_khepri_bindings, MatchHead)]. %% ------------------------------------------------------------------- %% get_all_for_source(). @@ -504,7 +505,8 @@ list_for_route(Route, true) -> end. get_all_for_source_in_khepri(Resource) -> - ets:lookup(rabbit_khepri_bindings, Resource). + Route = #route{binding = #binding{source = Resource, _ = '_'}}, + [B || #route{binding = B} <- ets:match_object(rabbit_khepri_bindings, Route)]. %% ------------------------------------------------------------------- %% get_all_for_destination(). @@ -533,10 +535,10 @@ get_all_for_destination_in_mnesia(Dst) -> mnesia:async_dirty(Fun). get_all_for_destination_in_khepri(Destination) -> - %% TODO: projection for bindings indexed by destination? - Match = #binding{destination = Destination, - _ = '_'}, - ets:match_object(rabbit_khepri_bindings, Match). + %% TODO: projection for bindings indexed by destination? + Match = #route{binding = #binding{destination = Destination, + _ = '_'}}, + [B || #route{binding = B} <- ets:match_object(rabbit_khepri_bindings, Match)]. %% ------------------------------------------------------------------- %% fold(). @@ -609,8 +611,10 @@ match_in_mnesia(SrcName, Match) -> Routes, Match(Binding)]. match_in_khepri(SrcName, Match) -> - Routes = ets:lookup(rabbit_khepri_bindings, SrcName), - [Dest || Binding = #binding{destination = Dest} <- + MatchHead = #route{binding = #binding{source = SrcName, + _ = '_'}}, + Routes = ets:select(rabbit_khepri_bindings, [{MatchHead, [], [['$_']]}]), + [Dest || [#route{binding = Binding = #binding{destination = Dest}}] <- Routes, Match(Binding)]. %% Routing - HOT CODE PATH diff --git a/deps/rabbit/src/rabbit_khepri.erl b/deps/rabbit/src/rabbit_khepri.erl index fb1a4ea8cd..1c11dd1edd 100644 --- a/deps/rabbit/src/rabbit_khepri.erl +++ b/deps/rabbit/src/rabbit_khepri.erl @@ -654,10 +654,10 @@ register_simple_projection(Name, PathPattern, KeyPos) -> register_rabbit_bindings_projection() -> MapFun = fun(_Path, Binding) -> - Binding + #route{binding = Binding} end, ProjectionFun = projection_fun_for_sets(MapFun), - Options = #{type => bag, keypos => #binding.source}, + Options = #{keypos => #route.binding}, Projection = khepri_projection:new( rabbit_khepri_bindings, ProjectionFun, Options), PathPattern = [rabbit_db_binding, |
