@@ -39,7 +39,7 @@ use slim_datapath::api::{
3939use slim_datapath:: api:: { NameId , ProtoName } ;
4040use slim_datapath:: message_processing:: MessageProcessor ;
4141use slim_datapath:: messages:: utils:: SlimHeaderFlags ;
42- use slim_datapath:: tables:: SubscriptionTable ;
42+ use slim_datapath:: tables:: { ConnType , SubscriptionTable } ;
4343
4444type TxChannel = mpsc:: Sender < Result < ControlMessage , Status > > ;
4545type TxChannels = HashMap < String , TxChannel > ;
@@ -1683,6 +1683,47 @@ impl ControllerService {
16831683 }
16841684 }
16851685
1686+ /// Replay all locally-registered agent subscriptions to the control plane.
1687+ /// Called after (re)connecting so the CP can recreate wildcard route templates.
1688+ async fn replay_local_subscriptions ( & self , clients : & [ ClientConfig ] ) {
1689+ let mut routes: Vec < v1:: Route > = Vec :: new ( ) ;
1690+ self . inner . message_processor . subscription_table ( ) . for_each (
1691+ |name, id, local, _remote, _peer, edge| {
1692+ if local. is_empty ( ) && edge. is_empty ( ) {
1693+ return ;
1694+ }
1695+ routes. push ( v1:: Route {
1696+ name : Some ( name. clone ( ) . with_id ( id) ) ,
1697+ link_id : None ,
1698+ direction : None ,
1699+ } ) ;
1700+ } ,
1701+ ) ;
1702+
1703+ if routes. is_empty ( ) {
1704+ return ;
1705+ }
1706+
1707+ info ! (
1708+ count = routes. len( ) ,
1709+ "replaying local subscriptions to control plane"
1710+ ) ;
1711+
1712+ let ctrl = ControlMessage {
1713+ message_id : uuid:: Uuid :: new_v4 ( ) . to_string ( ) ,
1714+ payload : Some ( Payload :: ConfigCommand ( v1:: ConfigurationCommand {
1715+ connections_to_create : vec ! [ ] ,
1716+ connections_to_delete : vec ! [ ] ,
1717+ routes_to_set : routes,
1718+ routes_to_delete : vec ! [ ] ,
1719+ reconcile : false ,
1720+ connections_received : vec ! [ ] ,
1721+ } ) ) ,
1722+ } ;
1723+
1724+ self . send_or_queue_notification ( ctrl, clients) . await ;
1725+ }
1726+
16861727 /// Process the control message stream.
16871728 fn process_control_message_stream (
16881729 & self ,
@@ -1837,6 +1878,11 @@ impl ControllerService {
18371878 }
18381879 }
18391880
1881+ // Replay local agent subscriptions so the CP recreates route templates.
1882+ if let Some ( ref cfg) = config {
1883+ this. replay_local_subscriptions ( & [ cfg. clone ( ) ] ) . await ;
1884+ }
1885+
18401886 let mut drain_fut = std:: pin:: pin!( watch. clone( ) . signaled( ) ) ;
18411887
18421888 loop {
@@ -1921,6 +1967,30 @@ impl ControllerService {
19211967 ) -> Result < mpsc:: Sender < Result < ControlMessage , Status > > , ControllerError > {
19221968 info ! ( %config. endpoint, "connecting to control plane" ) ;
19231969
1970+ // Disconnect all outgoing remote connections — the CP will create
1971+ // fresh links after re-registration and nodes will reconnect.
1972+ // This prevents stale connections from causing duplicate links.
1973+ let mut remote_conn_ids: Vec < u64 > = Vec :: new ( ) ;
1974+ self . inner
1975+ . message_processor
1976+ . connection_table ( )
1977+ . for_each ( |id, conn| {
1978+ if conn. is_outgoing ( ) && matches ! ( conn. connection_type( ) , ConnType :: Remote ) {
1979+ remote_conn_ids. push ( id) ;
1980+ }
1981+ } ) ;
1982+ for conn_id in & remote_conn_ids {
1983+ if let Err ( e) = self . inner . message_processor . disconnect ( * conn_id) {
1984+ debug ! ( conn_id, error = %e. chain( ) , "failed to disconnect remote connection" ) ;
1985+ }
1986+ }
1987+ if !remote_conn_ids. is_empty ( ) {
1988+ info ! (
1989+ count = remote_conn_ids. len( ) ,
1990+ "disconnected remote connections for clean restart"
1991+ ) ;
1992+ }
1993+
19241994 // Reset connection state before establishing a new session. The CP
19251995 // will send fresh ConfigCommands after re-registration to rebuild
19261996 // these maps.
0 commit comments