mirror of
https://github.com/openai/codex.git
synced 2026-11-02 19:43:24 +00:00
Overlap agent metadata reads when resuming a V2 root (#47369)
## Why Restoring persisted V2 agent identities reads descendant metadata serially, making each storage read wait for the previous one. ## What changed Read up to eight descendants concurrently while registering identities in graph order, preserving path and nickname reservation order. Skip identities already registered and continue restoring after individual read failures. ## Testing Add a cold-resume regression test that gates child metadata reads to verify overlap, completes reads out of order to check deterministic path reservation, and injects a read failure. It also verifies that child runtimes remain unloaded until requested. GitOrigin-RevId: ae90f24875966be83208884937621a6a9eb0d848
This commit is contained in:
@@ -30,6 +30,8 @@ use codex_protocol::intersect_effective_permission_profiles;
|
||||
use codex_protocol::protocol::EnvironmentConfigState;
|
||||
use codex_thread_store::PersistContext;
|
||||
use codex_utils_path_uri::PathUri;
|
||||
use futures::StreamExt;
|
||||
use futures::stream;
|
||||
|
||||
const AGENT_NAMES: &str = include_str!("../../../assets/agent/agent_names.txt");
|
||||
|
||||
@@ -187,7 +189,8 @@ impl LocalAgentControl {
|
||||
config: &Config,
|
||||
root_thread_id: ThreadId,
|
||||
) {
|
||||
self.runtime.registry.register_root_thread(root_thread_id);
|
||||
let registry = &self.runtime.registry;
|
||||
registry.register_root_thread(root_thread_id);
|
||||
|
||||
let Ok(state) = self.upgrade() else {
|
||||
return;
|
||||
@@ -209,23 +212,32 @@ impl LocalAgentControl {
|
||||
}
|
||||
};
|
||||
|
||||
for thread_id in descendant_ids {
|
||||
if self
|
||||
.runtime
|
||||
.registry
|
||||
.agent_metadata_for_thread(thread_id)
|
||||
.is_some()
|
||||
{
|
||||
// Overlap storage reads, but reserve paths and nicknames in graph order.
|
||||
let mut stored_threads = stream::iter(
|
||||
descendant_ids
|
||||
.into_iter()
|
||||
.filter(|thread_id| registry.agent_metadata_for_thread(*thread_id).is_none())
|
||||
.map(|thread_id| {
|
||||
let state = &state;
|
||||
async move {
|
||||
let stored_thread = state
|
||||
.read_stored_thread(ReadThreadParams {
|
||||
thread_id,
|
||||
include_archived: true,
|
||||
include_history: false,
|
||||
})
|
||||
.await;
|
||||
(thread_id, stored_thread)
|
||||
}
|
||||
}),
|
||||
)
|
||||
.buffered(/*n*/ 8);
|
||||
|
||||
while let Some((thread_id, stored_thread)) = stored_threads.next().await {
|
||||
if registry.agent_metadata_for_thread(thread_id).is_some() {
|
||||
continue;
|
||||
}
|
||||
let restore_result = async {
|
||||
let stored_thread = state
|
||||
.read_stored_thread(ReadThreadParams {
|
||||
thread_id,
|
||||
include_archived: true,
|
||||
include_history: false,
|
||||
})
|
||||
.await?;
|
||||
let restore_result = stored_thread.and_then(|stored_thread| {
|
||||
let stored_agent_path = stored_thread
|
||||
.agent_path
|
||||
.as_deref()
|
||||
@@ -234,10 +246,7 @@ impl LocalAgentControl {
|
||||
.map_err(|err| {
|
||||
CodexErr::InvalidRequest(format!("invalid stored agent path: {err}"))
|
||||
})?;
|
||||
let mut reservation = self
|
||||
.runtime
|
||||
.registry
|
||||
.reserve_spawn_slot(/*max_threads*/ None)?;
|
||||
let mut reservation = registry.reserve_spawn_slot(/*max_threads*/ None)?;
|
||||
let mut metadata = self.prepare_agent_metadata(
|
||||
&mut reservation,
|
||||
config,
|
||||
@@ -251,9 +260,8 @@ impl LocalAgentControl {
|
||||
)?;
|
||||
metadata.agent_id = Some(thread_id);
|
||||
reservation.commit(metadata);
|
||||
Ok::<(), CodexErr>(())
|
||||
}
|
||||
.await;
|
||||
Ok(())
|
||||
});
|
||||
if let Err(err) = restore_result {
|
||||
warn!("failed to restore V2 agent metadata for {thread_id}: {err}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user