Wait for cloud config refresh cancellation in loader lifetime tests (#56274)

## Why

Aborting a refresh task is asynchronous, so yielding once before checking `is_finished()` does not guarantee cancellation has completed.

## What changed

Return the refresh task's `JoinHandle` from `cloud_config_bundle_loader_for_service` and retain its `AbortHandle` in the loader lifetime. Update `each_loader_owns_refresh_until_its_last_clone_is_dropped` to await each task and assert cancellation after dropping its last loader clone, while still checking that the replacement loader's task remains active.

GitOrigin-RevId: 2eb65211aefc21f9195ff766d8793f4b8f5a74c4
This commit is contained in:
goodman-oai
2026-10-22 21:56:16 +00:00
committed by copyberry
parent 0eb98b4617
commit ca87ef86e1
2 changed files with 7 additions and 8 deletions
+4 -4
View File
@@ -13,7 +13,7 @@ use tokio::task::JoinHandle;
struct CloudConfigBundleLoaderLifetime<C> {
service: Arc<CloudConfigBundleService<C>>,
refresh_task: JoinHandle<()>,
refresh_task: AbortHandle,
}
impl<C> Drop for CloudConfigBundleLoaderLifetime<C> {
@@ -43,7 +43,7 @@ pub fn cloud_config_bundle_loader(
pub(crate) fn cloud_config_bundle_loader_for_service<C>(
service: CloudConfigBundleService<C>,
) -> (CloudConfigBundleLoader, AbortHandle)
) -> (CloudConfigBundleLoader, JoinHandle<()>)
where
C: BundleClient + 'static,
{
@@ -56,7 +56,7 @@ where
let abort_handle = refresh_task.abort_handle();
let lifetime = Arc::new(CloudConfigBundleLoaderLifetime {
service,
refresh_task,
refresh_task: abort_handle,
});
let snapshot_lifetime = Arc::clone(&lifetime);
@@ -69,7 +69,7 @@ where
let lifetime = Arc::clone(&snapshot_lifetime);
async move { lifetime.service.get_latest_snapshot().await }
});
(loader, abort_handle)
(loader, refresh_task)
}
pub async fn cloud_config_bundle_loader_for_storage(
+3 -4
View File
@@ -1564,14 +1564,13 @@ async fn each_loader_owns_refresh_until_its_last_clone_is_dropped() {
.expect("both loader owners should keep refreshing");
assert_eq!(fetcher.request_count.load(Ordering::SeqCst), 3);
// Aborting is asynchronous; joining waits for cancellation to finish.
drop(cloned_loader);
tokio::task::yield_now().await;
assert!(original_task.is_finished());
assert!(original_task.await.unwrap_err().is_cancelled());
assert!(!replacement_task.is_finished());
drop(replacement_loader);
tokio::task::yield_now().await;
assert!(replacement_task.is_finished());
assert!(replacement_task.await.unwrap_err().is_cancelled());
tokio::time::advance(CLOUD_CONFIG_BUNDLE_CACHE_REFRESH_INTERVAL + Duration::from_secs(1)).await;
tokio::task::yield_now().await;
assert_eq!(replacement_fetcher.request_count.load(Ordering::SeqCst), 2);