Skip to content

Commit b6e1ccf

Browse files
committed
chore: consolidated refactoring and documentation improvements
This PR consolidates all refactoring and documentation improvements from PRs #77 and #89. ## Refactoring (from PR #77) ### Timeout Constants - Created centralized cortex_common::timeout module with documented constants - Replaced scattered magic numbers with well-documented constants - Added comprehensive documentation for timeout hierarchy ### Code Quality - Used LazyLock for static regex initialization - Extracted subagent timeout values as named constants - Improved compile-time assertions ## Documentation (from PR #89) ### Timeout Hierarchy Documentation - Added detailed header documentation explaining timeout hierarchy - Documented each timeout constant with its purpose and use case - Established naming conventions for constants vs config fields - Added timeout hierarchy table for quick reference ### Timeout Reference Table | Use Case | Module | Constant | Value | Rationale | |-----------------------------|-----------------------------|-----------------------------|-------|-----------| | Health checks | cortex-common/http_client | HEALTH_CHECK_TIMEOUT | 5s | Quick validation | | Standard HTTP requests | cortex-common/http_client | DEFAULT_TIMEOUT | 30s | Normal API calls | | Connection pool idle | cortex-common/http_client | POOL_IDLE_TIMEOUT | 60s | DNS refresh | | LLM Request (non-streaming) | cortex-exec/runner | DEFAULT_REQUEST_TIMEOUT_SECS| 120s | Model inference | | LLM Streaming total | cortex-common/http_client | STREAMING_TIMEOUT | 300s | Long-running streams | ## Files Modified - src/cortex-common/src/timeout.rs (new) - src/cortex-common/src/http_client.rs - src/cortex-app-server/src/config.rs - src/cortex-exec/src/runner.rs - Multiple other files Closes #77, #89
1 parent c398212 commit b6e1ccf

File tree

19 files changed

+263
-177
lines changed

19 files changed

