@@ -196,10 +196,12 @@ def _simulate_probe_crash(
196196 crash_delay_s : float = 0.5 ,
197197) -> None :
198198 """
199- Start a persistency probe process and force-kill it (SIGKILL) to simulate a crash .
199+ Start a persistency probe process, freeze it with SIGSTOP, then kill it with SIGKILL .
200200
201- The process is given ``crash_delay_s`` seconds to start up before being killed,
202- exercising abnormal termination of a live process rather than post-exit cleanup.
201+ SIGSTOP is sent immediately after the process starts so the subsequent SIGKILL always
202+ targets a live process, regardless of how quickly the scenario binary would otherwise
203+ complete. ``crash_delay_s`` controls how long the process is held frozen before
204+ being killed, giving it no opportunity to flush or close KVS storage cleanly.
203205 """
204206 if version == "rust" :
205207 target = "//feature_integration_tests/test_scenarios/rust:rust_test_scenarios"
@@ -233,11 +235,22 @@ def _simulate_probe_crash(
233235
234236 proc = subprocess .Popen (command , stdout = subprocess .PIPE , stderr = subprocess .PIPE )
235237 try :
238+ # Freeze the process immediately so it cannot exit before we kill it.
239+ # Without SIGSTOP a fast-completing scenario exits normally before SIGKILL
240+ # is delivered — proc.kill() would then hit a zombie, exercising nothing.
241+ # After SIGSTOP the process is guaranteed alive; SIGKILL below delivers a
242+ # real abnormal termination regardless of how quickly the scenario runs.
243+ proc .send_signal (signal .SIGSTOP )
236244 time .sleep (crash_delay_s )
237245 finally :
238246 proc .kill ()
239247 proc .wait ()
240248
249+ assert proc .returncode == - signal .SIGKILL , (
250+ f"Expected SIGKILL exit (returncode { - signal .SIGKILL } ), got { proc .returncode } . "
251+ "Crash simulation did not exercise abnormal termination."
252+ )
253+
241254
242255def _wait_for_process_restart (daemon : Any , process_name : str , old_pid : int , timeout_s : float = 10.0 ) -> int | None :
243256 """
0 commit comments