@@ -74,11 +74,15 @@ impl LeaderLock {
7474 }
7575}
7676
77+ pub type BadNodeInformer = tokio:: sync:: mpsc:: UnboundedSender < std:: net:: SocketAddr > ;
78+
7779base64_serde_type ! ( pub Base64Standard , base64:: engine:: general_purpose:: STANDARD ) ;
7880#[ derive( Clone ) ]
7981#[ cfg_attr( test, derive( Debug ) ) ]
8082pub struct Config {
8183 pub dyn_cfg : DynamicConfig ,
84+ bad_node_informer : Option < BadNodeInformer > ,
85+ cancellation_token : Option < tokio_util:: sync:: CancellationToken > ,
8286}
8387
8488#[ cfg( test) ]
@@ -356,7 +360,18 @@ fn resolve_id(id: Option<String>) -> String {
356360 . unwrap_or_else ( uuid)
357361}
358362
363+ impl Drop for Config {
364+ fn drop ( & mut self ) {
365+ if let Some ( token) = & self . cancellation_token {
366+ token. cancel ( ) ;
367+ }
368+ }
369+ }
370+
359371impl Config {
372+ /// Creates and initializes a new Config
373+ ///
374+ /// This constructor is mainly intended for tests
360375 pub fn new (
361376 id : Option < String > ,
362377 icao_code : IcaoCode ,
@@ -370,15 +385,84 @@ impl Config {
370385 icao_code : NotifyingIcaoCode :: new ( icao_code) ,
371386 typemap : default_typemap ( ) ,
372387 } ,
388+ bad_node_informer : None ,
389+ cancellation_token : None ,
373390 } ;
374391 providers. init_config ( & mut config) ;
375392 service. init_config ( & mut config) ;
376393
377394 config
378395 }
379396
397+ /// Creates and initializes a new Arc<Config>
398+ ///
399+ /// Spawns a tokio task as a side effect that will be stopped when the cancellation token is
400+ /// cancelled. The token _will_ be cancelled when Config is dropped, so make sure to pass in a
401+ /// child token if the token is intended to live longer than the Config.
402+ pub fn new_rc (
403+ id : Option < String > ,
404+ icao_code : IcaoCode ,
405+ providers : & crate :: Providers ,
406+ service : & crate :: Service ,
407+ cancellation_token : tokio_util:: sync:: CancellationToken ,
408+ ) -> Arc < Self > {
409+ let ( tx, rx) = tokio:: sync:: mpsc:: unbounded_channel :: < std:: net:: SocketAddr > ( ) ;
410+
411+ let mut config = Self :: new ( id, icao_code, providers, service) ;
412+ config. cancellation_token = Some ( cancellation_token) ;
413+ config. bad_node_informer = Some ( tx) ;
414+
415+ let config = Arc :: new ( config) ;
416+ config
417+ . spawn_janitor ( rx)
418+ . expect ( "spawn_janitor() from new_rc() should not fail" ) ;
419+
420+ config
421+ }
422+
423+ /// Spawns a janitor task to help out with cleaning stale Datacenter entries from the Config
424+ fn spawn_janitor (
425+ self : & Arc < Config > ,
426+ mut rx : tokio:: sync:: mpsc:: UnboundedReceiver < std:: net:: SocketAddr > ,
427+ ) -> eyre:: Result < ( ) > {
428+ let cancellation_token = self
429+ . cancellation_token
430+ . as_ref ( )
431+ . ok_or_else ( || eyre:: eyre!( "cancellation token not set" ) ) ?
432+ . clone ( ) ;
433+
434+ // Ensure that we don't keep an owning reference to Arc<Config> so that Drop can occurr
435+ let janitor_config_ref = Arc :: downgrade ( self ) ;
436+ tokio:: spawn ( async move {
437+ loop {
438+ tokio:: select! {
439+ _ = cancellation_token. cancelled( ) => {
440+ break ;
441+ }
442+ node = rx. recv( ) => {
443+ if let Some ( node) = node {
444+ let ip = node. ip( ) ;
445+ if let Some ( config) = janitor_config_ref. upgrade( ) {
446+ if let Some ( datacenters) = config. dyn_cfg. datacenters( ) {
447+ datacenters. modify( |wg| {
448+ tracing:: warn!( %ip, "removing datacenter from local state" ) ;
449+ wg. remove( ip) ;
450+ } ) ;
451+ }
452+ } else {
453+ break ;
454+ }
455+ }
456+ }
457+ }
458+ }
459+ tracing:: trace!( "Stopping janitor task" ) ;
460+ } ) ;
461+ Ok ( ( ) )
462+ }
463+
380464 pub fn read_config (
381- & mut self ,
465+ self : & Arc < Self > ,
382466 config_path : & std:: path:: Path ,
383467 locality : Option < crate :: net:: endpoint:: Locality > ,
384468 ) -> Result < ( ) , eyre:: Error > {
@@ -409,6 +493,10 @@ impl Config {
409493 Ok ( ( ) )
410494 }
411495
496+ pub fn bad_node_informer ( & self ) -> Option < BadNodeInformer > {
497+ self . bad_node_informer . clone ( )
498+ }
499+
412500 /// Given a list of subscriptions and the current state of the calling client,
413501 /// construct a response with the current state of our resources that differ
414502 /// from those of the client
0 commit comments