+263
-177
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cortex-app-server/src/auth.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ impl Claims {
4545
pub fn new(user_id: impl Into<String>, expiry_seconds: u64) -> Self {
4646
let now = SystemTime::now()
4747
.duration_since(UNIX_EPOCH)
48-
.unwrap()
48+
.unwrap_or_default()
4949
.as_secs();
5050

5151
Self {
@@ -75,7 +75,7 @@ impl Claims {
7575
pub fn is_expired(&self) -> bool {
7676
let now = SystemTime::now()
7777
.duration_since(UNIX_EPOCH)
78-
.unwrap()
78+
.unwrap_or_default()
7979
.as_secs();
8080
self.exp < now
8181
}
@@ -187,7 +187,7 @@ impl AuthService {
187187
pub async fn cleanup_revoked_tokens(&self) {
188188
let now = SystemTime::now()
189189
.duration_since(UNIX_EPOCH)
190-
.unwrap()
190+
.unwrap_or_default()
191191
.as_secs();
192192

193193
let mut revoked = self.revoked_tokens.write().await;

src/cortex-app-server/src/config.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,12 +49,18 @@ pub struct ServerConfig {
4949
pub max_body_size: usize,
5050

5151
/// Request timeout in seconds (applies to full request lifecycle).
52+
///
53+
/// See `cortex_common::http_client` module documentation for the complete
54+
/// timeout hierarchy across Cortex services.
5255
#[serde(default = "default_request_timeout")]
5356
pub request_timeout: u64,
5457

5558
/// Read timeout for individual chunks in seconds.
5659
/// Applies to chunked transfer encoding to prevent indefinite hangs
5760
/// when clients disconnect without sending the terminal chunk.
61+
///
62+
/// See `cortex_common::http_client` module documentation for the complete
63+
/// timeout hierarchy across Cortex services.
5864
#[serde(default = "default_read_timeout")]
5965
pub read_timeout: u64,
6066

@@ -71,12 +77,17 @@ pub struct ServerConfig {
7177
pub cors_origins: Vec<String>,
7278

7379
/// Graceful shutdown timeout in seconds.
80+
///
81+
/// See `cortex_common::http_client` module documentation for the complete
82+
/// timeout hierarchy across Cortex services.
7483
#[serde(default = "default_shutdown_timeout")]
7584
pub shutdown_timeout: u64,
7685
}
7786

7887
fn default_shutdown_timeout() -> u64 {
79-
30 // 30 seconds for graceful shutdown
88+
// 30 seconds for graceful shutdown
89+
// See cortex_common::http_client for timeout hierarchy documentation
90+
30
8091
}
8192

8293
fn default_listen_addr() -> String {

src/cortex-app-server/src/storage.rs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,6 @@ pub struct StoredToolCall {
4747

4848
/// Session storage manager.
4949
pub struct SessionStorage {
50-
#[allow(dead_code)]
51-
base_dir: PathBuf,
5250
sessions_dir: PathBuf,
5351
history_dir: PathBuf,
5452
}
@@ -66,7 +64,6 @@ impl SessionStorage {
6664
info!("Session storage initialized at {:?}", base_dir);
6765

6866
Ok(Self {
69-
base_dir,
7067
sessions_dir,
7168
history_dir,
7269
})

src/cortex-app-server/src/streaming.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -510,10 +510,10 @@ async fn session_events_stream(
510510
serde_json::to_string(&StreamEvent::Ping {
511511
timestamp: std::time::SystemTime::now()
512512
.duration_since(std::time::UNIX_EPOCH)
513-
.unwrap()
513+
.unwrap_or_default()
514514
.as_secs(),
515515
})
516-
.unwrap(),
516+
.unwrap_or_default(),
517517
)))
518518
.await;
519519

src/cortex-apply-patch/src/hunk.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -250,9 +250,6 @@ pub struct SearchReplace {
250250
pub search: String,
251251
/// The text to replace with.
252252
pub replace: String,
253-
/// Replace all occurrences (true) or just the first (false).
254-
#[allow(dead_code)]
255-
pub replace_all: bool,
256253
}
257254

258255
impl SearchReplace {
@@ -266,16 +263,8 @@ impl SearchReplace {
266263
path: path.into(),
267264
search: search.into(),
268265
replace: replace.into(),
269-
replace_all: false,
270266
}
271267
}
272-
273-
/// Set whether to replace all occurrences.
274-
#[allow(dead_code)]
275-
pub fn with_replace_all(mut self, replace_all: bool) -> Self {
276-
self.replace_all = replace_all;
277-
self
278-
}
279268
}
280269

281270
#[cfg(test)]

src/cortex-common/src/config_substitution.rs

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,22 @@
88
99
use regex::Regex;
1010
use std::path::PathBuf;
11+
use std::sync::LazyLock;
1112
use thiserror::Error;
1213

14+
/// Static regex for environment variable substitution: {env:VAR} or {env:VAR:default}
15+
/// Group 1: variable name
16+
/// Group 2: optional default value (after second colon)
17+
static ENV_REGEX: LazyLock<Regex> = LazyLock::new(|| {
18+
Regex::new(r"\{env:([^:}]+)(?::([^}]*))?\}").expect("env regex pattern is valid and tested")
19+
});
20+
21+
/// Static regex for file content substitution: {file:path}
22+
/// Group 1: file path
23+
static FILE_REGEX: LazyLock<Regex> = LazyLock::new(|| {
24+
Regex::new(r"\{file:([^}]+)\}").expect("file regex pattern is valid and tested")
25+
});
26+
1327
/// Errors that can occur during configuration substitution.
1428
#[derive(Debug, Error)]
1529
pub enum SubstitutionError {
@@ -42,11 +56,13 @@ pub enum SubstitutionError {
4256
///
4357
/// Handles replacement of `{env:...}` and `{file:...}` placeholders
4458
/// in configuration strings.
59+
///
60+
/// This struct uses statically initialized regex patterns via `LazyLock`,
61+
/// making regex compilation a one-time cost shared across all instances.
4562
pub struct ConfigSubstitution {
46-
/// Regex for environment variable substitution: {env:VAR} or {env:VAR:default}
47-
env_regex: Regex,
48-
/// Regex for file content substitution: {file:path}
49-
file_regex: Regex,
63+
// This struct is kept for API compatibility.
64+
// Regex patterns are now static module-level constants.
65+
_private: (),
5066
}
5167

5268
impl Default for ConfigSubstitution {
@@ -56,22 +72,13 @@ impl Default for ConfigSubstitution {
5672
}
5773

5874
impl ConfigSubstitution {
59-
/// Creates a new `ConfigSubstitution` instance with compiled regex patterns.
75+
/// Creates a new `ConfigSubstitution` instance.
76+
///
77+
/// The regex patterns are statically initialized on first use,
78+
/// so creating multiple instances has no additional cost.
6079
#[must_use]
6180
pub fn new() -> Self {
62-
Self {
63-
// Matches {env:VAR_NAME} or {env:VAR_NAME:default_value}
64-
// Group 1: variable name
65-
// Group 2: optional default value (after second colon)
66-
env_regex: Regex::new(r"\{env:([^:}]+)(?::([^}]*))?\}").unwrap_or_else(|e| {
67-
panic!("Failed to compile env regex: {e}");
68-
}),
69-
// Matches {file:path}
70-
// Group 1: file path
71-
file_regex: Regex::new(r"\{file:([^}]+)\}").unwrap_or_else(|e| {
72-
panic!("Failed to compile file regex: {e}");
73-
}),
74-
}
81+
Self { _private: () }
7582
}
7683

7784
/// Substitutes all variables in a string.
@@ -109,8 +116,7 @@ impl ConfigSubstitution {
109116
let mut error: Option<SubstitutionError> = None;
110117

111118
// Collect all matches first to avoid borrowing issues
112-
let matches: Vec<_> = self
113-
.env_regex
119+
let matches: Vec<_> = ENV_REGEX
114120
.captures_iter(input)
115121
.map(|cap| {
116122
let full_match = cap.get(0).map(|m| m.as_str().to_string());
@@ -155,8 +161,7 @@ impl ConfigSubstitution {
155161
let mut error: Option<SubstitutionError> = None;
156162

157163
// Collect all matches first
158-
let matches: Vec<_> = self
159-
.file_regex
164+
let matches: Vec<_> = FILE_REGEX
160165
.captures_iter(input)
161166
.map(|cap| {
162167
let full_match = cap.get(0).map(|m| m.as_str().to_string());

src/cortex-common/src/http_client.rs

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,99 @@
99
//!
1010
//! DNS caching is configured with reasonable TTL to allow failover and load
1111
//! balancer updates (#2177).
12+
//!
13+
//! # Timeout Configuration Guide
14+
//!
15+
//! This section documents the timeout hierarchy across the Cortex codebase. Use this
16+
//! as a reference when configuring timeouts for new features or debugging timeout issues.
17+
//!
18+
//! ## Timeout Hierarchy
19+
//!
20+
//! | Use Case | Timeout | Constant/Location | Rationale |
21+
//! |-----------------------------|---------|--------------------------------------------|-----------------------------------------|
22+
//! | Health checks | 5s | `HEALTH_CHECK_TIMEOUT` (this module) | Quick validation of service status |
23+
//! | Standard HTTP requests | 30s | `DEFAULT_TIMEOUT` (this module) | Normal API calls with reasonable margin |
24+
//! | Per-chunk read (streaming) | 30s | `read_timeout` (cortex-app-server/config) | Individual chunk timeout during stream |
25+
//! | Pool idle timeout | 60s | `POOL_IDLE_TIMEOUT` (this module) | DNS re-resolution for failover |
26+
//! | LLM Request (non-streaming) | 120s | `DEFAULT_REQUEST_TIMEOUT_SECS` (cortex-exec/runner) | Model inference takes time |
27+
//! | LLM Streaming total | 300s | `STREAMING_TIMEOUT` (this module) | Long-running streaming responses |
28+
//! | Server request lifecycle | 300s | `request_timeout` (cortex-app-server/config) | Full HTTP request/response cycle |
29+
//! | Entire exec session | 600s | `DEFAULT_TIMEOUT_SECS` (cortex-exec/runner) | Multi-turn conversation limit |
30+
//! | Graceful shutdown | 30s | `shutdown_timeout` (cortex-app-server/config) | Time for cleanup on shutdown |
31+
//!
32+
//! ## Module-Specific Timeouts
33+
//!
34+
//! ### cortex-common (this module)
35+
//! - `DEFAULT_TIMEOUT` (30s): Use for standard API calls.
36+
//! - `STREAMING_TIMEOUT` (300s): Use for LLM streaming endpoints.
37+
//! - `HEALTH_CHECK_TIMEOUT` (5s): Use for health/readiness checks.
38+
//! - `POOL_IDLE_TIMEOUT` (60s): Connection pool cleanup for DNS freshness.
39+
//!
40+
//! ### cortex-exec (runner.rs)
41+
//! - `DEFAULT_TIMEOUT_SECS` (600s): Maximum duration for entire exec session.
42+
//! - `DEFAULT_REQUEST_TIMEOUT_SECS` (120s): Single LLM request timeout.
43+
//!
44+
//! ### cortex-app-server (config.rs)
45+
//! - `request_timeout` (300s): Full request lifecycle timeout.
46+
//! - `read_timeout` (30s): Per-chunk timeout for streaming reads.
47+
//! - `shutdown_timeout` (30s): Graceful shutdown duration.
48+
//!
49+
//! ### cortex-engine (api_client.rs)
50+
//! - Re-exports constants from this module for consistency.
51+
//!
52+
//! ## Recommendations
53+
//!
54+
//! When adding new timeout configurations:
55+
//! 1. Use constants from this module when possible for consistency.
56+
//! 2. Document any new timeout constants with their rationale.
57+
//! 3. Consider the timeout hierarchy - inner timeouts should be shorter than outer ones.
58+
//! 4. For LLM operations, use longer timeouts (120s-300s) to accommodate model inference.
59+
//! 5. For health checks and quick validations, use short timeouts (5s-10s).
1260
1361
use reqwest::Client;
1462
use std::time::Duration;
1563

64+
// ============================================================
65+
// Timeout Configuration Constants
66+
// ============================================================
67+
//
68+
// Timeout Hierarchy Documentation
69+
// ===============================
70+
//
71+
// This module defines the authoritative timeout constants for HTTP operations.
72+
// Other modules should reference these constants or document deviations.
73+
//
74+
// Timeout Categories:
75+
// - Request timeouts: Total time for a complete request/response cycle
76+
// - Connection timeouts: Time to establish TCP connection
77+
// - Read timeouts: Time to receive response data
78+
// - Pool timeouts: How long idle connections stay in pool
79+
//
80+
// Recommended Naming Convention:
81+
// - Constants: SCREAMING_SNAKE_CASE with Duration type
82+
// - Config fields: snake_case with _secs suffix for u64 values
83+
//
84+
// ============================================================
85+
1686
/// User-Agent string for all HTTP requests
1787
pub const USER_AGENT: &str = concat!("cortex-cli/", env!("CARGO_PKG_VERSION"));
1888

19-
/// Default timeout for standard API requests (30 seconds)
89+
/// Default timeout for standard HTTP requests (30 seconds).
90+
/// Used for non-streaming API calls, health checks with retries, etc.
2091
pub const DEFAULT_TIMEOUT: Duration = Duration::from_secs(30);
2192

22-
/// Extended timeout for LLM streaming requests (5 minutes)
93+
/// Timeout for streaming HTTP requests (5 minutes).
94+
/// Longer duration to accommodate LLM inference time.
2395
pub const STREAMING_TIMEOUT: Duration = Duration::from_secs(300);
2496

25-
/// Short timeout for health checks (5 seconds)
97+
/// Timeout for health check requests (5 seconds).
98+
/// Short timeout since health endpoints should respond quickly.
2699
pub const HEALTH_CHECK_TIMEOUT: Duration = Duration::from_secs(5);
27100

28-
/// Connection pool idle timeout to ensure DNS is re-resolved periodically.
101+
/// Idle timeout for connection pool (60 seconds).
102+
/// Connections are closed after being idle for this duration
103+
/// to allow DNS re-resolution for services behind load balancers.
29104
/// This helps with failover scenarios where DNS records change (#2177).
30-
/// Set to 60 seconds to balance between performance and DNS freshness.
31105
pub const POOL_IDLE_TIMEOUT: Duration = Duration::from_secs(60);
32106

33107
/// Creates an HTTP client with default configuration (30s timeout).

src/cortex-common/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ pub mod signal_safety;
1818
pub mod subprocess_env;
1919
pub mod subprocess_output;
2020
pub mod text_sanitize;
21+
pub mod timeout;
2122
pub mod truncate;
2223

2324
#[cfg(feature = "cli")]
@@ -73,6 +74,11 @@ pub use subprocess_output::{
7374
pub use text_sanitize::{
7475
has_control_chars, normalize_code_fences, sanitize_control_chars, sanitize_for_terminal,
7576
};
77+
pub use timeout::{
78+
DEFAULT_BATCH_TIMEOUT_SECS, DEFAULT_EXEC_TIMEOUT_SECS, DEFAULT_HEALTH_CHECK_TIMEOUT_SECS,
79+
DEFAULT_READ_TIMEOUT_SECS, DEFAULT_REQUEST_TIMEOUT_SECS, DEFAULT_SHUTDOWN_TIMEOUT_SECS,
80+
DEFAULT_STREAMING_TIMEOUT_SECS,
81+
};
7682
pub use truncate::{
7783
truncate_command, truncate_first_line, truncate_for_display, truncate_id, truncate_id_default,
7884
truncate_model_name, truncate_with_ellipsis, truncate_with_unicode_ellipsis,

0 commit comments

Comments
 (0)