mirror of
https://github.com/openai/codex.git
synced 2026-10-18 17:18:52 +00:00
Prune expired in-memory message board registry entries (#47952)
## Why Dropping the last board handle releases its state but leaves a stale entry in `InMemoryMessageBoards`, allowing registry entries to accumulate across sessions. ## What changed Prune entries whose state has no remaining strong references whenever a board is opened, while preserving shared state for live boards. ## Testing Add a regression test that repeatedly opens and drops boards, checks that expired entries are removed, and verifies that reopening a live board shares its state. GitOrigin-RevId: c3f1c27b42ee17915f0ece01b85a22d0fdf632ce
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
//! Tree-owned message boards for training. Agent handles share state; nothing is written to disk.
|
||||
//! Mutations and recipient selection are atomic. Host callbacks run outside the state lock.
|
||||
//! Opening a board prunes registry entries whose state has been released.
|
||||
|
||||
use crate::ChannelSummary;
|
||||
use crate::CreateChannelRequest;
|
||||
@@ -31,6 +32,10 @@ use uuid::Uuid;
|
||||
|
||||
mod queries;
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "in_memory_tests.rs"]
|
||||
mod tests;
|
||||
|
||||
/// Shares board state across a tree while giving each agent its own host handle.
|
||||
#[derive(Default)]
|
||||
pub struct InMemoryMessageBoards {
|
||||
@@ -44,6 +49,7 @@ impl InMemoryMessageBoards {
|
||||
host: Arc<dyn MessageBoardHost>,
|
||||
) -> InMemoryAgentMessageBoard {
|
||||
let mut states = self.states.lock().await;
|
||||
states.retain(|_, state| state.strong_count() > 0);
|
||||
let entry = states.entry(identity).or_default();
|
||||
let state = entry.upgrade().unwrap_or_else(|| {
|
||||
let state = Arc::default();
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
//! Verifies that registry cleanup releases expired entries and preserves live boards.
|
||||
|
||||
use super::*;
|
||||
use crate::NotificationDelivery;
|
||||
use futures::future::BoxFuture;
|
||||
use pretty_assertions::assert_eq;
|
||||
|
||||
struct UnusedHost;
|
||||
|
||||
impl MessageBoardHost for UnusedHost {
|
||||
fn agent_path(&self, _caller: ThreadId) -> BoxFuture<'_, Result<AgentPath>> {
|
||||
unreachable!("registry tests do not invoke host capabilities")
|
||||
}
|
||||
|
||||
fn resolve_agent(&self, _path: AgentPath) -> BoxFuture<'_, Result<ThreadId>> {
|
||||
unreachable!("registry tests do not invoke host capabilities")
|
||||
}
|
||||
|
||||
fn current_time(&self, _caller: ThreadId) -> BoxFuture<'_, Result<DateTime<Utc>>> {
|
||||
unreachable!("registry tests do not invoke host capabilities")
|
||||
}
|
||||
|
||||
fn notify(
|
||||
&self,
|
||||
_recipient: ThreadId,
|
||||
_post: PostPreview,
|
||||
) -> BoxFuture<'_, Result<NotificationDelivery>> {
|
||||
unreachable!("registry tests do not invoke host capabilities")
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn opening_boards_prunes_expired_entries_and_preserves_live_state() {
|
||||
let boards = InMemoryMessageBoards::default();
|
||||
let identity = SessionId::new();
|
||||
let live = boards.open(identity, Arc::new(UnusedHost)).await;
|
||||
|
||||
for _ in 0..16 {
|
||||
let next_identity = SessionId::new();
|
||||
let next = boards.open(next_identity, Arc::new(UnusedHost)).await;
|
||||
assert_eq!(
|
||||
boards
|
||||
.states
|
||||
.lock()
|
||||
.await
|
||||
.keys()
|
||||
.copied()
|
||||
.collect::<HashSet<_>>(),
|
||||
HashSet::from([identity, next_identity]),
|
||||
);
|
||||
drop(next);
|
||||
}
|
||||
|
||||
let shared = boards.open(identity, Arc::new(UnusedHost)).await;
|
||||
assert!(Arc::ptr_eq(&live.state, &shared.state));
|
||||
assert_eq!(
|
||||
boards
|
||||
.states
|
||||
.lock()
|
||||
.await
|
||||
.keys()
|
||||
.copied()
|
||||
.collect::<HashSet<_>>(),
|
||||
HashSet::from([identity]),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user