From d0176f8e303fa2bed971353686c5eccd8e59edf9 Mon Sep 17 00:00:00 2001 From: i5-650 Date: Sat, 22 Aug 2026 17:19:51 +0000 Subject: [PATCH] deregister from consul on shutdown (fix #1062) (#1507) Reviewed-on: https://git.deuxfleurs.fr/Deuxfleurs/garage/pulls/1507 --- src/garage/server.rs | 14 ++++++++++++++ src/rpc/consul.rs | 30 ++++++++++++++++++++++++++++++ src/rpc/system.rs | 11 ++++++++++- 3 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/garage/server.rs b/src/garage/server.rs index 53b65fbf..b38f7b56 100644 --- a/src/garage/server.rs +++ b/src/garage/server.rs @@ -152,6 +152,14 @@ pub async fn run_server( } } + // Deregister from Consul (if enabled) in the background, in parallel with the + // rest of the shutdown sequence, so that it doesn't add to shutdown latency. + #[cfg(feature = "consul-discovery")] + let deregister_consul_task = tokio::spawn({ + let system = garage.system.clone(); + async move { system.deregister_from_discovery().await } + }); + // Remove RPC handlers for system to break reference cycles info!("Deregistering RPC handlers for shutdown..."); garage.system.netapp.drop_all_handlers(); @@ -168,6 +176,12 @@ pub async fn run_server( // Await for all background tasks to end await_background_done.await?; + // Await for Consul deregistration to end, if it hasn't already + #[cfg(feature = "consul-discovery")] + if let Err(e) = deregister_consul_task.await { + error!("Error while joining Consul deregistration task: {}", e); + } + info!("Cleaning up..."); Ok(()) diff --git a/src/rpc/consul.rs b/src/rpc/consul.rs index 54d46679..dea49995 100644 --- a/src/rpc/consul.rs +++ b/src/rpc/consul.rs @@ -183,6 +183,36 @@ impl ConsulDiscovery { } // ---- PUBLISHING TO CONSUL CATALOG ---- + #[cfg(feature = "consul-discovery")] + pub async fn deregister_consul_service(&self, node_id: NodeID) -> Result<(), ConsulError> { + let node = format!("garage:{}", hex::encode(&node_id[..8])); + let url = format!( + "{}/v1/{}", + self.config.consul_http_addr, + (match &self.config.api { + ConsulDiscoveryAPI::Catalog => format!("catalog/deregister"), + ConsulDiscoveryAPI::Agent => format!("agent/service/deregister/{}", node), + }) + ); + + let req = self.client.put(&url); + + let http = if matches!(&self.config.api, ConsulDiscoveryAPI::Catalog) { + let deregister_request = serde_json::json!({ + "Node": node, + "ServiceID": node, + }); + let req = req.json(&deregister_request); + req.send().await? + } else { + req.send().await? + }; + http.error_for_status()?; + + debug!("Deregistered service {} from Consul", node); + Ok(()) + } + pub async fn publish_consul_service( &self, node_id: NodeID, diff --git a/src/rpc/system.rs b/src/rpc/system.rs index 246d4d86..27191cef 100644 --- a/src/rpc/system.rs +++ b/src/rpc/system.rs @@ -358,7 +358,7 @@ impl System { ); } - pub fn cleanup(&self) { + pub fn cleanup(self: &Arc) { // Break reference cycle self.metrics.store(None); } @@ -650,6 +650,15 @@ impl System { } } + #[cfg(feature = "consul-discovery")] + pub async fn deregister_from_discovery(self: &Arc) { + if let Some(c) = &self.consul_discovery { + if let Err(e) = c.deregister_consul_service(self.netapp.id).await { + error!("Error while deregistering from Consul: {}", e); + } + } + } + async fn discovery_loop(self: &Arc, mut stop_signal: watch::Receiver) { while !*stop_signal.borrow() { let peers_up = self