mirror of
https://github.com/openai/codex.git
synced 2026-11-13 14:46:14 +00:00
Keep app-server thread RPCs active until delegated work completes (#43950)
## Why Thread startup, running-thread resume, and rollback could outlive their request handlers, allowing connection draining and request serialization to finish too early. ## What changed - Await thread startup and listener completion for running-thread resume and rollback. - Release the thread-list permit before waiting for resume completion so the listener can finish rollback responses. - Release pending rollback waiters when clearing a listener, and discard queued requests when their connection closes. ## Testing Add coverage for releasing queued requests and rollback waiters, and pipeline resume during rollback to check that both responses complete. Update diagnostics expectations to allow startup to remain in flight after its response is queued. GitOrigin-RevId: 138156179afbc40de29965820db7fa9cd3669cc7
This commit is contained in:
1 parent
589874be81
commit
102e1763b9
8 files changed
+134
-42
No files matched your search
@@ -47,24 +47,25 @@ async fn server_diagnostics_exposes_process_and_registered_thread_gauge() -> Res
|
||||
assert!(diagnostics.process.physical_footprint_bytes.is_some());
|
||||
#[cfg(not(target_os = "macos"))]
|
||||
assert_eq!(diagnostics.process.physical_footprint_bytes, None);
|
||||
for expected_gauge in [
|
||||
ServerDiagnosticsGauge {
|
||||
name: "app.requests.in_flight".to_string(),
|
||||
value: 1,
|
||||
},
|
||||
ServerDiagnosticsGauge {
|
||||
// Startup may still be finishing after its response was queued.
|
||||
assert!(matches!(
|
||||
diagnostics
|
||||
.gauges
|
||||
.iter()
|
||||
.find(|gauge| gauge.name == "app.requests.in_flight")
|
||||
.map(|gauge| gauge.value),
|
||||
Some(1 | 2)
|
||||
));
|
||||
assert_eq!(
|
||||
diagnostics
|
||||
.gauges
|
||||
.iter()
|
||||
.find(|gauge| gauge.name == "core.threads.live"),
|
||||
Some(&ServerDiagnosticsGauge {
|
||||
name: "core.threads.live".to_string(),
|
||||
value: 1,
|
||||
},
|
||||
] {
|
||||
assert_eq!(
|
||||
diagnostics
|
||||
.gauges
|
||||
.iter()
|
||||
.find(|gauge| gauge.name == expected_gauge.name),
|
||||
Some(&expected_gauge)
|
||||
);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -230,6 +230,15 @@ async fn thread_rollback_drops_last_turns_and_persists_to_rollout() -> Result<()
|
||||
num_turns: 1,
|
||||
})
|
||||
.await?;
|
||||
// Pipeline resume while the listener may still be handling rollback. Waiting for
|
||||
// its response must not retain the thread-list permit needed by that listener.
|
||||
let pipelined_resume_id = mcp
|
||||
.send_thread_resume_request(ThreadResumeParams {
|
||||
thread_id: thread.id.clone(),
|
||||
exclude_turns: true,
|
||||
..Default::default()
|
||||
})
|
||||
.await?;
|
||||
let deprecation_notice = timeout(DEFAULT_READ_TIMEOUT, mcp.read_next_message()).await??;
|
||||
let JSONRPCMessage::Notification(deprecation_notice) = deprecation_notice else {
|
||||
panic!("thread/rollback should emit deprecationNotice before its response");
|
||||
@@ -297,7 +306,10 @@ async fn thread_rollback_drops_last_turns_and_persists_to_rollout() -> Result<()
|
||||
other => panic!("expected user message item, got {other:?}"),
|
||||
}
|
||||
|
||||
// Resume and confirm the history is pruned.
|
||||
let _: ThreadResumeResponse =
|
||||
timeout(DEFAULT_READ_TIMEOUT, mcp.read_response(pipelined_resume_id)).await??;
|
||||
|
||||
// Resume after rollback completes to verify the pruned history.
|
||||
let resume_id = mcp
|
||||
.send_thread_resume_request(ThreadResumeParams {
|
||||
thread_id: thread.id,
|
||||
|
||||
Reference in new issue
Block a user