Repository navigation
Description
While executing tests under our test_all_disk_types.py in Dogfood, we observed a couple of crashes that spawned from race conditions between start sagas and state changes.
From the the core file:
::status
debugging core file of propolis-server (64-bit) from oxz_propolis-server_6daa866f-254c-4d05-96cd-a21d5a8418ba
initial argv: /opt/oxide/propolis-server/bin/propolis-server run /opt/oxide/propolis-server/b
threading model: native threads
status: process terminated by SIGABRT (Abort), pid=17579 uid=0 code=-1
::stack ! demangle
libc.so.1`_lwp_kill+0xa()
libc.so.1`raise+0x22(6)
libc.so.1`abort+0x58()
~std::sys::pal::unix::abort_internal+8()
~std::process::abort+8()
~__rustc::__rust_abort+8()
~__rustc::__rust_start_panic+8()
__rustc::rust_panic+0xd()
std::panicking::panic_with_hook+0x113()
std::panicking::panic_handler::{closure#0}+0x41()
~std::sys::backtrace::__rust_end_short_backtrace::<std::panicking::panic_handler::{closure#0}, !>+8()
~__rustc::rust_begin_unwind+0x1b()
~core::panicking::panic_fmt+0x2a()
~<crucible::client::DownstairsClient>::continue_negotiation+0x1380()
<crucible::upstairs::Upstairs>::on_client_message+0x291()
<crucible::upstairs::Upstairs>::apply+0x11f9()
crucible::up_main::{closure#1}+0x593()
<tokio::runtime::task::core::Core<crucible::up_main::{closure#1}, alloc::sync::Arc<tokio::runtime::scheduler::current_thread::Handle>>>::poll+0x32()
<tokio::runtime::task::harness::Harness<crucible::up_main::{closure#1}, alloc::sync::Arc<tokio::runtime::scheduler::multi_thread::handle::Handle>>>::poll+0x5b()
<tokio::runtime::scheduler::multi_thread::worker::Context>::run_task+0x13f()
<tokio::runtime::scheduler::multi_thread::worker::Context>::run+0x88b()
<tokio::runtime::context::scoped::Scoped<tokio::runtime::scheduler::Context>>::set::<tokio::runtime::scheduler::multi_thread::worker::run::{closure#0}::{closure#0}, ()>+0x27()
tokio::runtime::context::runtime::enter_runtime::<tokio::runtime::scheduler::multi_thread::worker::run::{closure#0}, ()>+0x1cd()
tokio::runtime::scheduler::multi_thread::worker::run+0xa0()
<tokio::runtime::task::core::Core<tokio::runtime::blocking::task::BlockingTask<<tokio::runtime::scheduler::multi_thread::worker::Launch>::launch::{closure#0}>, tokio::runtime::blocking::schedule::BlockingSchedule>>::poll+0x6c()
<tokio::runtime::task::harness::Harness<tokio::runtime::blocking::task::BlockingTask<<tokio::runtime::scheduler::multi_thread::worker::Launch>::launch::{closure#0}>, tokio::runtime::blocking::schedule::BlockingSchedule>>::poll+0x8a()
<tokio::runtime::blocking::pool::Inner>::run+0xf3()
std::sys::backtrace::__rust_begin_short_backtrace::<<tokio::runtime::blocking::pool::Spawner>::spawn_thread::{closure#0}, ()>+0x3e()
<std::thread::lifecycle::spawn_unchecked<<tokio::runtime::blocking::pool::Spawner>::spawn_thread::{closure#0}, ()>::{closure#1} as core::ops::function::FnOnce<()>>::call_once::{shim:vtable#0}+0x50()
<std::sys::thread::unix::Thread>::new::thread_start+0x1dd()
libc.so.1`_thrp_setup+0x77(fffffc7feea35a40)
libc.so.1`_lwp_start()
In Nexus logs, we see that we attempt to stop the instance, but we get a sled agent API error (404)
2026-10-16 23:06:29.120Z INFO 3cdb3ab4-1a81-4ee3-9c84-2bacc3e9d5de/11297 (dropshot_external) on oxz_nexus_3cdb3ab4-1a81-4ee3-9c84-2bacc3e9d5de: set intended instance state
instance_id = 6ad10715-8625-46be-9658-0d4fc53a2f55
intended_state = running
method = POST
req_id = 1f800877-6ee0-4325-8f8c-9cc6c81814d0
uri = /v1/instances/alldisks-instance-main-4539/start?project=product-assurance
2026-10-16 23:06:29.321Z INFO 3cdb3ab4-1a81-4ee3-9c84-2bacc3e9d5de/11297 (ServerContext) on oxz_nexus_3cdb3ab4-1a81-4ee3-9c84-2bacc3e9d5de: sled reservation starting
instance_id = 6ad10715-8625-46be-9658-0d4fc53a2f55
propolis_id = 6daa866f-254c-4d05-96cd-a21d5a8418ba
reservation_reason = start
2026-10-16 23:06:44.825Z INFO 61420090-2657-4888-9d57-8f7e36f652a4/4199 (dropshot_external) on oxz_nexus_61420090-2657-4888-9d57-8f7e36f652a4: set intended instance state
instance_id = 6ad10715-8625-46be-9658-0d4fc53a2f55
intended_state = stopped
method = POST
req_id = 509fbcbd-8eec-4877-a541-32c8cb1672f0
uri = /v1/instances/alldisks-instance-main-4539/stop?project=product-assurance
2026-10-16 23:06:44.841Z ERRO 61420090-2657-4888-9d57-8f7e36f652a4/4199 (dropshot_external) on oxz_nexus_61420090-2657-4888-9d57-8f7e36f652a4: marking VMM failed due to sled agent API error
error = Error Response: status: 404 Not Found; headers: {"content-type": "application/json", "x-request-id": "9505f6a8-60a3-427b-a503-137054255c9a", "content-length": "120", "date": "Fri, 16 Oct 2026 23:06:44 GMT"}; value: Error { error_code: Some("NO_SUCH_INSTANCE"), message: "Not Found", request_id: "9505 \u{2026}
instance_id = 6ad10715-8625-46be-9658-0d4fc53a2f55
method = POST
reason = no_such_instance
req_id = 509fbcbd-8eec-4877-a541-32c8cb1672f0
uri = /v1/instances/alldisks-instance-main-4539/stop?project=product-assurance
vmm_id = 6daa866f-254c-4d05-96cd-a21d5a8418ba
2026-10-16 23:06:44.929Z INFO 61420090-2657-4888-9d57-8f7e36f652a4/4199 (dropshot_external) on oxz_nexus_61420090-2657-4888-9d57-8f7e36f652a4: marked VMM as Failed, preparing update saga
instance_id = 6ad10715-8625-46be-9658-0d4fc53a2f55
method = POST
reason = NoSuchInstance
req_id = 509fbcbd-8eec-4877-a541-32c8cb1672f0
uri = /v1/instances/alldisks-instance-main-4539/stop?project=product-assurance
vmm_id = 6daa866f-254c-4d05-96cd-a21d5a8418ba
2026-10-16 23:06:52.631Z INFO 3cdb3ab4-1a81-4ee3-9c84-2bacc3e9d5de/11297 (ServerContext) on oxz_nexus_3cdb3ab4-1a81-4ee3-9c84-2bacc3e9d5de: start saga: ensuring instance is registered on sled
instance_id = 6ad10715-8625-46be-9658-0d4fc53a2f55
saga_id = 0c16f444-38ce-4a46-a7ee-dff1eb4cd9fd
saga_name = instance-start
start_reason = User
2026-10-16 23:06:53.641Z INFO 3cdb3ab4-1a81-4ee3-9c84-2bacc3e9d5de/11297 (ServerContext) on oxz_nexus_3cdb3ab4-1a81-4ee3-9c84-2bacc3e9d5de: start saga: ensuring instance is running
instance_id = 6ad10715-8625-46be-9658-0d4fc53a2f55
saga_id = 0c16f444-38ce-4a46-a7ee-dff1eb4cd9fd
saga_name = instance-start
start_reason = User
Reincarnation failed as well
2026-10-16 23:07:01.131Z INFO 61420090-2657-4888-9d57-8f7e36f652a4/4199 (ServerContext) on oxz_nexus_61420090-2657-4888-9d57-8f7e36f652a4: attempting to reincarnate instance...
background_task = instance_reincarnation
instance_id = 6ad10715-8625-46be-9658-0d4fc53a2f55
instance_state = Failed
last_auto_restarted_at = None
reincarnation_reason = instance failed
2026-10-16 23:07:01.183Z WARN 61420090-2657-4888-9d57-8f7e36f652a4/4199 (dropshot_external) on oxz_nexus_61420090-2657-4888-9d57-8f7e36f652a4: request completed after handler was already cancelled
error_message_external = The instance was running but is no longer reachable. It is being moved to the Failed state.
error_message_internal = Error Response: status: 404 Not Found; headers: {"content-type": "application/json", "x-request-id": "9505f6a8-60a3-427b-a503-137054255c9a", "content-length": "120", "date": "Fri, 16 Oct 2026 23:06:44 GMT"}; value: Error { error_code: Some("NO_SUCH_INSTANCE"), message: "Not Found", request_id: "9505 \u{2026}
method = POST
req_id = 509fbcbd-8eec-4877-a541-32c8cb1672f0
response_code = 503
uri = /v1/instances/alldisks-instance-main-4539/stop?project=product-assurance
Later on, we see Propolis initializing the VM and creating the disks:
2026-10-16 23:07:01.308Z INFO propolis-server/17579 on oxz_propolis-server_6daa866f-254c-4d05-96cd-a21d5a8418ba: Running v0.1.0-939 (93e04e1) master, bhyve API v18, viona API v7
2026-10-16 23:07:01.609Z INFO propolis-server/17579 on oxz_propolis-server_6daa866f-254c-4d05-96cd-a21d5a8418ba: initializing new VM
bootrom = /opt/oxide/propolis-server/blob/OVMF_CODE.fd
use_reservoir = true
2026-10-16 23:07:01.708Z INFO propolis-server/17579 on oxz_propolis-server_6daa866f-254c-4d05-96cd-a21d5a8418ba: Creating Crucible disk
backend_id = 2388b54f-0ce8-42e6-9058-5151a445ceae
2026-10-16 23:07:01.757Z INFO propolis-server/17579 on oxz_propolis-server_6daa866f-254c-4d05-96cd-a21d5a8418ba: Creating Crucible disk
backend_id = 4499f9ce-25ba-47a5-8547-a10eaa25c613
2026-10-16 23:07:01.758Z INFO propolis-server/17579 (crucible-4499f9ce-25ba-47a5-8547-a10eaa25c613) on oxz_propolis-server_6daa866f-254c-4d05-96cd-a21d5a8418ba: Crucible 4499f9ce-25ba-47a5-8547-a10eaa25c613 has session id: 27c6918d-a71a-40dd-adb1-d6a525d93af0
Orphan disk gets deleted:
2026-10-17 00:26:28.504Z INFO 924cf6fe-d2ae-4150-b4bf-7091503eb242/5252 (ServerContext) on oxz_nexus_924cf6fe-d2ae-4150-b4bf-7091503eb242: saga create
saga_id = a77bedfa-daac-4aa5-b49e-2159faac70d4
saga_name = disk-delete
sec_id = 924cf6fe-d2ae-4150-b4bf-7091503eb242
2026-10-17 00:26:29.866Z INFO 3cdb3ab4-1a81-4ee3-9c84-2bacc3e9d5de/11297 (ServerContext) on oxz_nexus_3cdb3ab4-1a81-4ee3-9c84-2bacc3e9d5de: received downstairs_client_stopped_notification from upstairs 4499f9ce-25ba-47a5-8547-a10eaa25c613 for downstairs 86433a59-7cb8-4b1d-9d57-37d3a923f23a: DownstairsClientStopped { time: 2026-10-17T00:26:29.863442960Z, reason: Finished }
2026-10-17 00:26:29.983Z INFO 924cf6fe-d2ae-4150-b4bf-7091503eb242/5252 (ServerContext) on oxz_nexus_924cf6fe-d2ae-4150-b4bf-7091503eb242: received downstairs_client_stopped_notification from upstairs 4499f9ce-25ba-47a5-8547-a10eaa25c613 for downstairs 758a2326-8ee8-47ed-9708-84eeabc9f01c: DownstairsClientStopped { time: 2026-10-17T00:26:29.866091696Z, reason: Finished }
2026-10-17 00:26:30.075Z INFO 924cf6fe-d2ae-4150-b4bf-7091503eb242/5252 (ServerContext) on oxz_nexus_924cf6fe-d2ae-4150-b4bf-7091503eb242: received downstairs_client_stopped_notification from upstairs 4499f9ce-25ba-47a5-8547-a10eaa25c613 for downstairs 572c862d-6ca3-492e-a69e-87cacdcaf4e9: DownstairsClientStopped { time: 2026-10-17T00:26:30.030490642Z, reason: Finished }
2026-10-17 00:26:36.644Z INFO 924cf6fe-d2ae-4150-b4bf-7091503eb242/5252 (ServerContext) on oxz_nexus_924cf6fe-d2ae-4150-b4bf-7091503eb242: saga finished
result = success
saga_id = a77bedfa-daac-4aa5-b49e-2159faac70d4
saga_name = disk-delete
sec_id = 924cf6fe-d2ae-4150-b4bf-7091503eb242
2026-10-17 00:26:36.644Z INFO 924cf6fe-d2ae-4150-b4bf-7091503eb242/5252 (ServerContext) on oxz_nexus_924cf6fe-d2ae-4150-b4bf-7091503eb242: saga finished
saga_id = a77bedfa-daac-4aa5-b49e-2159faac70d4
saga_name = disk-delete
About ~4h later (and after all three downstairs were dropped), a different region answers and the upstairs panic
2026-10-17 00:26:29.863Z WARN propolis-server/17579 (crucible-4499f9ce-25ba-47a5-8547-a10eaa25c613) on oxz_propolis-server_6daa866f-254c-4d05-96cd-a21d5a8418ba: downstairs disconnected
= downstairs
client = 0
2026-10-17 00:26:29.865Z WARN propolis-server/17579 (crucible-4499f9ce-25ba-47a5-8547-a10eaa25c613) on oxz_propolis-server_6daa866f-254c-4d05-96cd-a21d5a8418ba: downstairs disconnected
= downstairs
client = 1
2026-10-17 00:26:30.030Z WARN propolis-server/17579 (crucible-4499f9ce-25ba-47a5-8547-a10eaa25c613) on oxz_propolis-server_6daa866f-254c-4d05-96cd-a21d5a8418ba: downstairs disconnected
= downstairs
client = 2
2026-10-17 04:55:22.968Z INFO propolis-server/17579 (crucible-4499f9ce-25ba-47a5-8547-a10eaa25c613) on oxz_propolis-server_6daa866f-254c-4d05-96cd-a21d5a8418ba: connecting to [fd00:1122:3344:109::4]:19025
= downstairs
client = 2
2026-10-17 04:55:22.969Z INFO propolis-server/17579 (crucible-4499f9ce-25ba-47a5-8547-a10eaa25c613) on oxz_propolis-server_6daa866f-254c-4d05-96cd-a21d5a8418ba: ds_connection connected from Ok([fd00:1122:3344:128::1:25cc]:51694)
= downstairs
client = 2
2026-10-17 04:55:22.970Z INFO propolis-server/17579 (crucible-4499f9ce-25ba-47a5-8547-a10eaa25c613) on oxz_propolis-server_6daa866f-254c-4d05-96cd-a21d5a8418ba: downstairs client at Some([fd00:1122:3344:109::4]:19025) has region UUID 56ec88cb-ac5e-4858-8d8e-906480aeeca4
= downstairs
client = 2
thread 'tokio-rt-vmm' (13) panicked at /home/build/.cargo/git/checkouts/crucible-0a48bd218bc2bbbc/ad8a317/upstairs/src/client.rs:1488:37:
New client:2 uuid:56ec88cb-ac5e-4858-8d8e-906480aeeca4 does not match existing 572c862d-6ca3-492e-a69e-87cacdcaf4e9 ds_state:Connecting { state: WaitForRegionInfo, mode: Offline } up_state:Active
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
Core can be found at /staff/dogfood/BRM22250001/core.oxz_propolis-server_6daa866f-254c-4d05-96cd-a21d5a8418ba.propolis-server.17579.1792212924
Zone bundle can be found at /staff/dogfood/BRM22250001/omicron-11560/6daa866f.tar.gz
Activity
paudmir commentedon Oct 22, 2026
Similar race condition for the core at /staff/dogfood/BRM22250001/omicron-11560/core.oxz_propolis-server_cc50860d-2eea-410e-a820-cbb63a32658e.propolis-server.6245.1792278272
Stack: https://github.com/oxidecomputer/product-assurance/issues/727#issuecomment-6327701929
VMM record is deleted and an attempt to reincarnate pops up
A minute later, propolis show a YouAreNoLongerActive warning and an error mentioning the scrubbing of volume failed.
Zone bundle can be found at /staff/dogfood/BRM22250001/omicron-11560/cc50860d.tar.gz
[-]Instance state change get 404 during start saga; Nexus marks VMM as Failed but propolis keeps running.[/-][+]Instance state change gets 404 during start saga; Nexus marks VMM as Failed but propolis keeps running.[/+]hawkw commentedon Oct 24, 2026
Do we know what's attempting to stop the instances here? Is it an automated product-assurance thing? Nexus is supposed to fail calls to stop an instance that hasn't started yet, but perhaps we've broken that check somehow.
hawkw commentedon Oct 24, 2026
I think this logic is probably wrong:
omicron/nexus/src/app/instance.rs
Lines 1354 to 1357 in ed9659c
What I think is happening here is that we are allowing the stop request through to the sled-agent when the VMM's state is (probably)
Creating, because aCreatingVMM is represented externally as aStartinginstance:omicron/nexus/db-model/src/vmm_state.rs
Lines 328 to 337 in ed9659c
However, when the VMM is
Creating, we may not yet have sent an instance-ensure request to the sled-agent to create that VMM, so sled-agent doesn't know it's supposed to have a Propolis process for it yet. In that state, the sled-agent will (correctly) inform Nexus that it doesn't know about that VMM, at which point Nexus will move it toFailed. Only once the VMM has moved toStartingdo we know that the sled-agent is aware of it; we should reject requests to stop an instance while the VMM is in theCreatingstate.paudmir commentedon Oct 26, 2026
Yups, this s an automated product-assurance thing :) https://github.com/oxidecomputer/product-assurance/blob/main/pytest_tests/tests/functional/test_all_disk_types.py#L502
hawkw commentedon Oct 27, 2026
Doing some more digging into why the start saga keeps running in this case. I think I know what's going on there too.
When the VMM has been marked as
Failedby the stop request, which bumped its generation number, the subsequent write to the VMM record when theinstance-startsaga'ssis_ensure_registeredaction tries to advance the VMM fromCreatingtoStartingfails because the generation number has advanced by the transition toFailed. However, when we write the new VMM state here:omicron/nexus/db-queries/src/db/datastore/vmm.rs
Lines 363 to 386 in 5c1b8ba
we just...say "okay, it wasn't updated", and don't indicate that it has transitioned to a state that is incompatible with the update we tried to apply. Then, because
notify_vmm_updatedsucceeded, the call toNexus::instance_ensure_registeredhere returns adb::model::Vmmthat's...synthesized from the state we received from the sled-agent, rather than reflective of what's actually in the DB:omicron/nexus/src/app/instance.rs
Lines 1763 to 1774 in 5c1b8ba
Basically, we are assuming that because
notify_vmm_updateddid not fail, whatever we were trying to write is actually the instance's state, but because that function returnsOkwhen the generation number has not advanced (intended to make it idempotent), we are essentially lying about what the state that's been written is. Then, the saga just keeps on going under the assumption that the VMM has advanced toStarting, when it really should unwind since the VMM is nowFailed.I think
notify_vmm_updatedand theDataStore::vmm_and_migration_update_runtimefunction it calls need to return the VmmRuntimeState` in the db, regardless of whether or not the generation has been advanced by that call. That way, the caller can see if the state they were trying to write has made it to the database, and (in the saga case), we can unwind if the VMM has failed or been destroyed.[sled-agent-sim] add a way to interrupt VMM registration
[sled-agent-sim] add a way to interrupt VMM registration
[sled-agent-sim] add a way to interrupt VMM registration
[sled-agent-sim] add a way to interrupt VMM registration (#11606)
[nexus] retry ensuring VMMs are unregistered (#11629)
[nexus] ensure VMMs are unregistered even if register action fails (#…
[nexus] unwind instance-start sagas if they race with the VMM going a…