mirror of
https://github.com/openai/codex.git
synced 2026-10-31 19:28:49 +00:00
Add optional process metrics sampling for code-mode hosts (#57841)
## What changed - Add `GetProcessMetrics` and provider sampling APIs for process ID, start time, cumulative CPU time, and resident memory, exposed through `ThreadManager` without creating a thread or session. - Distinguish unsupported collection, a locally absent host, and sampling failures. Process-owned providers never start or replace a host when sampling. - Bound gRPC sampling to one second without retries or explicit connection resets. Run optional collectors on a detached worker with at most one collection in flight, so stalled collection cannot block the HTTP/2 driver or runtime shutdown. ## Testing Add coverage for snapshots, collector errors and panics, deadlines, overload, and shutdown with a stuck collector. Extend lifecycle tests to check that sampling preserves session state and keepalive behavior without starting or restarting local hosts. GitOrigin-RevId: eeafe275891d0464636f28a29345a87aed1a12dc
This commit is contained in:
@@ -0,0 +1,69 @@
|
||||
//! Isolates optional synchronous metrics collection from the HTTP/2 driver and
|
||||
//! session admission. A stuck collector occupies at most one detached thread,
|
||||
//! which is never joined during host or Tokio runtime shutdown.
|
||||
|
||||
use std::panic::AssertUnwindSafe;
|
||||
use std::panic::catch_unwind;
|
||||
use std::sync::Arc;
|
||||
|
||||
use codex_code_mode_protocol::CodeModeProcessMetrics;
|
||||
use codex_code_mode_protocol::grpc;
|
||||
use tokio::sync::Semaphore;
|
||||
use tokio::sync::oneshot;
|
||||
use tonic::Response;
|
||||
use tonic::Status;
|
||||
|
||||
/// Collectors capture any resources they need before the host enters its sandbox.
|
||||
/// They must be bounded, read-only observations that do not acquire execution
|
||||
/// locks. Isolation covers unwinding panics and stalled collection, not process
|
||||
/// aborts, resource exhaustion, or interference through shared state. A collector
|
||||
/// that never returns disables further sampling for this host; it is not retried
|
||||
/// on another thread, and its worker is not joined during Tokio runtime shutdown.
|
||||
pub type CodeModeProcessMetricsCollector =
|
||||
Arc<dyn Fn() -> Result<CodeModeProcessMetrics, String> + Send + Sync>;
|
||||
|
||||
pub(crate) struct ProcessMetrics {
|
||||
collector: CodeModeProcessMetricsCollector,
|
||||
permits: Arc<Semaphore>,
|
||||
}
|
||||
|
||||
impl ProcessMetrics {
|
||||
pub(crate) fn new(collector: CodeModeProcessMetricsCollector) -> Self {
|
||||
Self {
|
||||
collector,
|
||||
permits: Arc::new(Semaphore::new(/*permits*/ 1)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) async fn sample(&self) -> Result<Response<grpc::GetProcessMetricsResponse>, Status> {
|
||||
let permit = self
|
||||
.permits
|
||||
.clone()
|
||||
.try_acquire_owned()
|
||||
.map_err(|_| Status::resource_exhausted("process metrics collector is busy"))?;
|
||||
let collector = self.collector.clone();
|
||||
let (sender, receiver) = oneshot::channel();
|
||||
// Do not use Tokio's blocking pool: runtime drop joins running blocking
|
||||
// tasks even after the request times out. Dropping this thread's handle
|
||||
// detaches it; the permit still bounds abandoned collection to one thread.
|
||||
std::thread::Builder::new()
|
||||
.name("code-mode-metrics".to_string())
|
||||
.spawn(move || {
|
||||
let result = catch_unwind(AssertUnwindSafe(|| collector()));
|
||||
drop(permit);
|
||||
let _ = sender.send(result);
|
||||
})
|
||||
.map_err(|_| Status::internal("process metrics worker could not start"))?;
|
||||
let snapshot = receiver
|
||||
.await
|
||||
.map_err(|_| Status::internal("process metrics worker ended"))?
|
||||
.map_err(|_| Status::internal("process metrics collector panicked"))?
|
||||
.map_err(|_| Status::internal("process metrics collection failed"))?;
|
||||
Ok(Response::new(grpc::GetProcessMetricsResponse {
|
||||
process_id: snapshot.process_id,
|
||||
process_start_time_seconds: snapshot.process_start_time_seconds,
|
||||
cpu_seconds_total: snapshot.cpu_seconds_total,
|
||||
resident_memory_bytes: snapshot.resident_memory_bytes,
|
||||
}))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user