fix: harden concurrency limits and high-RPM runtime paths

Bound request, stream, queue, and shutdown resource lifetimes. Reduce scheduler and Redis hot-path work and isolate database maintenance. Include regression coverage, load probes, and concurrency audit results.
This commit is contained in:
elky
2026-09-10 08:14:58 +08:00
parent 361952ada9
commit ecc16673eb
149 changed files with 27963 additions and 1926 deletions
@@ -224,6 +224,36 @@ pub struct StoredRequestCandidate {
}
impl StoredRequestCandidate {
/// Scheduling needs identity, status, counters and times, without diagnostic payloads.
pub fn runtime_snapshot(&self) -> Self {
Self {
id: self.id.clone(),
request_id: self.request_id.clone(),
user_id: self.user_id.clone(),
api_key_id: self.api_key_id.clone(),
username: None,
api_key_name: None,
candidate_index: self.candidate_index,
retry_index: self.retry_index,
provider_id: self.provider_id.clone(),
endpoint_id: self.endpoint_id.clone(),
key_id: self.key_id.clone(),
status: self.status,
skip_reason: None,
is_cached: self.is_cached,
status_code: self.status_code,
error_type: None,
error_message: None,
latency_ms: self.latency_ms,
concurrent_requests: self.concurrent_requests,
extra_data: None,
required_capabilities: None,
created_at_unix_ms: self.created_at_unix_ms,
started_at_unix_ms: self.started_at_unix_ms,
finished_at_unix_ms: self.finished_at_unix_ms,
}
}
pub fn sanitize_for_persistence(&mut self) {
self.username = None;
self.api_key_name = None;
@@ -691,6 +721,19 @@ pub trait RequestCandidateReadRepository: Send + Sync {
limit: usize,
) -> Result<Vec<StoredRequestCandidate>, crate::DataLayerError>;
/// Same ordering and limit as `list_recent`, omitting diagnostic fields.
async fn list_recent_runtime(
&self,
limit: usize,
) -> Result<Vec<StoredRequestCandidate>, crate::DataLayerError> {
Ok(self
.list_recent(limit)
.await?
.iter()
.map(StoredRequestCandidate::runtime_snapshot)
.collect())
}
async fn list_by_provider_id(
&self,
provider_id: &str,
@@ -849,9 +892,7 @@ pub fn sanitize_request_candidate_extra_data_for_persistence(
extra_data: Option<serde_json::Value>,
) -> Option<serde_json::Value> {
let object = extra_data.as_ref()?.as_object()?;
let mut sanitized = sanitize_request_candidate_extra_data(extra_data.clone())
.and_then(|value| value.as_object().cloned())
.unwrap_or_default();
let mut sanitized = sanitize_candidate_extra_data_object(object);
for (key, fields) in [
("upstream_response", &["headers", "body"][..]),
("error_flow", &["message"][..]),
@@ -884,7 +925,10 @@ pub fn sanitize_request_candidate_extra_data_for_persistence(
};
let mut summary = sanitized
.remove(key)
.and_then(|value| value.as_object().cloned())
.and_then(|value| match value {
serde_json::Value::Object(object) => Some(object),
_ => None,
})
.unwrap_or_default();
for field in fields {
if let Some(value) = diagnostic.get(*field).filter(|value| !value.is_null()) {
@@ -907,70 +951,72 @@ pub fn sanitize_request_candidate_extra_data(
let serde_json::Value::Object(object) = extra_data? else {
return None;
};
let sanitized = sanitize_candidate_extra_data_object(&object);
(!sanitized.is_empty()).then_some(serde_json::Value::Object(sanitized))
}
fn sanitize_candidate_extra_data_object(
object: &serde_json::Map<String, serde_json::Value>,
) -> serde_json::Map<String, serde_json::Value> {
let mut sanitized = serde_json::Map::new();
for field in ["gateway_execution_runtime", "stream_completed", "cache_1h"] {
insert_candidate_bool(&object, &mut sanitized, field);
insert_candidate_bool(object, &mut sanitized, field);
}
for field in ["first_byte_time_ms", "pool_key_index"] {
insert_candidate_u64(&object, &mut sanitized, field);
insert_candidate_u64(object, &mut sanitized, field);
}
insert_candidate_i64(&object, &mut sanitized, "priority_slot");
insert_candidate_u64(&object, &mut sanitized, "ranking_index");
insert_candidate_i64(object, &mut sanitized, "priority_slot");
insert_candidate_u64(object, &mut sanitized, "ranking_index");
insert_candidate_known_string(&object, &mut sanitized, "phase", sanitize_candidate_phase);
insert_candidate_known_string(object, &mut sanitized, "phase", sanitize_candidate_phase);
for field in [
"client_api_format",
"provider_api_format",
"client_contract",
"provider_contract",
] {
insert_candidate_known_string(
&object,
&mut sanitized,
field,
sanitize_candidate_api_format,
);
insert_candidate_known_string(object, &mut sanitized, field, sanitize_candidate_api_format);
}
insert_candidate_known_string(
&object,
object,
&mut sanitized,
"execution_strategy",
sanitize_candidate_execution_strategy,
);
insert_candidate_known_string(
&object,
object,
&mut sanitized,
"conversion_mode",
sanitize_candidate_conversion_mode,
);
insert_candidate_known_string(
&object,
object,
&mut sanitized,
"ranking_mode",
sanitize_candidate_ranking_mode,
);
insert_candidate_known_string(
&object,
object,
&mut sanitized,
"priority_mode",
sanitize_candidate_priority_mode,
);
insert_candidate_known_string(
&object,
object,
&mut sanitized,
"promoted_by",
sanitize_candidate_promotion_reason,
);
insert_candidate_known_string(
&object,
object,
&mut sanitized,
"demoted_by",
sanitize_candidate_demotion_reason,
);
insert_candidate_known_string(&object, &mut sanitized, "source", sanitize_candidate_source);
insert_candidate_known_string(object, &mut sanitized, "source", sanitize_candidate_source);
insert_candidate_known_string(
&object,
object,
&mut sanitized,
"execution_path",
sanitize_candidate_execution_path,
@@ -1030,7 +1076,7 @@ pub fn sanitize_request_candidate_extra_data(
sanitized.insert("pool_group_exhaustion".to_string(), exhaustion);
}
(!sanitized.is_empty()).then_some(serde_json::Value::Object(sanitized))
sanitized
}
pub fn sanitize_request_candidate_required_capabilities(
@@ -0,0 +1,490 @@
use std::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use std::sync::Arc;
use serde_json::{Map, Value};
use super::types::UsageBodyCaptureState;
/// Shared accounting for the estimated heap retained by diagnostic JSON bodies.
#[doc(hidden)]
#[derive(Debug)]
pub struct UsageCaptureMemoryBudget {
limit: usize,
retained: AtomicUsize,
downgraded_total: AtomicU64,
}
impl UsageCaptureMemoryBudget {
pub fn new(limit: usize) -> Self {
Self {
limit,
retained: AtomicUsize::new(0),
downgraded_total: AtomicU64::new(0),
}
}
fn try_reserve(&self, bytes: usize) -> bool {
if bytes == 0 {
return true;
}
self.retained
.fetch_update(Ordering::AcqRel, Ordering::Acquire, |retained| {
retained
.checked_add(bytes)
.filter(|next| *next <= self.limit)
})
.is_ok()
}
fn release(&self, bytes: usize) {
if bytes != 0 {
self.retained.fetch_sub(bytes, Ordering::AcqRel);
}
}
fn record_downgrade(&self) {
self.downgraded_total.fetch_add(1, Ordering::Relaxed);
}
pub fn retained_bytes(&self) -> usize {
self.retained.load(Ordering::Acquire)
}
pub fn downgraded_total(&self) -> u64 {
self.downgraded_total.load(Ordering::Relaxed)
}
pub fn snapshot(&self) -> (usize, usize, u64) {
(self.limit, self.retained_bytes(), self.downgraded_total())
}
}
/// Non-serialized ownership of the diagnostic JSON heap estimate.
#[doc(hidden)]
#[derive(Debug, Default)]
pub struct UsageCaptureRetention {
budget: Option<Arc<UsageCaptureMemoryBudget>>,
bytes: usize,
}
// Runtime accounting does not participate in value or wire equality.
impl PartialEq for UsageCaptureRetention {
fn eq(&self, _other: &Self) -> bool {
true
}
}
impl UsageCaptureRetention {
pub fn reserve(&mut self, budget: Arc<UsageCaptureMemoryBudget>, bytes: usize) -> bool {
if self
.budget
.as_ref()
.is_some_and(|current| Arc::ptr_eq(current, &budget))
{
if bytes > self.bytes && !budget.try_reserve(bytes - self.bytes) {
budget.record_downgrade();
return false;
}
if bytes < self.bytes {
budget.release(self.bytes - bytes);
}
self.bytes = bytes;
return true;
}
if !budget.try_reserve(bytes) {
budget.record_downgrade();
return false;
}
*self = Self {
budget: Some(budget),
bytes,
};
true
}
pub fn clear(&mut self, budget: Arc<UsageCaptureMemoryBudget>) {
*self = Self {
budget: Some(budget),
bytes: 0,
};
}
pub fn clone_for_bodies(&self, estimate: impl FnOnce() -> usize) -> (Self, bool) {
let Some(budget) = &self.budget else {
return (Self::default(), true);
};
let mut retention = Self::default();
if retention.reserve(Arc::clone(budget), estimate()) {
(retention, true)
} else {
retention.clear(Arc::clone(budget));
(retention, false)
}
}
}
impl Drop for UsageCaptureRetention {
fn drop(&mut self) {
if let Some(budget) = &self.budget {
budget.release(self.bytes);
}
}
}
// serde_json::Map does not expose its backing allocation capacity. This charges a
// conservative per-entry estimate, not an allocator or process RSS measurement.
#[doc(hidden)]
pub fn usage_json_heap_estimate(value: &Value) -> usize {
match value {
Value::String(value) => value.capacity(),
Value::Array(values) => values.iter().fold(
values
.capacity()
.saturating_mul(std::mem::size_of::<Value>()),
|bytes, value| bytes.saturating_add(usage_json_heap_estimate(value)),
),
Value::Object(values) => values.iter().fold(
values.len().saturating_mul(
4 * (std::mem::size_of::<String>()
+ std::mem::size_of::<Value>()
+ std::mem::size_of::<usize>()),
),
|bytes, (key, value)| {
bytes
.saturating_add(key.capacity())
.saturating_add(usage_json_heap_estimate(value))
},
),
Value::Null | Value::Bool(_) | Value::Number(_) => 0,
}
}
/// Marks an omitted diagnostic body using the existing capture metadata shape.
#[doc(hidden)]
pub fn mark_usage_capture_memory_omitted(metadata: &mut Option<Value>, key: &str) {
let source_bytes = metadata
.as_ref()
.and_then(|metadata| metadata.get("body_capture"))
.and_then(|capture| capture.get(key))
.and_then(|entry| entry.get("source_bytes"))
.and_then(Value::as_u64);
let Some(metadata) = metadata
.get_or_insert_with(|| Value::Object(Map::with_capacity(1)))
.as_object_mut()
else {
return;
};
let Some(body_capture) = metadata
.entry("body_capture".to_owned())
.or_insert_with(|| Value::Object(Map::with_capacity(1)))
.as_object_mut()
else {
return;
};
let mut entry = Map::with_capacity(3 + usize::from(source_bytes.is_some()));
entry.insert(
"state".to_owned(),
Value::String(UsageBodyCaptureState::Truncated.as_str().to_owned()),
);
entry.insert("stored_bytes".to_owned(), Value::from(0));
if let Some(source_bytes) = source_bytes {
entry.insert("source_bytes".to_owned(), Value::from(source_bytes));
}
entry.insert(
"reason".to_owned(),
Value::String("usage_event_memory_budget_exceeded".to_owned()),
);
body_capture.insert(key.to_owned(), Value::Object(entry));
}
#[cfg(test)]
mod tests {
use super::super::types::UpsertUsageRecord;
use super::*;
use serde_json::json;
fn upsert_with_diagnostic_bodies() -> UpsertUsageRecord {
serde_json::from_str(
r#"{
"request_id": "retained-request",
"provider_name": "openai",
"model": "test-model",
"status": "completed",
"billing_status": "settled",
"updated_at_unix_secs": 123,
"input_tokens": 100,
"output_tokens": 500,
"total_tokens": 600,
"cache_creation_input_tokens": 7,
"cache_creation_ephemeral_5m_input_tokens": 7,
"cache_creation_ephemeral_1h_input_tokens": 0,
"cache_read_input_tokens": 0,
"total_cost_usd": 1.25,
"actual_total_cost_usd": 0.75,
"cache_creation_cost_usd": 0.05,
"cache_read_cost_usd": 0.0,
"status_code": 200,
"error_message": "preserved diagnostic classification",
"request_headers": {"x-request": "original"},
"provider_request_headers": {"x-provider-request": "original"},
"response_headers": {"x-response": "original"},
"client_response_headers": {"x-client-response": "original"},
"request_body": {"text": "original request body"},
"provider_request_body": {"text": "original provider request body"},
"response_body": {"text": "original provider response body"},
"client_response_body": {"text": "original client response body"},
"request_body_ref": "usage://retained-request/request",
"provider_request_body_ref": "usage://retained-request/provider_request",
"response_body_ref": "usage://retained-request/response",
"client_response_body_ref": "usage://retained-request/client_response",
"request_body_state": "inline",
"provider_request_body_state": "reference",
"response_body_state": "inline",
"client_response_body_state": "truncated",
"request_metadata": {
"trace_id": "unchanged",
"body_capture": {
"request": {"state": "inline", "source_bytes": 100},
"provider_request": {"state": "reference", "source_bytes": 200},
"response": {"state": "inline", "source_bytes": 300},
"client_response": {"state": "truncated", "source_bytes": 400}
}
}
}"#,
)
.expect("valid usage write fixture")
}
fn upsert_body_estimate(record: &UpsertUsageRecord) -> usize {
[
&record.request_body,
&record.provider_request_body,
&record.response_body,
&record.client_response_body,
]
.into_iter()
.flatten()
.map(|body| std::mem::size_of::<Value>() + usage_json_heap_estimate(body))
.sum()
}
#[test]
fn usage_capture_memory_upsert_serde_skips_retention_and_preserves_value_equality() {
let mut source = upsert_with_diagnostic_bodies();
let weight = upsert_body_estimate(&source);
let budget = Arc::new(UsageCaptureMemoryBudget::new(weight));
assert!(source
.capture_retention
.reserve(Arc::clone(&budget), weight));
let mut serialized = serde_json::to_value(&source).unwrap();
assert!(serialized.get("capture_retention").is_none());
serialized["capture_retention"] = json!({"bytes": usize::MAX});
let roundtrip: UpsertUsageRecord = serde_json::from_value(serialized).unwrap();
assert_eq!(source, roundtrip);
let unmanaged_clone = roundtrip.clone();
assert_eq!(source, unmanaged_clone);
assert!(unmanaged_clone.request_body.is_some());
assert!(unmanaged_clone.provider_request_body.is_some());
assert!(unmanaged_clone.response_body.is_some());
assert!(unmanaged_clone.client_response_body.is_some());
assert_eq!(budget.retained_bytes(), weight);
assert_eq!(budget.downgraded_total(), 0);
drop((roundtrip, unmanaged_clone));
assert_eq!(budget.retained_bytes(), weight);
drop(source);
assert_eq!(budget.retained_bytes(), 0);
}
#[test]
fn usage_capture_memory_upsert_clone_reserves_for_all_four_deep_copies() {
let mut source = upsert_with_diagnostic_bodies();
let original = serde_json::to_value(&source).unwrap();
let weight = upsert_body_estimate(&source);
let budget = Arc::new(UsageCaptureMemoryBudget::new(weight * 2));
assert!(source
.capture_retention
.reserve(Arc::clone(&budget), weight));
let cloned = source.clone();
assert_eq!(source, cloned);
assert_eq!(budget.retained_bytes(), weight * 2);
assert_eq!(budget.downgraded_total(), 0);
for (source_body, cloned_body) in [
(&source.request_body, &cloned.request_body),
(&source.provider_request_body, &cloned.provider_request_body),
(&source.response_body, &cloned.response_body),
(&source.client_response_body, &cloned.client_response_body),
] {
let source_text = source_body.as_ref().unwrap()["text"].as_str().unwrap();
let cloned_text = cloned_body.as_ref().unwrap()["text"].as_str().unwrap();
assert_eq!(source_text, cloned_text);
assert_ne!(source_text.as_ptr(), cloned_text.as_ptr());
}
assert_eq!(serde_json::to_value(&source).unwrap(), original);
drop(source);
assert_eq!(budget.retained_bytes(), weight);
assert_eq!(serde_json::to_value(&cloned).unwrap(), original);
drop(cloned);
assert_eq!(budget.retained_bytes(), 0);
}
#[test]
fn usage_capture_memory_upsert_clone_over_budget_only_omits_four_bodies() {
let mut source = upsert_with_diagnostic_bodies();
let original = serde_json::to_value(&source).unwrap();
let weight = upsert_body_estimate(&source);
let budget = Arc::new(UsageCaptureMemoryBudget::new(weight));
assert!(source
.capture_retention
.reserve(Arc::clone(&budget), weight));
let cloned = source.clone();
assert_eq!(budget.retained_bytes(), weight);
assert_eq!(budget.downgraded_total(), 1);
assert_eq!(serde_json::to_value(&source).unwrap(), original);
let mut expected = original;
for (body, state, key, source_bytes) in [
("request_body", "request_body_state", "request", 100),
(
"provider_request_body",
"provider_request_body_state",
"provider_request",
200,
),
("response_body", "response_body_state", "response", 300),
(
"client_response_body",
"client_response_body_state",
"client_response",
400,
),
] {
expected[body] = Value::Null;
expected[state] = json!("truncated");
expected["request_metadata"]["body_capture"][key] = json!({
"state": "truncated",
"stored_bytes": 0,
"source_bytes": source_bytes,
"reason": "usage_event_memory_budget_exceeded"
});
}
assert_eq!(serde_json::to_value(&cloned).unwrap(), expected);
assert_eq!(cloned.output_tokens, Some(500));
assert_eq!(cloned.cache_read_input_tokens, Some(0));
assert_eq!(cloned.cache_creation_ephemeral_1h_input_tokens, Some(0));
assert_eq!(cloned.total_cost_usd, Some(1.25));
assert_eq!(cloned.actual_total_cost_usd, Some(0.75));
drop(cloned);
assert_eq!(budget.retained_bytes(), weight);
drop(source);
assert_eq!(budget.retained_bytes(), 0);
}
#[test]
fn usage_capture_memory_upsert_clone_preserves_explicit_clearing_states() {
for state in [
UsageBodyCaptureState::None,
UsageBodyCaptureState::Disabled,
UsageBodyCaptureState::Unavailable,
] {
let mut source = upsert_with_diagnostic_bodies();
source.request_body_state = Some(state);
source.provider_request_body_state = Some(state);
source.response_body_state = Some(state);
source.client_response_body_state = Some(state);
let original = serde_json::to_value(&source).unwrap();
let weight = upsert_body_estimate(&source);
let budget = Arc::new(UsageCaptureMemoryBudget::new(weight));
assert!(source
.capture_retention
.reserve(Arc::clone(&budget), weight));
let cloned = source.clone();
let mut expected = original.clone();
for body in [
"request_body",
"provider_request_body",
"response_body",
"client_response_body",
] {
expected[body] = Value::Null;
}
assert_eq!(serde_json::to_value(&cloned).unwrap(), expected);
assert_eq!(serde_json::to_value(&source).unwrap(), original);
assert_eq!(budget.retained_bytes(), weight);
assert_eq!(budget.downgraded_total(), 1);
drop(cloned);
assert_eq!(budget.retained_bytes(), weight);
drop(source);
assert_eq!(budget.retained_bytes(), 0);
}
}
#[test]
fn usage_capture_memory_metadata_preserves_source_bytes_and_unrelated_metadata() {
let mut metadata = Some(json!({
"trace_id": "unchanged",
"body_capture": {
"request": {"state": "complete", "source_bytes": 42, "stored_bytes": 42, "extra": true},
"response": {"state": "complete", "source_bytes": 7}
}
}));
mark_usage_capture_memory_omitted(&mut metadata, "request");
assert_eq!(
metadata,
Some(json!({
"trace_id": "unchanged",
"body_capture": {
"request": {
"state": "truncated",
"source_bytes": 42,
"stored_bytes": 0,
"reason": "usage_event_memory_budget_exceeded"
},
"response": {"state": "complete", "source_bytes": 7}
}
}))
);
}
#[test]
fn usage_capture_memory_metadata_creates_missing_objects_and_replaces_entries() {
for mut metadata in [
None,
Some(json!({})),
Some(json!({"body_capture": {}})),
Some(json!({"body_capture": {"request": null}})),
Some(json!({"body_capture": {"request": "legacy"}})),
Some(json!({"body_capture": {"request": {"source_bytes": "42"}}})),
Some(json!({"body_capture": {"request": {"source_bytes": -1}}})),
] {
mark_usage_capture_memory_omitted(&mut metadata, "request");
assert_eq!(
metadata,
Some(json!({"body_capture": {"request": {
"state": "truncated",
"stored_bytes": 0,
"reason": "usage_event_memory_budget_exceeded"
}}}))
);
}
}
#[test]
fn usage_capture_memory_metadata_preserves_existing_non_object_containers() {
for metadata in [
Value::Null,
json!(false),
json!(7),
json!("legacy"),
json!([]),
json!({"body_capture": null}),
json!({"body_capture": false}),
json!({"body_capture": 7}),
json!({"body_capture": "legacy"}),
json!({"body_capture": []}),
] {
let mut actual = Some(metadata.clone());
mark_usage_capture_memory_omitted(&mut actual, "request");
assert_eq!(actual, Some(metadata));
}
}
}
@@ -1,8 +1,14 @@
mod capture_memory;
mod compression;
mod metadata_policy;
mod policy;
mod types;
#[doc(hidden)]
pub use capture_memory::{
mark_usage_capture_memory_omitted, usage_json_heap_estimate, UsageCaptureMemoryBudget,
UsageCaptureRetention,
};
pub use compression::{read_decompressed_usage_json, MAX_DECOMPRESSED_USAGE_JSON_BYTES};
pub use metadata_policy::*;
pub use policy::*;
@@ -673,6 +673,7 @@ mod tests {
fn usage_with_http_capture() -> UpsertUsageRecord {
UpsertUsageRecord {
capture_retention: Default::default(),
request_id: "req-sensitive-capture".to_string(),
user_id: Some("user-1".to_string()),
api_key_id: Some("key-1".to_string()),
@@ -54,11 +54,11 @@ pub fn extract_provider_reasoning_effort_from_body(value: Option<&Value>) -> Opt
}
fn normalize_provider_reasoning_effort(value: &str) -> Option<String> {
let normalized = value.trim().to_ascii_lowercase();
if normalized.is_empty() || normalized.len() > 64 {
let value = value.trim();
if value.is_empty() || value.len() > 64 {
return None;
}
Some(normalized)
Some(value.to_ascii_lowercase())
}
pub fn extract_provider_service_tier_from_body(value: Option<&Value>) -> Option<String> {
@@ -112,11 +112,11 @@ pub fn extract_provider_actual_service_tier_from_response(value: Option<&Value>)
}
pub fn normalize_provider_service_tier(value: &str) -> Option<String> {
let normalized = value.trim().to_ascii_lowercase();
if normalized.is_empty() || normalized.len() > 64 {
let value = value.trim();
if value.is_empty() || value.len() > 64 {
return None;
}
Some(normalized)
Some(value.to_ascii_lowercase())
}
/// Resolves a provider processing tier exclusively from the final upstream request.
@@ -1980,7 +1980,7 @@ pub trait UsageReadRepository: Send + Sync {
/// Request/response headers and bodies here are capture inputs that the repository persists into
/// the dedicated HTTP audit/body stores. Deprecated mirror columns on `public.usage` remain in the
/// schema for compatibility only and are not the intended long-term destination for new writes.
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
pub struct UpsertUsageRecord {
pub request_id: String,
pub user_id: Option<String>,
@@ -2055,6 +2055,142 @@ pub struct UpsertUsageRecord {
pub finalized_at_unix_secs: Option<u64>,
pub created_at_unix_ms: Option<u64>,
pub updated_at_unix_secs: u64,
#[doc(hidden)]
#[serde(skip)]
pub capture_retention: super::UsageCaptureRetention,
}
impl Clone for UpsertUsageRecord {
fn clone(&self) -> Self {
let (capture_retention, retain_bodies) = self.capture_retention.clone_for_bodies(|| {
[
&self.request_body,
&self.provider_request_body,
&self.response_body,
&self.client_response_body,
]
.into_iter()
.flatten()
.fold(0usize, |bytes, body| {
bytes
.saturating_add(std::mem::size_of::<Value>())
.saturating_add(super::usage_json_heap_estimate(body))
})
});
let mut cloned = Self {
request_id: self.request_id.clone(),
user_id: self.user_id.clone(),
api_key_id: self.api_key_id.clone(),
username: self.username.clone(),
api_key_name: self.api_key_name.clone(),
provider_name: self.provider_name.clone(),
model: self.model.clone(),
target_model: self.target_model.clone(),
provider_id: self.provider_id.clone(),
provider_endpoint_id: self.provider_endpoint_id.clone(),
provider_api_key_id: self.provider_api_key_id.clone(),
request_type: self.request_type.clone(),
api_format: self.api_format.clone(),
api_family: self.api_family.clone(),
endpoint_kind: self.endpoint_kind.clone(),
endpoint_api_format: self.endpoint_api_format.clone(),
provider_api_family: self.provider_api_family.clone(),
provider_endpoint_kind: self.provider_endpoint_kind.clone(),
has_format_conversion: self.has_format_conversion,
is_stream: self.is_stream,
input_tokens: self.input_tokens,
output_tokens: self.output_tokens,
total_tokens: self.total_tokens,
cache_creation_input_tokens: self.cache_creation_input_tokens,
cache_creation_ephemeral_5m_input_tokens: self.cache_creation_ephemeral_5m_input_tokens,
cache_creation_ephemeral_1h_input_tokens: self.cache_creation_ephemeral_1h_input_tokens,
cache_read_input_tokens: self.cache_read_input_tokens,
cache_creation_cost_usd: self.cache_creation_cost_usd,
cache_read_cost_usd: self.cache_read_cost_usd,
output_price_per_1m: self.output_price_per_1m,
total_cost_usd: self.total_cost_usd,
actual_total_cost_usd: self.actual_total_cost_usd,
status_code: self.status_code,
error_message: self.error_message.clone(),
error_category: self.error_category.clone(),
response_time_ms: self.response_time_ms,
first_byte_time_ms: self.first_byte_time_ms,
status: self.status.clone(),
billing_status: self.billing_status.clone(),
request_headers: self.request_headers.clone(),
request_body: retain_bodies.then(|| self.request_body.clone()).flatten(),
request_body_ref: self.request_body_ref.clone(),
request_body_state: self.request_body_state,
provider_request_headers: self.provider_request_headers.clone(),
provider_request_body: retain_bodies
.then(|| self.provider_request_body.clone())
.flatten(),
provider_request_body_ref: self.provider_request_body_ref.clone(),
provider_request_body_state: self.provider_request_body_state,
response_headers: self.response_headers.clone(),
response_body: retain_bodies.then(|| self.response_body.clone()).flatten(),
response_body_ref: self.response_body_ref.clone(),
response_body_state: self.response_body_state,
client_response_headers: self.client_response_headers.clone(),
client_response_body: retain_bodies
.then(|| self.client_response_body.clone())
.flatten(),
client_response_body_ref: self.client_response_body_ref.clone(),
client_response_body_state: self.client_response_body_state,
candidate_id: self.candidate_id.clone(),
candidate_index: self.candidate_index,
key_name: self.key_name.clone(),
planner_kind: self.planner_kind.clone(),
route_family: self.route_family.clone(),
route_kind: self.route_kind.clone(),
execution_path: self.execution_path.clone(),
local_execution_runtime_miss_reason: self.local_execution_runtime_miss_reason.clone(),
request_metadata: self.request_metadata.clone(),
finalized_at_unix_secs: self.finalized_at_unix_secs,
created_at_unix_ms: self.created_at_unix_ms,
updated_at_unix_secs: self.updated_at_unix_secs,
capture_retention,
};
if !retain_bodies {
for (present, key, state) in [
(
self.request_body.is_some(),
"request",
&mut cloned.request_body_state,
),
(
self.provider_request_body.is_some(),
"provider_request",
&mut cloned.provider_request_body_state,
),
(
self.response_body.is_some(),
"response",
&mut cloned.response_body_state,
),
(
self.client_response_body.is_some(),
"client_response",
&mut cloned.client_response_body_state,
),
] {
if present
&& !matches!(
state,
Some(
UsageBodyCaptureState::None
| UsageBodyCaptureState::Disabled
| UsageBodyCaptureState::Unavailable
)
)
{
*state = Some(UsageBodyCaptureState::Truncated);
super::mark_usage_capture_memory_omitted(&mut cloned.request_metadata, key);
}
}
}
cloned
}
}
impl UpsertUsageRecord {
@@ -2501,15 +2637,59 @@ fn parse_timestamp(value: i64, field_name: &str) -> Result<u64, crate::DataLayer
mod tests {
use super::{
canonical_usage_body_ref_for, extract_provider_actual_service_tier_from_response,
extract_provider_service_tier_from_body, resolve_provider_cache_ttl_minutes,
usage_body_ref, StoredRequestUsageAudit, UpsertUsageRecord, UsageBodyCaptureState,
UsageBodyCaptureStorage, UsageBodyField, UsageProviderPerformanceQuery,
REALTIME_SESSION_METADATA_KEY, USAGE_AVAILABLE_METADATA_KEY,
USAGE_PRICING_AVAILABLE_METADATA_KEY, WEBSOCKET_MODE_METADATA_KEY,
WEBSOCKET_TRANSPORT_METADATA_KEY,
extract_provider_service_tier_from_body, normalize_provider_reasoning_effort,
normalize_provider_service_tier, resolve_provider_cache_ttl_minutes, usage_body_ref,
StoredRequestUsageAudit, UpsertUsageRecord, UsageBodyCaptureState, UsageBodyCaptureStorage,
UsageBodyField, UsageProviderPerformanceQuery, REALTIME_SESSION_METADATA_KEY,
USAGE_AVAILABLE_METADATA_KEY, USAGE_PRICING_AVAILABLE_METADATA_KEY,
WEBSOCKET_MODE_METADATA_KEY, WEBSOCKET_TRANSPORT_METADATA_KEY,
};
use serde_json::{json, Value};
#[test]
fn provider_fact_normalization_preserves_trimmed_byte_limit() {
for normalize in [
normalize_provider_reasoning_effort as fn(&str) -> Option<String>,
normalize_provider_service_tier,
] {
assert_eq!(normalize(" \t\r\n"), None);
assert_eq!(normalize(" HIGH\n"), Some("high".to_string()));
assert_eq!(normalize(&"A".repeat(64)), Some("a".repeat(64)));
assert_eq!(
normalize(&format!(" \t{}\n", "A".repeat(64))),
Some("a".repeat(64))
);
assert_eq!(normalize(&"A".repeat(65)), None);
}
}
#[test]
fn provider_fact_normalization_preserves_non_ascii_case_and_byte_count() {
for normalize in [
normalize_provider_reasoning_effort as fn(&str) -> Option<String>,
normalize_provider_service_tier,
] {
let accepted = format!("{}A", "\u{00c9}".repeat(31));
assert_eq!(
normalize(&accepted),
Some(format!("{}a", "\u{00c9}".repeat(31)))
);
assert_eq!(
normalize(&"\u{00c9}".repeat(32)),
Some("\u{00c9}".repeat(32))
);
assert_eq!(normalize(&format!("{}A", "\u{00c9}".repeat(32))), None);
assert_eq!(normalize("\u{2003}FAST\u{2003}"), Some("fast".to_string()));
}
}
#[test]
fn provider_fact_normalization_rejects_large_input_before_copying() {
let oversized = "A".repeat(4 * 1024 * 1024);
assert_eq!(normalize_provider_reasoning_effort(&oversized), None);
assert_eq!(normalize_provider_service_tier(&oversized), None);
}
fn sample_usage() -> StoredRequestUsageAudit {
StoredRequestUsageAudit::new(
"usage-1".to_string(),
@@ -2700,6 +2880,7 @@ mod tests {
#[test]
fn rejects_invalid_upsert_payload() {
let mut record = UpsertUsageRecord {
capture_retention: Default::default(),
request_id: "".to_string(),
user_id: None,
api_key_id: None,