Add client-side exec-server RPC attempt metrics (#42883)

## What changed

- Record `exec_server_client_requests_total` for every client RPC call attempt,
  labeled by protocol method.
- Count attempts before local admission so rejected, timed-out, cancelled, and
  transport-failed calls are included, while notifications and responses are not.
- Export the counter to configured OpenTelemetry collectors while excluding it
  from the built-in Statsig metrics set.

## Testing

- Cover every RPC call entry point, successful responses, local and transport
  failures, timeouts, cancellation, notifications, and disabled metrics.
- Verify that the OTLP HTTP exporter retains the counter.

GitOrigin-RevId: d266e840d5871aa2c34bed8ce44f3345e2b4650f
This commit is contained in:
bkotsopoulos
2026-09-04 23:06:00 +00:00
committed by copyberry
parent a97cf1b72e
commit 574a36ff99
8 changed files with 310 additions and 0 deletions
+11
View File
@@ -16,6 +16,7 @@ use codex_exec_server_protocol::JSONRPCNotification;
use codex_exec_server_protocol::JSONRPCRequest;
use codex_exec_server_protocol::JSONRPCResponse;
use codex_exec_server_protocol::RequestId;
use codex_otel::MetricsClient;
use codex_protocol::protocol::W3cTraceContext;
use serde::Serialize;
use serde::de::DeserializeOwned;
@@ -30,11 +31,16 @@ use tokio::sync::watch;
use tokio::task::JoinHandle;
use tokio::time::timeout;
use crate::client_telemetry::record_client_request;
use crate::connection::JsonRpcConnection;
use crate::connection::JsonRpcConnectionEvent;
use crate::connection::JsonRpcTransport;
use crate::rpc_server_requests::RpcServerRequestSender;
#[cfg(test)]
#[path = "rpc_client_metrics_tests.rs"]
mod client_metrics_tests;
pub(crate) const SESSION_ALREADY_ATTACHED_ERROR_CODE: i64 = -32010;
const MAX_IN_FLIGHT_REGULAR_CALLS: usize = 1024;
const RESERVED_CLEANUP_CALLS: usize = 1;
@@ -313,6 +319,7 @@ where
}
pub(crate) struct RpcClient {
metrics: Option<MetricsClient>,
write_tx: mpsc::Sender<JSONRPCMessage>,
pending: Arc<Mutex<HashMap<RequestId, PendingRequest>>>,
inbound_request_ids: Arc<StdMutex<HashSet<RequestId>>>,
@@ -397,6 +404,7 @@ impl RpcClient {
(
Self {
metrics: codex_otel::global(),
write_tx,
pending,
inbound_request_ids: Arc::new(StdMutex::new(HashSet::new())),
@@ -559,6 +567,7 @@ impl RpcClient {
P: Serialize,
T: DeserializeOwned,
{
record_client_request(self.metrics.as_ref(), method);
let _call_slot = self.acquire_regular_call_slot()?;
self.call_inner(method, params, RpcCallTimeout::None).await
}
@@ -573,6 +582,7 @@ impl RpcClient {
P: Serialize,
T: DeserializeOwned,
{
record_client_request(self.metrics.as_ref(), method);
let _call_slot = self.acquire_regular_call_slot()?;
self.call_inner(method, params, RpcCallTimeout::After(call_timeout))
.await
@@ -597,6 +607,7 @@ impl RpcClient {
P: Serialize,
T: DeserializeOwned,
{
record_client_request(self.metrics.as_ref(), method);
let _call_slot = match self.shared_call_slots.try_acquire() {
Ok(call_slot) => call_slot,
Err(_) => match self.cleanup_call_slots.try_acquire() {