Skip to content

Commit be164f1

Browse files
authored
Fixing flake related to tokio::fs::write cancellation (#10192)
Fixes flake in `test_instance_create_timeout_while_starting_propolis` Manually constructs a tokio runtime, and prevents a temporary directory from being dropped while any tokio tasks - explicitly or implicitly created - might still be alive. Fixes #10063
1 parent 68e62ef commit be164f1

1 file changed

Lines changed: 70 additions & 44 deletions

File tree

sled-agent/src/instance.rs

Lines changed: 70 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -3226,66 +3226,92 @@ mod tests {
32263226
}
32273227

32283228
// tests around dropshot request timeouts during the blocking propolis setup
3229-
#[tokio::test]
3230-
async fn test_instance_create_timeout_while_starting_propolis() {
3229+
#[test]
3230+
fn test_instance_create_timeout_while_starting_propolis() {
32313231
let logctx = omicron_test_utils::dev::test_setup_log(
32323232
"test_instance_create_timeout_while_starting_propolis",
32333233
);
32343234
let log = logctx.log.new(o!(FileKv));
3235-
3236-
let FakeNexusParts {
3237-
nexus_client,
3238-
state_rx,
3239-
_dns_server,
3240-
_nexus_server,
3241-
} = FakeNexusParts::new(&log).await;
3242-
32433235
let temp_guard = Utf8TempDir::new().unwrap();
32443236

3245-
let (inst, _) = timeout(
3246-
TIMEOUT_DURATION,
3247-
instance_struct(
3248-
&log,
3249-
// we want to test propolis not ever coming up
3250-
SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::LOCALHOST, 1, 0, 0)),
3237+
// Use a manual runtime so that `temp_guard` outlives it.
3238+
//
3239+
// The runner task's `setup_propolis_zone` calls `tokio::fs::write`
3240+
// (which uses `spawn_blocking`) to write zone config files. This
3241+
// test times out before the runner finishes, so the blocking write
3242+
// may still be in-flight when the test drops its locals. If
3243+
// `temp_guard` drops while the blocking thread is writing,
3244+
// `remove_dir_all` can race and fail silently, leaking files.
3245+
//
3246+
// Dropping the runtime first drains the blocking thread pool,
3247+
// ensuring the write completes before `temp_guard` cleans up.
3248+
//
3249+
// See: https://github.com/oxidecomputer/omicron/issues/10063
3250+
let rt = tokio::runtime::Builder::new_current_thread()
3251+
.enable_all()
3252+
.build()
3253+
.unwrap();
3254+
3255+
rt.block_on(async {
3256+
let FakeNexusParts {
32513257
nexus_client,
3252-
AvailableDatasetsReceiver::fake_in_tempdir_for_tests(
3253-
ZpoolOrRamdisk::Ramdisk,
3254-
),
3255-
temp_guard.path().as_str(),
3256-
),
3257-
)
3258-
.await
3259-
.expect("timed out creating Instance struct");
3258+
state_rx,
3259+
_dns_server,
3260+
_nexus_server,
3261+
} = FakeNexusParts::new(&log).await;
32603262

3261-
let (put_tx, put_rx) = oneshot::channel();
3263+
let (inst, _) = timeout(
3264+
TIMEOUT_DURATION,
3265+
instance_struct(
3266+
&log,
3267+
// we want to test propolis not ever coming up
3268+
SocketAddr::V6(SocketAddrV6::new(
3269+
Ipv6Addr::LOCALHOST,
3270+
1,
3271+
0,
3272+
0,
3273+
)),
3274+
nexus_client,
3275+
AvailableDatasetsReceiver::fake_in_tempdir_for_tests(
3276+
ZpoolOrRamdisk::Ramdisk,
3277+
),
3278+
temp_guard.path().as_str(),
3279+
),
3280+
)
3281+
.await
3282+
.expect("timed out creating Instance struct");
32623283

3263-
tokio::time::pause();
3284+
let (put_tx, put_rx) = oneshot::channel();
32643285

3265-
// pretending we're InstanceManager::ensure_state, try in vain to start
3266-
// our "instance", but no propolis server is running
3267-
inst.put_state(put_tx, VmmStateRequested::Running)
3268-
.expect("failed to send Instance::put_state");
3286+
tokio::time::pause();
32693287

3270-
let timeout_fut = timeout(TIMEOUT_DURATION, put_rx);
3288+
// pretending we're InstanceManager::ensure_state, try in vain
3289+
// to start our "instance", but no propolis server is running
3290+
inst.put_state(put_tx, VmmStateRequested::Running)
3291+
.expect("failed to send Instance::put_state");
32713292

3272-
tokio::time::advance(TIMEOUT_DURATION).await;
3293+
let timeout_fut = timeout(TIMEOUT_DURATION, put_rx);
32733294

3274-
tokio::time::resume();
3295+
tokio::time::advance(TIMEOUT_DURATION).await;
32753296

3276-
timeout_fut
3277-
.await
3278-
.expect_err("*should've* timed out waiting for Instance::put_state, but didn't?");
3297+
tokio::time::resume();
32793298

3280-
if let ReceivedInstanceState::InstancePut(SledVmmState {
3281-
vmm_state: VmmRuntimeState { state: VmmState::Running, .. },
3282-
..
3283-
}) = state_rx.borrow().to_owned()
3284-
{
3285-
panic!(
3286-
"Nexus's InstanceState should never have reached running if zone creation timed out"
3299+
timeout_fut.await.expect_err(
3300+
"*should've* timed out waiting for \
3301+
Instance::put_state, but didn't?",
32873302
);
3288-
}
3303+
3304+
if let ReceivedInstanceState::InstancePut(SledVmmState {
3305+
vmm_state: VmmRuntimeState { state: VmmState::Running, .. },
3306+
..
3307+
}) = state_rx.borrow().to_owned()
3308+
{
3309+
panic!(
3310+
"Nexus's InstanceState should never have reached \
3311+
running if zone creation timed out"
3312+
);
3313+
}
3314+
});
32893315

32903316
logctx.cleanup_successful();
32913317
}

0 commit comments

Comments
 (0)