summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Davis <mcarsondavis@gmail.com>2023-01-31 12:30:23 -0600
committerDiana Parra Corbacho <dparracorbac@vmware.com>2023-05-12 11:31:16 +0200
commitcfeea2622de3d413cba26534087024d6f3680914 (patch)
tree1719c7b3fd6718778493e1f9285d55333326aa6b
parente0c535ae7dd491dca2bad2e952489b34fd231524 (diff)
downloadrabbitmq-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.erl34
-rw-r--r--deps/rabbit/src/rabbit_khepri.erl4
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,