mirror of
https://github.com/openai/codex.git
synced 2026-10-24 18:18:41 +00:00
codex: enforce timer db cap in core (#17380)
This commit is contained in:
@@ -20,6 +20,7 @@ use crate::pending_input::GeneratedMessageInput;
|
||||
use crate::pending_input::PendingInputItem;
|
||||
use crate::timers::ClaimedTimer;
|
||||
use crate::timers::CreateTimer;
|
||||
use crate::timers::MAX_ACTIVE_TIMERS_PER_THREAD;
|
||||
use crate::timers::PersistedTimer;
|
||||
use crate::timers::RecurringTimerPolicy;
|
||||
use crate::timers::RestoredTimerTask;
|
||||
@@ -157,11 +158,25 @@ impl Session {
|
||||
let params = self
|
||||
.thread_timer_create_params(&persisted_timer, TIMER_SOURCE_AGENT)
|
||||
.await?;
|
||||
if let Err(err) = state_db.create_thread_timer(¶ms).await {
|
||||
if let Some(runtime) = self.timers.lock().await.remove_timer(&id) {
|
||||
TimersState::cancel_runtime(&runtime);
|
||||
match state_db
|
||||
.create_thread_timer_if_below_limit(¶ms, MAX_ACTIVE_TIMERS_PER_THREAD)
|
||||
.await
|
||||
{
|
||||
Ok(true) => {}
|
||||
Ok(false) => {
|
||||
if let Some(runtime) = self.timers.lock().await.remove_timer(&id) {
|
||||
TimersState::cancel_runtime(&runtime);
|
||||
}
|
||||
return Err(format!(
|
||||
"too many active timers; each thread supports at most {MAX_ACTIVE_TIMERS_PER_THREAD} timers"
|
||||
));
|
||||
}
|
||||
Err(err) => {
|
||||
if let Some(runtime) = self.timers.lock().await.remove_timer(&id) {
|
||||
TimersState::cancel_runtime(&runtime);
|
||||
}
|
||||
return Err(format!("failed to persist timer to sqlite: {err}"));
|
||||
}
|
||||
return Err(format!("failed to persist timer to sqlite: {err}"));
|
||||
}
|
||||
|
||||
if let Some(timer_spec) = timer_spec {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::make_session_and_context_with_rx;
|
||||
use crate::messages::MessagePayload;
|
||||
use crate::timers::MAX_ACTIVE_TIMERS_PER_THREAD;
|
||||
use crate::timers::ThreadTimerTrigger;
|
||||
use crate::timers::TimerDelivery;
|
||||
use codex_features::Feature;
|
||||
@@ -108,3 +109,82 @@ async fn maybe_start_pending_timer_claims_only_one_timer_while_start_is_in_progr
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_timer_rejects_when_sqlite_thread_timer_limit_is_reached() {
|
||||
let (mut session, _, _) = make_session_and_context_with_rx().await;
|
||||
Arc::get_mut(&mut session)
|
||||
.expect("session should have no other references")
|
||||
.features
|
||||
.enable(Feature::Timers)
|
||||
.expect("test config should allow feature update");
|
||||
let config = {
|
||||
let state = session.state.lock().await;
|
||||
state
|
||||
.session_configuration
|
||||
.original_config_do_not_use
|
||||
.clone()
|
||||
};
|
||||
let state_db = codex_state::StateRuntime::init(
|
||||
config.sqlite_home.clone(),
|
||||
config.model_provider_id.clone(),
|
||||
)
|
||||
.await
|
||||
.expect("state db should open");
|
||||
let thread_id = session.conversation_id.to_string();
|
||||
for index in 0..MAX_ACTIVE_TIMERS_PER_THREAD {
|
||||
state_db
|
||||
.create_thread_timer(&test_timer_params(&thread_id, &format!("timer-{index}")))
|
||||
.await
|
||||
.expect("seed timer");
|
||||
}
|
||||
|
||||
let err = session
|
||||
.create_timer(
|
||||
ThreadTimerTrigger::Delay {
|
||||
seconds: 10,
|
||||
repeat: None,
|
||||
},
|
||||
MessagePayload {
|
||||
content: "overflow".to_string(),
|
||||
instructions: None,
|
||||
meta: Default::default(),
|
||||
},
|
||||
TimerDelivery::AfterTurn,
|
||||
)
|
||||
.await
|
||||
.expect_err("timer creation should reject full sqlite timer set");
|
||||
|
||||
assert_eq!(
|
||||
err,
|
||||
format!(
|
||||
"too many active timers; each thread supports at most {MAX_ACTIVE_TIMERS_PER_THREAD} timers"
|
||||
)
|
||||
);
|
||||
assert_eq!(
|
||||
state_db
|
||||
.list_thread_timers(&thread_id)
|
||||
.await
|
||||
.expect("list timers")
|
||||
.len(),
|
||||
MAX_ACTIVE_TIMERS_PER_THREAD
|
||||
);
|
||||
}
|
||||
|
||||
fn test_timer_params(thread_id: &str, id: &str) -> codex_state::ThreadTimerCreateParams {
|
||||
codex_state::ThreadTimerCreateParams {
|
||||
id: id.to_string(),
|
||||
thread_id: thread_id.to_string(),
|
||||
source: "agent".to_string(),
|
||||
client_id: "codex-cli".to_string(),
|
||||
trigger_json: r#"{"kind":"delay","seconds":10}"#.to_string(),
|
||||
content: "existing timer".to_string(),
|
||||
instructions: None,
|
||||
meta_json: "{}".to_string(),
|
||||
delivery: TimerDelivery::AfterTurn.as_str().to_string(),
|
||||
created_at: 100,
|
||||
next_run_at: Some(200),
|
||||
last_run_at: None,
|
||||
pending_run: false,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user