170170% % equivalent cluster
171171-export ([khepri_db_migration_enable /1 ,
172172 khepri_db_migration_post_enable /1 ,
173+ enable_feature_flag /1 ,
173174 is_enabled /0 , is_enabled /1 ,
174175 get_feature_state /0 , get_feature_state /1 ,
175176 handle_fallback /1 ]).
@@ -332,8 +333,10 @@ init(IsVirgin) ->
332333 " up to the Raft cluster leader" , [],
333334 #{domain => ? RMQLOG_DOMAIN_DB }),
334335 ok ?= case IsVirgin of
335- true -> register_projections ();
336- false -> ok
336+ true ->
337+ register_projections ();
338+ false ->
339+ register_4_2_0_projections ()
337340 end ,
338341 % % Delete transient queues on init.
339342 % % Note that we also do this in the
@@ -1318,26 +1321,34 @@ register_projections() ->
13181321 fun register_rabbit_per_vhost_runtime_parameters_projection /0 ,
13191322 fun register_rabbit_user_permissions_projection /0 ,
13201323 fun register_rabbit_bindings_projection /0 ,
1321- fun register_rabbit_index_route_projection /0 ,
1322- fun register_rabbit_source_route_projection /0 ,
1324+ fun register_rabbit_route_by_source_key_projection /0 ,
1325+ fun register_rabbit_route_by_source_projection /0 ,
13231326 fun register_rabbit_topic_graph_projection /0 ],
1324- rabbit_misc :for_each_while_ok (
1325- fun (RegisterFun ) ->
1326- case RegisterFun () of
1327- ok ->
1328- ok ;
1329- % % Before Khepri v0.13.0, `khepri:register_projection/1,2,3`
1330- % % would return `{error, exists}` for projections which
1331- % % already exist.
1332- {error , exists } ->
1333- ok ;
1334- % % In v0.13.0+, Khepri returns a `?khepri_error(..)` instead.
1335- {error , {khepri , projection_already_exists , _Info }} ->
1336- ok ;
1337- {error , _ } = Error ->
1338- Error
1339- end
1340- end , RegFuns ).
1327+ rabbit_misc :for_each_while_ok (fun register_projection /1 , RegFuns ).
1328+
1329+ % % This function registers projections introduced in 4.2.0. In a mixed version
1330+ % % cluster, these new projections will appear but won't be used on older nodes.
1331+ % % This function can be deleted after feature flag rabbitmq_4.2.0 becomes required.
1332+ register_4_2_0_projections () ->
1333+ RegFuns = [fun register_rabbit_route_by_source_key_projection /0 ,
1334+ fun register_rabbit_route_by_source_projection /0 ],
1335+ rabbit_misc :for_each_while_ok (fun register_projection /1 , RegFuns ).
1336+
1337+ register_projection (RegisterFun ) ->
1338+ case RegisterFun () of
1339+ ok ->
1340+ ok ;
1341+ % % Before Khepri v0.13.0, `khepri:register_projection/1,2,3`
1342+ % % would return `{error, exists}` for projections which
1343+ % % already exist.
1344+ {error , exists } ->
1345+ ok ;
1346+ % % In v0.13.0+, Khepri returns a `?khepri_error(..)` instead.
1347+ {error , {khepri , projection_already_exists , _Info }} ->
1348+ ok ;
1349+ {error , _ } = Error ->
1350+ Error
1351+ end .
13411352
13421353register_rabbit_exchange_projection () ->
13431354 Name = rabbit_khepri_exchange ,
@@ -1415,7 +1426,7 @@ register_rabbit_bindings_projection() ->
14151426 _RoutingKey = ? KHEPRI_WILDCARD_STAR ),
14161427 khepri :register_projection (? STORE_ID , PathPattern , Projection ).
14171428
1418- register_rabbit_index_route_projection () ->
1429+ register_rabbit_route_by_source_key_projection () ->
14191430 MapFun = fun (_Path , # binding {source = Source ,
14201431 key = Key ,
14211432 destination = Destination ,
@@ -1428,44 +1439,51 @@ register_rabbit_index_route_projection() ->
14281439 Options = #{type => bag ,
14291440 keypos => # index_route .source_key ,
14301441 read_concurrency => true },
1431- Projection = khepri_projection :new (
1432- rabbit_khepri_index_route , ProjectionFun , Options ),
1433- IgnoreHeadersAndTopic = # if_data_matches {
1434- pattern = # exchange {type = '$1' , _ = '_' },
1435- conditions = [{'andalso' ,
1436- {'=/=' , '$1' , headers },
1437- {'=/=' , '$1' , topic }}]},
1442+ Projection = khepri_projection :new (rabbit_khepri_route_by_source_key ,
1443+ ProjectionFun ,
1444+ Options ),
1445+ Exchange = # if_data_matches {
1446+ pattern = # exchange {type = '$1' , _ = '_' },
1447+ conditions = [{'andalso' ,
1448+ {'=/=' , '$1' , headers },
1449+ {'=/=' , '$1' , topic },
1450+ {'=/=' , '$1' , fanout },
1451+ {'=/=' , '$1' , 'x-jms-topic' },
1452+ {'=/=' , '$1' , 'x-random' }
1453+ }]},
14381454 PathPattern = rabbit_db_binding :khepri_route_path (
14391455 _VHost = ? KHEPRI_WILDCARD_STAR ,
1440- _Exchange = IgnoreHeadersAndTopic ,
1456+ Exchange ,
14411457 _Kind = ? KHEPRI_WILDCARD_STAR ,
14421458 _DstName = ? KHEPRI_WILDCARD_STAR ,
14431459 _RoutingKey = ? KHEPRI_WILDCARD_STAR ),
14441460 khepri :register_projection (? STORE_ID , PathPattern , Projection ).
14451461
1446- register_rabbit_source_route_projection () ->
1462+ register_rabbit_route_by_source_projection () ->
14471463 MapFun = fun (_Path , # binding {source = Source ,
14481464 key = Key ,
14491465 destination = Destination ,
14501466 args = Args }) ->
1451- # source_route {source = Source ,
1452- key = Key ,
1453- destination = Destination ,
1454- args = Args }
1467+ # route_by_source {source = Source ,
1468+ key = Key ,
1469+ destination = Destination ,
1470+ args = Args }
14551471 end ,
14561472 ProjectionFun = projection_fun_for_sets (MapFun ),
14571473 Options = #{type => bag ,
1458- keypos => # source_route .source ,
1474+ keypos => # route_by_source .source ,
14591475 read_concurrency => true },
1460- Projection = khepri_projection :new (
1461- rabbit_khepri_source_route , ProjectionFun , Options ),
1476+ Projection = khepri_projection :new (rabbit_khepri_route_by_source ,
1477+ ProjectionFun ,
1478+ Options ),
14621479 Exchange = # if_data_matches {
14631480 pattern = # exchange {type = '$1' , _ = '_' },
14641481 conditions = [{'andalso' ,
1465- {'=/=' , '$1' , direct },
14661482 {'=/=' , '$1' , headers },
14671483 {'=/=' , '$1' , topic },
1468- {'=/=' , '$1' , 'x-local-random' }
1484+ {'=/=' , '$1' , direct },
1485+ {'=/=' , '$1' , 'x-local-random' },
1486+ {'=/=' , '$1' , 'x-jms-topic' }
14691487 }]},
14701488 PathPattern = rabbit_db_binding :khepri_route_path (
14711489 _VHost = ? KHEPRI_WILDCARD_STAR ,
@@ -1788,6 +1806,22 @@ khepri_db_migration_post_enable(
17881806 _ = mnesia_to_khepri :rollback_table_copy (? STORE_ID , ? MIGRATION_ID ),
17891807 ok .
17901808
1809+ enable_feature_flag (#{command := enable ,
1810+ feature_name := 'rabbitmq_4.2.0' = FeatureName }) ->
1811+ % % We unregister this projection because it's superseded by
1812+ % % rabbit_khepri_route_by_source_key introduced in 4.2.0
1813+ ProjectionName = rabbit_khepri_index_route ,
1814+ Result = try khepri :unregister_projections (? STORE_ID , [ProjectionName ])
1815+ catch _ :Reason -> Reason
1816+ end ,
1817+ ? LOG_DEBUG (
1818+ " enabling feature flag ~s unregisters projection ~s : ~tp " ,
1819+ [FeatureName , ProjectionName , Result ],
1820+ #{domain => ? RMQLOG_DOMAIN_DB }),
1821+ ok ;
1822+ enable_feature_flag (_ ) ->
1823+ ok .
1824+
17911825- spec sync_cluster_membership_from_mnesia (FeatureName ) -> Ret when
17921826 FeatureName :: rabbit_feature_flags :feature_name (),
17931827 Ret :: ok | {error , Reason },
0 commit comments