|
14 | 14 | %% Used by Khepri projections and the Mnesia-to-Khepri migration. |
15 | 15 | -export([split_topic_key_binary/1]). |
16 | 16 |
|
| 17 | +-define(KHEPRI_PROJECTION_V3, rabbit_khepri_topic_trie_v3). |
| 18 | + |
17 | 19 | -define(TOPIC_TRIE_PROJECTION, rabbit_khepri_topic_trie_v4). |
18 | 20 | -define(TOPIC_BINDING_PROJECTION, rabbit_khepri_topic_binding_v4). |
19 | 21 |
|
|
32 | 34 | %% |
33 | 35 | %% @private |
34 | 36 |
|
35 | | -match(#resource{virtual_host = VHost, name = XName}, RoutingKey, Opts) -> |
| 37 | +match(#resource{virtual_host = VHost, name = XName} = X, RoutingKey, Opts) -> |
36 | 38 | BKeys = maps:get(return_binding_keys, Opts, false), |
37 | 39 | Words = split_topic_key_binary(RoutingKey), |
38 | | - try |
39 | | - trie_match({VHost, XName}, root, Words, BKeys, []) |
40 | | - catch |
41 | | - error:badarg -> |
42 | | - [] |
| 40 | + case rabbit_khepri:get_effective_topic_binding_projection_version() of |
| 41 | + V when V >= 4 -> |
| 42 | + try |
| 43 | + trie_match({VHost, XName}, root, Words, BKeys, []) |
| 44 | + catch |
| 45 | + error:badarg -> |
| 46 | + [] |
| 47 | + end; |
| 48 | + _ -> |
| 49 | + trie_match_in_khepri(X, Words, BKeys) |
43 | 50 | end. |
44 | 51 |
|
45 | 52 | -spec split_topic_key_binary(RoutingKey) -> Words when |
@@ -162,3 +169,93 @@ format_dest_bkeys([{#resource{kind = queue} = Dest, BKey} | Rest], Acc) -> |
162 | 169 | format_dest_bkeys(Rest, [{Dest, BKey} | Acc]); |
163 | 170 | format_dest_bkeys([{Dest, _BKey} | Rest], Acc) -> |
164 | 171 | format_dest_bkeys(Rest, [Dest | Acc]). |
| 172 | + |
| 173 | +%% -------------------------------------------------------------- |
| 174 | +%% Internal |
| 175 | +%% -------------------------------------------------------------- |
| 176 | + |
| 177 | +-spec add_matched([rabbit_types:binding_destination() | |
| 178 | + {rabbit_types:binding_destination(), BindingArgs :: list()}], |
| 179 | + ReturnBindingKeys :: boolean(), |
| 180 | + match_result()) -> |
| 181 | + match_result(). |
| 182 | +add_matched(Destinations, false, Acc) -> |
| 183 | + Destinations ++ Acc; |
| 184 | +add_matched(DestinationsArgs, true, Acc) -> |
| 185 | + lists:foldl( |
| 186 | + fun({DestQ = #resource{kind = queue}, BindingArgs}, L) -> |
| 187 | + case rabbit_misc:table_lookup(BindingArgs, <<"x-binding-key">>) of |
| 188 | + {longstr, BKey} -> |
| 189 | + [{DestQ, BKey} | L]; |
| 190 | + _ -> |
| 191 | + [DestQ | L] |
| 192 | + end; |
| 193 | + ({DestX, _BindingArgs}, L) -> |
| 194 | + [DestX | L] |
| 195 | + end, Acc, DestinationsArgs). |
| 196 | + |
| 197 | +%% Khepri topic graph |
| 198 | + |
| 199 | +trie_match_in_khepri(X, Words, BKeys) -> |
| 200 | + try |
| 201 | + trie_match_in_khepri(X, root, Words, BKeys, []) |
| 202 | + catch |
| 203 | + error:badarg -> |
| 204 | + [] |
| 205 | + end. |
| 206 | + |
| 207 | +trie_match_in_khepri(X, Node, [], BKeys, ResAcc0) -> |
| 208 | + Destinations = trie_bindings_in_khepri(X, Node, BKeys), |
| 209 | + ResAcc = add_matched(Destinations, BKeys, ResAcc0), |
| 210 | + trie_match_part_in_khepri( |
| 211 | + X, Node, <<"#">>, |
| 212 | + fun trie_match_skip_any_in_khepri/5, [], BKeys, ResAcc); |
| 213 | +trie_match_in_khepri(X, Node, [W | RestW] = Words, BKeys, ResAcc) -> |
| 214 | + lists:foldl(fun ({WArg, MatchFun, RestWArg}, Acc) -> |
| 215 | + trie_match_part_in_khepri( |
| 216 | + X, Node, WArg, MatchFun, RestWArg, BKeys, Acc) |
| 217 | + end, ResAcc, [{W, fun trie_match_in_khepri/5, RestW}, |
| 218 | + {<<"*">>, fun trie_match_in_khepri/5, RestW}, |
| 219 | + {<<"#">>, |
| 220 | + fun trie_match_skip_any_in_khepri/5, Words}]). |
| 221 | + |
| 222 | +trie_match_part_in_khepri(X, Node, Search, MatchFun, RestW, BKeys, ResAcc) -> |
| 223 | + case trie_child_in_khepri(X, Node, Search) of |
| 224 | + {ok, NextNode} -> MatchFun(X, NextNode, RestW, BKeys, ResAcc); |
| 225 | + error -> ResAcc |
| 226 | + end. |
| 227 | + |
| 228 | +trie_match_skip_any_in_khepri(X, Node, [], BKeys, ResAcc) -> |
| 229 | + trie_match_in_khepri(X, Node, [], BKeys, ResAcc); |
| 230 | +trie_match_skip_any_in_khepri(X, Node, [_ | RestW] = Words, BKeys, ResAcc) -> |
| 231 | + trie_match_skip_any_in_khepri( |
| 232 | + X, Node, RestW, BKeys, |
| 233 | + trie_match_in_khepri(X, Node, Words, BKeys, ResAcc)). |
| 234 | + |
| 235 | +trie_child_in_khepri(X, Node, Word) -> |
| 236 | + case ets:lookup( |
| 237 | + ?KHEPRI_PROJECTION_V3, |
| 238 | + #trie_edge{exchange_name = X, |
| 239 | + node_id = Node, |
| 240 | + word = Word}) of |
| 241 | + [#topic_trie_edge_v2{node_id = NextNode}] -> {ok, NextNode}; |
| 242 | + [] -> error |
| 243 | + end. |
| 244 | + |
| 245 | +trie_bindings_in_khepri(X, Node, BKeys) -> |
| 246 | + case ets:lookup( |
| 247 | + ?KHEPRI_PROJECTION_V3, |
| 248 | + #trie_edge{exchange_name = X, |
| 249 | + node_id = Node, |
| 250 | + word = bindings}) of |
| 251 | + [#topic_trie_edge_v2{node_id = {bindings, Bindings}}] -> |
| 252 | + [case BKeys of |
| 253 | + true -> |
| 254 | + {Dest, Args}; |
| 255 | + false -> |
| 256 | + Dest |
| 257 | + end || #binding{destination = Dest, |
| 258 | + args = Args} <- sets:to_list(Bindings)]; |
| 259 | + [] -> |
| 260 | + [] |
| 261 | + end. |
0 commit comments