mirror of
https://github.com/openai/codex.git
synced 2026-09-11 20:36:49 +00:00
## Why Diagnostic reports can span several envelopes, and the previous 10-second shared network budget could expire before slow uploads and their attachments completed. ## What changed - Give each diagnostic report a single five-minute deadline shared by the event, attachments, retries, and retry backoff. - Stop reading or sending later attachments once the deadline or a Sentry rate limit is reached. - Limit the app server to three concurrent report uploads and return an overloaded JSON-RPC error for additional requests until a slot is released. ## Testing Add coverage for slow multi-envelope reports, deadline-aware retries, skipped attachments, rate-limit handling, and concurrency-slot release after failures. GitOrigin-RevId: bdacf6c9731df16d2763e204565986eb6d540233
93 lines
3.1 KiB
Rust
93 lines
3.1 KiB
Rust
use std::time::Duration;
|
|
|
|
use anyhow::Result;
|
|
use app_test_support::TestAppServer;
|
|
use codex_app_server_protocol::RequestId;
|
|
use pretty_assertions::assert_eq;
|
|
use serde_json::json;
|
|
use tokio::io::AsyncBufReadExt;
|
|
use tokio::io::AsyncWriteExt;
|
|
use tokio::io::BufReader;
|
|
use tokio::net::TcpListener;
|
|
use tokio::time::timeout;
|
|
|
|
#[tokio::test]
|
|
async fn feedback_upload_limits_concurrency_and_releases_failed_uploads() -> Result<()> {
|
|
let proxy = TcpListener::bind("127.0.0.1:0").await?;
|
|
let proxy_uri = format!("http://{}", proxy.local_addr()?);
|
|
let mut app_server = TestAppServer::builder()
|
|
.with_env_overrides(&[
|
|
("HTTPS_PROXY", Some(proxy_uri.as_str())),
|
|
("https_proxy", Some(proxy_uri.as_str())),
|
|
("NO_PROXY", Some("")),
|
|
("no_proxy", Some("")),
|
|
])
|
|
.build_initialized()
|
|
.await?;
|
|
|
|
let mut pending = Vec::new();
|
|
for _ in 0..3 {
|
|
let request_id = app_server
|
|
.send_raw_request(
|
|
"feedback/upload",
|
|
Some(json!({ "classification": "bug", "includeLogs": false })),
|
|
)
|
|
.await?;
|
|
let (stream, _) = timeout(Duration::from_secs(/*secs*/ 15), proxy.accept()).await??;
|
|
let mut stream = BufReader::new(stream);
|
|
let mut request = String::new();
|
|
timeout(
|
|
Duration::from_secs(/*secs*/ 15),
|
|
stream.read_line(&mut request),
|
|
)
|
|
.await??;
|
|
assert!(request.starts_with("CONNECT "));
|
|
pending.push((request_id, stream.into_inner()));
|
|
}
|
|
|
|
let excess_id = app_server
|
|
.send_raw_request(
|
|
"feedback/upload",
|
|
Some(json!({ "classification": "bug", "includeLogs": false })),
|
|
)
|
|
.await?;
|
|
let error = timeout(
|
|
Duration::from_secs(/*secs*/ 15),
|
|
app_server.read_stream_until_error_message(RequestId::Integer(excess_id)),
|
|
)
|
|
.await??;
|
|
|
|
assert_eq!(error.error.code, -32001);
|
|
|
|
let unavailable =
|
|
b"HTTP/1.1 503 Service Unavailable\r\nContent-Length: 0\r\nConnection: close\r\n\r\n";
|
|
for (request_id, mut stream) in pending {
|
|
stream.write_all(unavailable).await?;
|
|
stream.shutdown().await?;
|
|
let error = timeout(
|
|
Duration::from_secs(/*secs*/ 15),
|
|
app_server.read_stream_until_error_message(RequestId::Integer(request_id)),
|
|
)
|
|
.await??;
|
|
assert_eq!(error.error.code, -32603);
|
|
assert!(error.error.message.contains("failed to upload feedback"));
|
|
}
|
|
|
|
let request_id = app_server
|
|
.send_raw_request(
|
|
"feedback/upload",
|
|
Some(json!({ "classification": "bug", "includeLogs": false })),
|
|
)
|
|
.await?;
|
|
let (mut stream, _) = timeout(Duration::from_secs(/*secs*/ 15), proxy.accept()).await??;
|
|
stream.write_all(unavailable).await?;
|
|
stream.shutdown().await?;
|
|
let error = timeout(
|
|
Duration::from_secs(/*secs*/ 15),
|
|
app_server.read_stream_until_error_message(RequestId::Integer(request_id)),
|
|
)
|
|
.await??;
|
|
assert_eq!(error.error.code, -32603);
|
|
Ok(())
|
|
}
|