diff --git a/src/client/device_registry.rs b/src/client/device_registry.rs index 96c3ec323..dedd18e93 100644 --- a/src/client/device_registry.rs +++ b/src/client/device_registry.rs @@ -988,18 +988,6 @@ impl Client { devices } - /// Background loop placeholder for device registry cleanup. - /// Note: Cleanup functionality was removed as part of trait simplification. - /// Device registry entries are managed through normal update/get operations. - pub(super) async fn device_registry_cleanup_loop(&self) { - // Simply wait for shutdown signal - self.shutdown_notifier.listen().await; - debug!( - target: "Client/DeviceRegistry", - "Shutdown signaled, exiting cleanup loop" - ); - } - /// Migrate device registry entries from PN key to LID key. #[cfg_attr( feature = "tracing", diff --git a/src/client/lifecycle.rs b/src/client/lifecycle.rs index 4d7de6912..7acd8efcd 100644 --- a/src/client/lifecycle.rs +++ b/src/client/lifecycle.rs @@ -313,14 +313,6 @@ impl Client { })) .detach(); - // Start background task to clean up stale device registry entries - let cleanup_arc = arc.clone(); - arc.runtime - .spawn(Box::pin(async move { - cleanup_arc.device_registry_cleanup_loop().await; - })) - .detach(); - (arc, rx) } diff --git a/src/client/tests.rs b/src/client/tests.rs index 2098a64cc..84f6b1ebf 100644 --- a/src/client/tests.rs +++ b/src/client/tests.rs @@ -3409,6 +3409,28 @@ async fn terminal_disconnect_propagates_to_per_connection_signal() { ); } +/// Dropping the last owner must release persistence handles promptly. +#[tokio::test] +async fn dropping_fresh_client_releases_it_without_shutdown() { + let client = crate::test_utils::create_test_client().await; + let weak = Arc::downgrade(&client); + + drop(client); + + tokio::time::timeout(Duration::from_secs(5), async { + while weak.strong_count() != 0 { + tokio::task::yield_now().await; + } + }) + .await + .unwrap_or_else(|_| { + panic!( + "client is still retained by a background task (strong_count={})", + weak.strong_count() + ) + }); +} + /// Locks the zero-allocation property of the ack miss path: id resolution and /// the waiter probe must borrow from the node buffer. An `into_owned()` here /// costs one String per received ack, which the e2e dhat profile caught live.