[model-gateway] Remove legacy RouterMetrics and Rename SmgMetrics to Metrics and smg_labels to metrics_labels (#15160)
This commit is contained in:
@@ -8,7 +8,7 @@ use std::{
|
|||||||
|
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use crate::observability::metrics::{RouterMetrics, SmgMetrics};
|
use crate::observability::metrics::Metrics;
|
||||||
|
|
||||||
/// Circuit breaker configuration
|
/// Circuit breaker configuration
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -96,10 +96,7 @@ impl CircuitBreaker {
|
|||||||
/// Create a new circuit breaker with custom configuration and metric label
|
/// Create a new circuit breaker with custom configuration and metric label
|
||||||
pub fn with_config_and_label(config: CircuitBreakerConfig, metric_label: String) -> Self {
|
pub fn with_config_and_label(config: CircuitBreakerConfig, metric_label: String) -> Self {
|
||||||
let init_state = CircuitState::Closed;
|
let init_state = CircuitState::Closed;
|
||||||
// New metrics
|
Metrics::set_worker_cb_state(&metric_label, init_state.to_int());
|
||||||
SmgMetrics::set_worker_cb_state(&metric_label, init_state.to_int());
|
|
||||||
// Legacy metrics
|
|
||||||
RouterMetrics::set_cb_state(&metric_label, init_state.to_int());
|
|
||||||
Self {
|
Self {
|
||||||
state: Arc::new(RwLock::new(init_state)),
|
state: Arc::new(RwLock::new(init_state)),
|
||||||
consecutive_failures: Arc::new(AtomicU32::new(0)),
|
consecutive_failures: Arc::new(AtomicU32::new(0)),
|
||||||
@@ -156,10 +153,7 @@ impl CircuitBreaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let outcome_str = if success { "success" } else { "failure" };
|
let outcome_str = if success { "success" } else { "failure" };
|
||||||
// New metrics
|
Metrics::record_worker_cb_outcome(&self.metric_label, outcome_str);
|
||||||
SmgMetrics::record_worker_cb_outcome(&self.metric_label, outcome_str);
|
|
||||||
// Legacy metrics
|
|
||||||
RouterMetrics::record_cb_outcome(&self.metric_label, outcome_str);
|
|
||||||
self.publish_gauge_metrics();
|
self.publish_gauge_metrics();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -238,12 +232,8 @@ impl CircuitBreaker {
|
|||||||
let from = old_state.as_str();
|
let from = old_state.as_str();
|
||||||
let to = new_state.as_str();
|
let to = new_state.as_str();
|
||||||
info!("Circuit breaker state transition: {} -> {}", from, to);
|
info!("Circuit breaker state transition: {} -> {}", from, to);
|
||||||
// New metrics
|
Metrics::record_worker_cb_transition(&self.metric_label, from, to);
|
||||||
SmgMetrics::record_worker_cb_transition(&self.metric_label, from, to);
|
Metrics::set_worker_cb_state(&self.metric_label, new_state.to_int());
|
||||||
SmgMetrics::set_worker_cb_state(&self.metric_label, new_state.to_int());
|
|
||||||
// Legacy metrics
|
|
||||||
RouterMetrics::record_cb_state_transition(&self.metric_label, from, to);
|
|
||||||
RouterMetrics::set_cb_state(&self.metric_label, new_state.to_int());
|
|
||||||
self.publish_gauge_metrics();
|
self.publish_gauge_metrics();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -323,14 +313,9 @@ impl CircuitBreaker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO maybe publish whenever the variable is changed
|
|
||||||
fn publish_gauge_metrics(&self) {
|
fn publish_gauge_metrics(&self) {
|
||||||
// New metrics
|
Metrics::set_worker_cb_consecutive_failures(&self.metric_label, self.failure_count());
|
||||||
SmgMetrics::set_worker_cb_consecutive_failures(&self.metric_label, self.failure_count());
|
Metrics::set_worker_cb_consecutive_successes(&self.metric_label, self.success_count());
|
||||||
SmgMetrics::set_worker_cb_consecutive_successes(&self.metric_label, self.success_count());
|
|
||||||
// Legacy metrics
|
|
||||||
RouterMetrics::set_cb_consecutive_failures(&self.metric_label, self.failure_count());
|
|
||||||
RouterMetrics::set_cb_consecutive_successes(&self.metric_label, self.success_count());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ use crate::{
|
|||||||
WorkerRemovalRequest,
|
WorkerRemovalRequest,
|
||||||
},
|
},
|
||||||
mcp::McpConfig,
|
mcp::McpConfig,
|
||||||
observability::metrics::RouterMetrics,
|
|
||||||
protocols::worker_spec::{JobStatus, WorkerConfigRequest, WorkerUpdateRequest},
|
protocols::worker_spec::{JobStatus, WorkerConfigRequest, WorkerUpdateRequest},
|
||||||
workflow::{WorkflowContext, WorkflowEngine, WorkflowId, WorkflowInstanceId, WorkflowStatus},
|
workflow::{WorkflowContext, WorkflowEngine, WorkflowId, WorkflowInstanceId, WorkflowStatus},
|
||||||
};
|
};
|
||||||
@@ -232,7 +231,6 @@ impl JobQueue {
|
|||||||
pub async fn submit(&self, job: Job) -> Result<(), String> {
|
pub async fn submit(&self, job: Job) -> Result<(), String> {
|
||||||
// Check if context is still alive before accepting jobs
|
// Check if context is still alive before accepting jobs
|
||||||
if self.context.upgrade().is_none() {
|
if self.context.upgrade().is_none() {
|
||||||
RouterMetrics::record_job_shutdown_rejected();
|
|
||||||
return Err("Job queue shutting down: AppContext dropped".to_string());
|
return Err("Job queue shutting down: AppContext dropped".to_string());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -249,7 +247,6 @@ impl JobQueue {
|
|||||||
match self.tx.send(job).await {
|
match self.tx.send(job).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
let (queue_depth, available_permits) = self.get_load_info();
|
let (queue_depth, available_permits) = self.get_load_info();
|
||||||
RouterMetrics::set_job_queue_depth(queue_depth);
|
|
||||||
debug!(
|
debug!(
|
||||||
"Job submitted: type={}, worker={}, queue_depth={}, available_slots={}",
|
"Job submitted: type={}, worker={}, queue_depth={}, available_slots={}",
|
||||||
job_type, worker_url, queue_depth, available_permits
|
job_type, worker_url, queue_depth, available_permits
|
||||||
@@ -257,7 +254,6 @@ impl JobQueue {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
RouterMetrics::record_job_queue_full();
|
|
||||||
self.status_map.remove(&worker_url);
|
self.status_map.remove(&worker_url);
|
||||||
let (queue_depth, _) = self.get_load_info();
|
let (queue_depth, _) = self.get_load_info();
|
||||||
Err(format!(
|
Err(format!(
|
||||||
@@ -806,40 +802,30 @@ impl JobQueue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Record job completion metrics and update status
|
/// Update job status on completion
|
||||||
fn record_job_completion(
|
fn record_job_completion(
|
||||||
job_type: &'static str,
|
job_type: &'static str,
|
||||||
worker_url: &str,
|
worker_url: &str,
|
||||||
duration: Duration,
|
_duration: Duration,
|
||||||
result: &Result<String, String>,
|
result: &Result<String, String>,
|
||||||
status_map: &Arc<DashMap<String, JobStatus>>,
|
status_map: &Arc<DashMap<String, JobStatus>>,
|
||||||
) {
|
) {
|
||||||
RouterMetrics::record_job_duration(job_type, duration);
|
|
||||||
|
|
||||||
match result {
|
match result {
|
||||||
Ok(message) => {
|
Ok(message) => {
|
||||||
RouterMetrics::record_job_success(job_type);
|
|
||||||
status_map.remove(worker_url);
|
status_map.remove(worker_url);
|
||||||
debug!(
|
debug!(
|
||||||
"Completed job: type={}, worker={}, duration={:.3}s, result={}",
|
"Completed job: type={}, worker={}, result={}",
|
||||||
job_type,
|
job_type, worker_url, message
|
||||||
worker_url,
|
|
||||||
duration.as_secs_f64(),
|
|
||||||
message
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
RouterMetrics::record_job_failure(job_type);
|
|
||||||
status_map.insert(
|
status_map.insert(
|
||||||
worker_url.to_string(),
|
worker_url.to_string(),
|
||||||
JobStatus::failed(job_type, worker_url, error.clone()),
|
JobStatus::failed(job_type, worker_url, error.clone()),
|
||||||
);
|
);
|
||||||
warn!(
|
warn!(
|
||||||
"Failed job: type={}, worker={}, duration={:.3}s, error={}",
|
"Failed job: type={}, worker={}, error={}",
|
||||||
job_type,
|
job_type, worker_url, error
|
||||||
worker_url,
|
|
||||||
duration.as_secs_f64(),
|
|
||||||
error
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,8 +111,8 @@ impl RetryExecutor {
|
|||||||
/// resp
|
/// resp
|
||||||
/// },
|
/// },
|
||||||
/// |res, _| matches!(res.status(), StatusCode::REQUEST_TIMEOUT | StatusCode::TOO_MANY_REQUESTS | StatusCode::INTERNAL_SERVER_ERROR | StatusCode::BAD_GATEWAY | StatusCode::SERVICE_UNAVAILABLE | StatusCode::GATEWAY_TIMEOUT),
|
/// |res, _| matches!(res.status(), StatusCode::REQUEST_TIMEOUT | StatusCode::TOO_MANY_REQUESTS | StatusCode::INTERNAL_SERVER_ERROR | StatusCode::BAD_GATEWAY | StatusCode::SERVICE_UNAVAILABLE | StatusCode::GATEWAY_TIMEOUT),
|
||||||
/// |delay, attempt| RouterMetrics::record_retry_backoff_duration(delay, attempt),
|
/// |delay, _attempt| { /* record backoff metrics */ },
|
||||||
/// || RouterMetrics::record_retries_exhausted("/route"),
|
/// || { /* record retries exhausted */ },
|
||||||
/// ).await;
|
/// ).await;
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn execute_response_with_retry<Op, Fut, ShouldRetry, OnBackoff, OnExhausted>(
|
pub async fn execute_response_with_retry<Op, Fut, ShouldRetry, OnBackoff, OnExhausted>(
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use tracing::{debug, error, info, warn};
|
|||||||
use crate::{
|
use crate::{
|
||||||
app_context::AppContext,
|
app_context::AppContext,
|
||||||
mcp::{config::McpServerConfig, manager::McpManager},
|
mcp::{config::McpServerConfig, manager::McpManager},
|
||||||
observability::metrics::SmgMetrics,
|
observability::metrics::Metrics,
|
||||||
workflow::*,
|
workflow::*,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -153,7 +153,7 @@ impl StepExecutor for RegisterMcpServerStep {
|
|||||||
mcp_manager.register_static_server(config_request.name.clone(), mcp_client);
|
mcp_manager.register_static_server(config_request.name.clone(), mcp_client);
|
||||||
|
|
||||||
// Update active MCP servers metric
|
// Update active MCP servers metric
|
||||||
SmgMetrics::set_mcp_servers_active(mcp_manager.list_servers().len());
|
Metrics::set_mcp_servers_active(mcp_manager.list_servers().len());
|
||||||
|
|
||||||
info!("Registered MCP server: {}", config_request.name);
|
info!("Registered MCP server: {}", config_request.name);
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use tracing::{debug, warn};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
app_context::AppContext,
|
app_context::AppContext,
|
||||||
observability::metrics::{RouterMetrics, SmgMetrics},
|
observability::metrics::Metrics,
|
||||||
workflow::{StepExecutor, StepResult, WorkflowContext, WorkflowError, WorkflowResult},
|
workflow::{StepExecutor, StepResult, WorkflowContext, WorkflowError, WorkflowResult},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -63,9 +63,6 @@ impl StepExecutor for RemoveFromWorkerRegistryStep {
|
|||||||
debug!("Removed {} worker(s) from registry", removed_count);
|
debug!("Removed {} worker(s) from registry", removed_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update active workers metric (legacy)
|
|
||||||
RouterMetrics::set_active_workers(app_context.worker_registry.len());
|
|
||||||
|
|
||||||
// Update Layer 3 worker pool size metrics for unique configurations
|
// Update Layer 3 worker pool size metrics for unique configurations
|
||||||
for (worker_type, connection_mode, model_id) in unique_configs {
|
for (worker_type, connection_mode, model_id) in unique_configs {
|
||||||
// Get labels before moving values into get_workers_filtered
|
// Get labels before moving values into get_workers_filtered
|
||||||
@@ -83,7 +80,7 @@ impl StepExecutor for RemoveFromWorkerRegistryStep {
|
|||||||
)
|
)
|
||||||
.len();
|
.len();
|
||||||
|
|
||||||
SmgMetrics::set_worker_pool_size(
|
Metrics::set_worker_pool_size(
|
||||||
worker_type_label,
|
worker_type_label,
|
||||||
connection_mode_label,
|
connection_mode_label,
|
||||||
&model_id,
|
&model_id,
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use tracing::debug;
|
|||||||
use crate::{
|
use crate::{
|
||||||
app_context::AppContext,
|
app_context::AppContext,
|
||||||
core::Worker,
|
core::Worker,
|
||||||
observability::metrics::{RouterMetrics, SmgMetrics},
|
observability::metrics::Metrics,
|
||||||
workflow::{StepExecutor, StepResult, WorkflowContext, WorkflowResult},
|
workflow::{StepExecutor, StepResult, WorkflowContext, WorkflowResult},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -37,9 +37,6 @@ impl StepExecutor for RegisterWorkersStep {
|
|||||||
worker_ids.push(worker_id);
|
worker_ids.push(worker_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update active workers metric (legacy)
|
|
||||||
RouterMetrics::set_active_workers(app_context.worker_registry.len());
|
|
||||||
|
|
||||||
// Collect unique worker configurations to avoid redundant metric updates
|
// Collect unique worker configurations to avoid redundant metric updates
|
||||||
let unique_configs: HashSet<_> = workers
|
let unique_configs: HashSet<_> = workers
|
||||||
.iter()
|
.iter()
|
||||||
@@ -70,7 +67,7 @@ impl StepExecutor for RegisterWorkersStep {
|
|||||||
)
|
)
|
||||||
.len();
|
.len();
|
||||||
|
|
||||||
SmgMetrics::set_worker_pool_size(
|
Metrics::set_worker_pool_size(
|
||||||
worker_type_label,
|
worker_type_label,
|
||||||
connection_mode_label,
|
connection_mode_label,
|
||||||
&model_id,
|
&model_id,
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{BasicWorkerBuilder, DPAwareWorkerBuilder},
|
core::{BasicWorkerBuilder, DPAwareWorkerBuilder},
|
||||||
observability::metrics::{smg_labels, RouterMetrics, SmgMetrics},
|
observability::metrics::{metrics_labels, Metrics},
|
||||||
protocols::worker_spec::WorkerInfo,
|
protocols::worker_spec::WorkerInfo,
|
||||||
routers::grpc::client::GrpcClient,
|
routers::grpc::client::GrpcClient,
|
||||||
};
|
};
|
||||||
@@ -314,8 +314,8 @@ impl ConnectionMode {
|
|||||||
/// Get the metric label for this connection mode
|
/// Get the metric label for this connection mode
|
||||||
pub fn as_metric_label(&self) -> &'static str {
|
pub fn as_metric_label(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
ConnectionMode::Http => smg_labels::CONNECTION_HTTP,
|
ConnectionMode::Http => metrics_labels::CONNECTION_HTTP,
|
||||||
ConnectionMode::Grpc { .. } => smg_labels::CONNECTION_GRPC,
|
ConnectionMode::Grpc { .. } => metrics_labels::CONNECTION_GRPC,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -404,9 +404,9 @@ impl WorkerType {
|
|||||||
/// Get the metric label for this worker type
|
/// Get the metric label for this worker type
|
||||||
pub fn as_metric_label(&self) -> &'static str {
|
pub fn as_metric_label(&self) -> &'static str {
|
||||||
match self {
|
match self {
|
||||||
WorkerType::Regular => smg_labels::WORKER_REGULAR,
|
WorkerType::Regular => metrics_labels::WORKER_REGULAR,
|
||||||
WorkerType::Prefill { .. } => smg_labels::WORKER_PREFILL,
|
WorkerType::Prefill { .. } => metrics_labels::WORKER_PREFILL,
|
||||||
WorkerType::Decode => smg_labels::WORKER_DECODE,
|
WorkerType::Decode => metrics_labels::WORKER_DECODE,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -557,8 +557,7 @@ impl BasicWorker {
|
|||||||
|
|
||||||
fn update_running_requests_metrics(&self) {
|
fn update_running_requests_metrics(&self) {
|
||||||
let load = self.load();
|
let load = self.load();
|
||||||
RouterMetrics::set_running_requests(self.url(), load);
|
Metrics::set_worker_requests_active(self.url(), load);
|
||||||
SmgMetrics::set_worker_requests_active(self.url(), load);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -586,7 +585,6 @@ impl Worker for BasicWorker {
|
|||||||
|
|
||||||
fn set_healthy(&self, healthy: bool) {
|
fn set_healthy(&self, healthy: bool) {
|
||||||
self.healthy.store(healthy, Ordering::Release);
|
self.healthy.store(healthy, Ordering::Release);
|
||||||
RouterMetrics::set_worker_health(self.url(), healthy);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn check_health_async(&self) -> WorkerResult<()> {
|
async fn check_health_async(&self) -> WorkerResult<()> {
|
||||||
@@ -603,7 +601,7 @@ impl Worker for BasicWorker {
|
|||||||
let successes = self.consecutive_successes.fetch_add(1, Ordering::AcqRel) + 1;
|
let successes = self.consecutive_successes.fetch_add(1, Ordering::AcqRel) + 1;
|
||||||
|
|
||||||
// Record health check success metric
|
// Record health check success metric
|
||||||
SmgMetrics::record_worker_health_check(worker_type_str, smg_labels::CB_SUCCESS);
|
Metrics::record_worker_health_check(worker_type_str, metrics_labels::CB_SUCCESS);
|
||||||
|
|
||||||
if !self.is_healthy()
|
if !self.is_healthy()
|
||||||
&& successes >= self.metadata.health_config.success_threshold as usize
|
&& successes >= self.metadata.health_config.success_threshold as usize
|
||||||
@@ -617,7 +615,7 @@ impl Worker for BasicWorker {
|
|||||||
let failures = self.consecutive_failures.fetch_add(1, Ordering::AcqRel) + 1;
|
let failures = self.consecutive_failures.fetch_add(1, Ordering::AcqRel) + 1;
|
||||||
|
|
||||||
// Record health check failure metric
|
// Record health check failure metric
|
||||||
SmgMetrics::record_worker_health_check(worker_type_str, smg_labels::CB_FAILURE);
|
Metrics::record_worker_health_check(worker_type_str, metrics_labels::CB_FAILURE);
|
||||||
|
|
||||||
if self.is_healthy()
|
if self.is_healthy()
|
||||||
&& failures >= self.metadata.health_config.failure_threshold as usize
|
&& failures >= self.metadata.health_config.failure_threshold as usize
|
||||||
|
|||||||
@@ -7,10 +7,7 @@ use std::sync::{Arc, RwLock};
|
|||||||
use dashmap::DashMap;
|
use dashmap::DashMap;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
use crate::{
|
use crate::core::{ConnectionMode, RuntimeType, Worker, WorkerType};
|
||||||
core::{ConnectionMode, RuntimeType, Worker, WorkerType},
|
|
||||||
observability::metrics::RouterMetrics,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Unique identifier for a worker
|
/// Unique identifier for a worker
|
||||||
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
#[derive(Debug, Clone, Hash, Eq, PartialEq)]
|
||||||
@@ -142,7 +139,6 @@ impl WorkerRegistry {
|
|||||||
}
|
}
|
||||||
|
|
||||||
worker.set_healthy(false);
|
worker.set_healthy(false);
|
||||||
RouterMetrics::remove_worker_metrics(worker.url());
|
|
||||||
|
|
||||||
Some(worker)
|
Some(worker)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ use tracing::{debug, error, field::Empty, info, info_span, warn, Span};
|
|||||||
|
|
||||||
pub use crate::core::token_bucket::TokenBucket;
|
pub use crate::core::token_bucket::TokenBucket;
|
||||||
use crate::{
|
use crate::{
|
||||||
observability::metrics::{smg_labels, RouterMetrics, SmgMetrics},
|
observability::metrics::{metrics_labels, Metrics},
|
||||||
routers::error::extract_error_code_from_response,
|
routers::error::extract_error_code_from_response,
|
||||||
server::AppState,
|
server::AppState,
|
||||||
wasm::{
|
wasm::{
|
||||||
@@ -308,8 +308,6 @@ impl<B> OnRequest<B> for RequestLogger {
|
|||||||
span.record("request_id", request_id.0.as_str());
|
span.record("request_id", request_id.0.as_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
RouterMetrics::record_http_request();
|
|
||||||
|
|
||||||
// Log the request start
|
// Log the request start
|
||||||
info!(
|
info!(
|
||||||
target: "sgl_model_gateway::request",
|
target: "sgl_model_gateway::request",
|
||||||
@@ -339,12 +337,8 @@ impl<B> OnResponse<B> for ResponseLogger {
|
|||||||
|
|
||||||
let error_code = extract_error_code_from_response(response);
|
let error_code = extract_error_code_from_response(response);
|
||||||
|
|
||||||
// TODO support `route` information
|
// Layer 1: HTTP metrics
|
||||||
RouterMetrics::record_http_status_code(status_code, error_code);
|
Metrics::record_http_response(status_code, error_code);
|
||||||
RouterMetrics::record_request_duration(latency);
|
|
||||||
|
|
||||||
// New SMG metrics (Layer 1: HTTP)
|
|
||||||
SmgMetrics::record_http_response(status_code, error_code);
|
|
||||||
|
|
||||||
// Record these in the span for structured logging/observability tools
|
// Record these in the span for structured logging/observability tools
|
||||||
span.record("status_code", status_code);
|
span.record("status_code", status_code);
|
||||||
@@ -520,7 +514,7 @@ pub async fn concurrency_limit_middleware(
|
|||||||
// Try to acquire token immediately
|
// Try to acquire token immediately
|
||||||
if token_bucket.try_acquire(1.0).await.is_ok() {
|
if token_bucket.try_acquire(1.0).await.is_ok() {
|
||||||
debug!("Acquired token immediately");
|
debug!("Acquired token immediately");
|
||||||
SmgMetrics::record_http_rate_limit(smg_labels::RATE_LIMIT_ALLOWED);
|
Metrics::record_http_rate_limit(metrics_labels::RATE_LIMIT_ALLOWED);
|
||||||
let response = next.run(request).await;
|
let response = next.run(request).await;
|
||||||
|
|
||||||
// Wrap the response body with TokenGuardBody to return token when stream ends
|
// Wrap the response body with TokenGuardBody to return token when stream ends
|
||||||
@@ -545,22 +539,19 @@ pub async fn concurrency_limit_middleware(
|
|||||||
// Try to send to queue
|
// Try to send to queue
|
||||||
match queue_tx.try_send(queued) {
|
match queue_tx.try_send(queued) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
// On successful enqueue, update embeddings queue gauge if applicable
|
// On successful enqueue, update embeddings queue counter if applicable
|
||||||
if is_embeddings {
|
if is_embeddings {
|
||||||
let new_val = EMBEDDINGS_QUEUE_SIZE.fetch_add(1, Ordering::Relaxed) + 1;
|
EMBEDDINGS_QUEUE_SIZE.fetch_add(1, Ordering::Relaxed);
|
||||||
RouterMetrics::set_embeddings_queue_size(new_val as usize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wait for token from queue processor
|
// Wait for token from queue processor
|
||||||
match permit_rx.await {
|
match permit_rx.await {
|
||||||
Ok(Ok(())) => {
|
Ok(Ok(())) => {
|
||||||
debug!("Acquired token from queue");
|
debug!("Acquired token from queue");
|
||||||
SmgMetrics::record_http_rate_limit(smg_labels::RATE_LIMIT_ALLOWED);
|
Metrics::record_http_rate_limit(metrics_labels::RATE_LIMIT_ALLOWED);
|
||||||
// Dequeue for embeddings
|
// Dequeue for embeddings
|
||||||
if is_embeddings {
|
if is_embeddings {
|
||||||
let new_val =
|
EMBEDDINGS_QUEUE_SIZE.fetch_sub(1, Ordering::Relaxed);
|
||||||
EMBEDDINGS_QUEUE_SIZE.fetch_sub(1, Ordering::Relaxed) - 1;
|
|
||||||
RouterMetrics::set_embeddings_queue_size(new_val as usize);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = next.run(request).await;
|
let response = next.run(request).await;
|
||||||
@@ -572,23 +563,19 @@ pub async fn concurrency_limit_middleware(
|
|||||||
}
|
}
|
||||||
Ok(Err(status)) => {
|
Ok(Err(status)) => {
|
||||||
warn!("Queue returned error status: {}", status);
|
warn!("Queue returned error status: {}", status);
|
||||||
SmgMetrics::record_http_rate_limit(smg_labels::RATE_LIMIT_REJECTED);
|
Metrics::record_http_rate_limit(metrics_labels::RATE_LIMIT_REJECTED);
|
||||||
// Dequeue for embeddings on error
|
// Dequeue for embeddings on error
|
||||||
if is_embeddings {
|
if is_embeddings {
|
||||||
let new_val =
|
EMBEDDINGS_QUEUE_SIZE.fetch_sub(1, Ordering::Relaxed);
|
||||||
EMBEDDINGS_QUEUE_SIZE.fetch_sub(1, Ordering::Relaxed) - 1;
|
|
||||||
RouterMetrics::set_embeddings_queue_size(new_val as usize);
|
|
||||||
}
|
}
|
||||||
status.into_response()
|
status.into_response()
|
||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!("Queue response channel closed");
|
error!("Queue response channel closed");
|
||||||
SmgMetrics::record_http_rate_limit(smg_labels::RATE_LIMIT_REJECTED);
|
Metrics::record_http_rate_limit(metrics_labels::RATE_LIMIT_REJECTED);
|
||||||
// Dequeue for embeddings on channel error
|
// Dequeue for embeddings on channel error
|
||||||
if is_embeddings {
|
if is_embeddings {
|
||||||
let new_val =
|
EMBEDDINGS_QUEUE_SIZE.fetch_sub(1, Ordering::Relaxed);
|
||||||
EMBEDDINGS_QUEUE_SIZE.fetch_sub(1, Ordering::Relaxed) - 1;
|
|
||||||
RouterMetrics::set_embeddings_queue_size(new_val as usize);
|
|
||||||
}
|
}
|
||||||
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
StatusCode::INTERNAL_SERVER_ERROR.into_response()
|
||||||
}
|
}
|
||||||
@@ -596,13 +583,13 @@ pub async fn concurrency_limit_middleware(
|
|||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
warn!("Request queue is full, returning 429");
|
warn!("Request queue is full, returning 429");
|
||||||
SmgMetrics::record_http_rate_limit(smg_labels::RATE_LIMIT_REJECTED);
|
Metrics::record_http_rate_limit(metrics_labels::RATE_LIMIT_REJECTED);
|
||||||
StatusCode::TOO_MANY_REQUESTS.into_response()
|
StatusCode::TOO_MANY_REQUESTS.into_response()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
warn!("No tokens available and queuing is disabled, returning 429");
|
warn!("No tokens available and queuing is disabled, returning 429");
|
||||||
SmgMetrics::record_http_rate_limit(smg_labels::RATE_LIMIT_REJECTED);
|
Metrics::record_http_rate_limit(metrics_labels::RATE_LIMIT_REJECTED);
|
||||||
StatusCode::TOO_MANY_REQUESTS.into_response()
|
StatusCode::TOO_MANY_REQUESTS.into_response()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -663,22 +650,22 @@ where
|
|||||||
Box::pin(async move {
|
Box::pin(async move {
|
||||||
// Increment inside async block - ensures no leak if future is dropped before polling
|
// Increment inside async block - ensures no leak if future is dropped before polling
|
||||||
let active = ACTIVE_HTTP_CONNECTIONS.fetch_add(1, Ordering::Relaxed) + 1;
|
let active = ACTIVE_HTTP_CONNECTIONS.fetch_add(1, Ordering::Relaxed) + 1;
|
||||||
SmgMetrics::set_http_connections_active(active as usize);
|
Metrics::set_http_connections_active(active as usize);
|
||||||
|
|
||||||
// Capture result before decrementing to ensure decrement happens on error too
|
// Capture result before decrementing to ensure decrement happens on error too
|
||||||
let result = inner.call(req).await;
|
let result = inner.call(req).await;
|
||||||
|
|
||||||
// Always decrement, regardless of success or failure
|
// Always decrement, regardless of success or failure
|
||||||
let active = ACTIVE_HTTP_CONNECTIONS.fetch_sub(1, Ordering::Relaxed) - 1;
|
let active = ACTIVE_HTTP_CONNECTIONS.fetch_sub(1, Ordering::Relaxed) - 1;
|
||||||
SmgMetrics::set_http_connections_active(active as usize);
|
Metrics::set_http_connections_active(active as usize);
|
||||||
|
|
||||||
let response = result?;
|
let response = result?;
|
||||||
|
|
||||||
let duration = start.elapsed();
|
let duration = start.elapsed();
|
||||||
let status_class = status_to_class(response.status().as_u16());
|
let status_class = status_to_class(response.status().as_u16());
|
||||||
|
|
||||||
SmgMetrics::record_http_request(&method, &path, status_class);
|
Metrics::record_http_request(&method, &path, status_class);
|
||||||
SmgMetrics::record_http_duration(&method, &path, duration);
|
Metrics::record_http_duration(&method, &path, duration);
|
||||||
|
|
||||||
Ok(response)
|
Ok(response)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -24,197 +24,6 @@ impl Default for PrometheusConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn init_metrics() {
|
pub fn init_metrics() {
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_requests_total",
|
|
||||||
"Total number of requests by route and method"
|
|
||||||
);
|
|
||||||
describe_histogram!(
|
|
||||||
"sgl_router_request_duration_seconds",
|
|
||||||
"Request duration in seconds"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_request_errors_total",
|
|
||||||
"Total number of request errors by route and error type"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_attempt_http_responses_total",
|
|
||||||
"Total number of upstream engine HTTP responses by status code"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_retries_total",
|
|
||||||
"Total number of request retries by route"
|
|
||||||
);
|
|
||||||
describe_histogram!(
|
|
||||||
"sgl_router_retry_backoff_duration_seconds",
|
|
||||||
"Backoff duration in seconds by attempt index"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_retries_exhausted_total",
|
|
||||||
"Total number of requests that exhausted retries by route"
|
|
||||||
);
|
|
||||||
|
|
||||||
describe_gauge!(
|
|
||||||
"sgl_router_cb_state",
|
|
||||||
"Circuit breaker state per worker (0=closed, 1=open, 2=half_open)"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_cb_state_transitions_total",
|
|
||||||
"Total number of circuit breaker state transitions by worker"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_cb_outcomes_total",
|
|
||||||
"Total number of circuit breaker outcomes by worker and outcome type (success/failure)"
|
|
||||||
);
|
|
||||||
describe_gauge!(
|
|
||||||
"sgl_router_cb_consecutive_failures",
|
|
||||||
"Current consecutive failure count per worker circuit breaker"
|
|
||||||
);
|
|
||||||
describe_gauge!(
|
|
||||||
"sgl_router_cb_consecutive_successes",
|
|
||||||
"Current consecutive success count per worker circuit breaker"
|
|
||||||
);
|
|
||||||
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_discovery_watcher_errors_total",
|
|
||||||
"Total number of Kubernetes watcher errors"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_discovery_watcher_restarts_total",
|
|
||||||
"Total number of Kubernetes watcher restarts"
|
|
||||||
);
|
|
||||||
|
|
||||||
describe_gauge!(
|
|
||||||
"sgl_router_active_workers",
|
|
||||||
"Number of currently active workers"
|
|
||||||
);
|
|
||||||
describe_gauge!(
|
|
||||||
"sgl_router_worker_health",
|
|
||||||
"Worker health status (1=healthy, 0=unhealthy)"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_processed_requests_total",
|
|
||||||
"Total requests processed by each worker"
|
|
||||||
);
|
|
||||||
|
|
||||||
describe_gauge!(
|
|
||||||
"sgl_router_job_queue_depth",
|
|
||||||
"Current number of pending jobs in the queue"
|
|
||||||
);
|
|
||||||
describe_histogram!(
|
|
||||||
"sgl_router_job_duration_seconds",
|
|
||||||
"Job processing duration in seconds by job type"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_job_success_total",
|
|
||||||
"Total successful job completions by job type"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_job_failure_total",
|
|
||||||
"Total failed job completions by job type"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_job_queue_full_total",
|
|
||||||
"Total number of jobs rejected due to queue full"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_job_shutdown_rejected_total",
|
|
||||||
"Total number of jobs rejected due to shutdown"
|
|
||||||
);
|
|
||||||
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_policy_decisions_total",
|
|
||||||
"Total routing policy decisions by policy and worker"
|
|
||||||
);
|
|
||||||
describe_counter!("sgl_router_cache_hits_total", "Total cache hits");
|
|
||||||
describe_counter!("sgl_router_cache_misses_total", "Total cache misses");
|
|
||||||
describe_gauge!(
|
|
||||||
"sgl_router_tree_size",
|
|
||||||
"Current tree size for cache-aware routing"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_load_balancing_events_total",
|
|
||||||
"Total load balancing trigger events"
|
|
||||||
);
|
|
||||||
describe_gauge!("sgl_router_max_load", "Maximum worker load");
|
|
||||||
describe_gauge!("sgl_router_min_load", "Minimum worker load");
|
|
||||||
|
|
||||||
describe_counter!("sgl_router_pd_requests_total", "Total PD requests by route");
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_pd_prefill_requests_total",
|
|
||||||
"Total prefill requests per worker"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_pd_decode_requests_total",
|
|
||||||
"Total decode requests per worker"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_pd_errors_total",
|
|
||||||
"Total PD errors by error type"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_pd_prefill_errors_total",
|
|
||||||
"Total prefill server errors"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_pd_decode_errors_total",
|
|
||||||
"Total decode server errors"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_pd_stream_errors_total",
|
|
||||||
"Total streaming errors per worker"
|
|
||||||
);
|
|
||||||
describe_histogram!(
|
|
||||||
"sgl_router_pd_request_duration_seconds",
|
|
||||||
"PD request duration by route"
|
|
||||||
);
|
|
||||||
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_discovery_updates_total",
|
|
||||||
"Total service discovery update events"
|
|
||||||
);
|
|
||||||
describe_gauge!(
|
|
||||||
"sgl_router_discovery_workers_added",
|
|
||||||
"Number of workers added in last discovery update"
|
|
||||||
);
|
|
||||||
describe_gauge!(
|
|
||||||
"sgl_router_discovery_workers_removed",
|
|
||||||
"Number of workers removed in last discovery update"
|
|
||||||
);
|
|
||||||
|
|
||||||
describe_histogram!(
|
|
||||||
"sgl_router_generate_duration_seconds",
|
|
||||||
"Generate request duration"
|
|
||||||
);
|
|
||||||
|
|
||||||
describe_counter!("sgl_router_embeddings_total", "Total embedding requests");
|
|
||||||
describe_histogram!(
|
|
||||||
"sgl_router_embeddings_duration_seconds",
|
|
||||||
"Embedding request duration"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_embeddings_errors_total",
|
|
||||||
"Embedding request errors"
|
|
||||||
);
|
|
||||||
describe_gauge!("sgl_router_embeddings_queue_size", "Embedding queue size");
|
|
||||||
|
|
||||||
describe_gauge!(
|
|
||||||
"sgl_router_running_requests",
|
|
||||||
"Number of running requests per worker"
|
|
||||||
);
|
|
||||||
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_http_requests_total",
|
|
||||||
"Total number of HTTP requests"
|
|
||||||
);
|
|
||||||
describe_counter!(
|
|
||||||
"sgl_router_http_responses_total",
|
|
||||||
"Total number of HTTP responses by status code and error code"
|
|
||||||
);
|
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// SMG Metrics (new layered architecture)
|
|
||||||
// ========================================================================
|
|
||||||
|
|
||||||
// Layer 1: HTTP metrics
|
// Layer 1: HTTP metrics
|
||||||
describe_counter!(
|
describe_counter!(
|
||||||
"smg_http_requests_total",
|
"smg_http_requests_total",
|
||||||
@@ -414,339 +223,8 @@ pub fn start_prometheus(config: PrometheusConfig) {
|
|||||||
.expect("failed to install Prometheus metrics exporter");
|
.expect("failed to install Prometheus metrics exporter");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct RouterMetrics;
|
|
||||||
|
|
||||||
impl RouterMetrics {
|
|
||||||
pub fn record_request(route: &'static str) {
|
|
||||||
counter!("sgl_router_requests_total",
|
|
||||||
"route" => route
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_request_duration(duration: Duration) {
|
|
||||||
histogram!("sgl_router_request_duration_seconds").record(duration.as_secs_f64());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_request_error(route: &'static str, error_type: &'static str) {
|
|
||||||
counter!("sgl_router_request_errors_total",
|
|
||||||
"route" => route,
|
|
||||||
"error_type" => error_type
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO unify metric names
|
|
||||||
pub fn record_attempt_http_response(route: &'static str, status_code: u16, error_code: &str) {
|
|
||||||
counter!("sgl_router_attempt_http_responses_total",
|
|
||||||
"route" => route,
|
|
||||||
"status_code" => status_code.to_string(),
|
|
||||||
"error_code" => error_code.to_string()
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_retry(route: &'static str) {
|
|
||||||
counter!("sgl_router_retries_total",
|
|
||||||
"route" => route
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_retry_backoff_duration(duration: Duration, attempt: u32) {
|
|
||||||
histogram!("sgl_router_retry_backoff_duration_seconds",
|
|
||||||
"attempt" => attempt.to_string()
|
|
||||||
)
|
|
||||||
.record(duration.as_secs_f64());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_retries_exhausted(route: &'static str) {
|
|
||||||
counter!("sgl_router_retries_exhausted_total",
|
|
||||||
"route" => route
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_worker_health(worker_url: &str, healthy: bool) {
|
|
||||||
gauge!("sgl_router_worker_health",
|
|
||||||
"worker" => worker_url.to_string()
|
|
||||||
)
|
|
||||||
.set(if healthy { 1.0 } else { 0.0 });
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_active_workers(count: usize) {
|
|
||||||
gauge!("sgl_router_active_workers").set(count as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_processed_request(worker_url: &str) {
|
|
||||||
counter!("sgl_router_processed_requests_total",
|
|
||||||
"worker" => worker_url.to_string()
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_policy_decision(policy: &'static str, worker: &str) {
|
|
||||||
counter!("sgl_router_policy_decisions_total",
|
|
||||||
"policy" => policy,
|
|
||||||
"worker" => worker.to_string()
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_cache_hit() {
|
|
||||||
counter!("sgl_router_cache_hits_total").increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_cache_miss() {
|
|
||||||
counter!("sgl_router_cache_misses_total").increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_tree_size(worker: &str, size: usize) {
|
|
||||||
gauge!("sgl_router_tree_size",
|
|
||||||
"worker" => worker.to_string()
|
|
||||||
)
|
|
||||||
.set(size as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_load_balancing_event() {
|
|
||||||
counter!("sgl_router_load_balancing_events_total").increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_load_range(max_load: usize, min_load: usize) {
|
|
||||||
gauge!("sgl_router_max_load").set(max_load as f64);
|
|
||||||
gauge!("sgl_router_min_load").set(min_load as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_pd_request(route: &'static str) {
|
|
||||||
counter!("sgl_router_pd_requests_total",
|
|
||||||
"route" => route
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_pd_request_duration(route: &'static str, duration: Duration) {
|
|
||||||
histogram!("sgl_router_pd_request_duration_seconds",
|
|
||||||
"route" => route
|
|
||||||
)
|
|
||||||
.record(duration.as_secs_f64());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_pd_prefill_request(worker: &str) {
|
|
||||||
counter!("sgl_router_pd_prefill_requests_total",
|
|
||||||
"worker" => worker.to_string()
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_pd_decode_request(worker: &str) {
|
|
||||||
counter!("sgl_router_pd_decode_requests_total",
|
|
||||||
"worker" => worker.to_string()
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_pd_error(error_type: &'static str) {
|
|
||||||
counter!("sgl_router_pd_errors_total",
|
|
||||||
"error_type" => error_type
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_pd_prefill_error(worker: &str) {
|
|
||||||
counter!("sgl_router_pd_prefill_errors_total",
|
|
||||||
"worker" => worker.to_string()
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_pd_decode_error(worker: &str) {
|
|
||||||
counter!("sgl_router_pd_decode_errors_total",
|
|
||||||
"worker" => worker.to_string()
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_pd_stream_error(worker: &str) {
|
|
||||||
counter!("sgl_router_pd_stream_errors_total",
|
|
||||||
"worker" => worker.to_string()
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_discovery_update(added: usize, removed: usize) {
|
|
||||||
counter!("sgl_router_discovery_updates_total").increment(1);
|
|
||||||
gauge!("sgl_router_discovery_workers_added").set(added as f64);
|
|
||||||
gauge!("sgl_router_discovery_workers_removed").set(removed as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_generate_duration(duration: Duration) {
|
|
||||||
histogram!("sgl_router_generate_duration_seconds").record(duration.as_secs_f64());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_embeddings_request() {
|
|
||||||
counter!("sgl_router_embeddings_total").increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_embeddings_duration(duration: Duration) {
|
|
||||||
histogram!("sgl_router_embeddings_duration_seconds").record(duration.as_secs_f64());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_embeddings_error(error_type: &str) {
|
|
||||||
counter!(
|
|
||||||
"sgl_router_embeddings_errors_total",
|
|
||||||
"error_type" => error_type.to_string()
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_embeddings_queue_size(size: usize) {
|
|
||||||
gauge!("sgl_router_embeddings_queue_size").set(size as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_classify_request() {
|
|
||||||
counter!("sgl_router_classify_total").increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_classify_duration(duration: Duration) {
|
|
||||||
histogram!("sgl_router_classify_duration_seconds").record(duration.as_secs_f64());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_classify_error(error_type: &str) {
|
|
||||||
counter!(
|
|
||||||
"sgl_router_classify_errors_total",
|
|
||||||
"error_type" => error_type.to_string()
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_classify_queue_size(size: usize) {
|
|
||||||
gauge!("sgl_router_classify_queue_size").set(size as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_running_requests(worker: &str, count: usize) {
|
|
||||||
gauge!("sgl_router_running_requests",
|
|
||||||
"worker" => worker.to_string()
|
|
||||||
)
|
|
||||||
.set(count as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_cb_state(worker: &str, state_code: u8) {
|
|
||||||
gauge!("sgl_router_cb_state",
|
|
||||||
"worker" => worker.to_string()
|
|
||||||
)
|
|
||||||
.set(state_code as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_cb_state_transition(worker: &str, from: &'static str, to: &'static str) {
|
|
||||||
counter!("sgl_router_cb_state_transitions_total",
|
|
||||||
"worker" => worker.to_string(),
|
|
||||||
"from" => from,
|
|
||||||
"to" => to
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_cb_outcome(worker: &str, outcome: &'static str) {
|
|
||||||
counter!("sgl_router_cb_outcomes_total",
|
|
||||||
"worker" => worker.to_string(),
|
|
||||||
"outcome" => outcome
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_cb_consecutive_failures(worker: &str, count: u32) {
|
|
||||||
gauge!("sgl_router_cb_consecutive_failures",
|
|
||||||
"worker" => worker.to_string()
|
|
||||||
)
|
|
||||||
.set(count as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_cb_consecutive_successes(worker: &str, count: u32) {
|
|
||||||
gauge!("sgl_router_cb_consecutive_successes",
|
|
||||||
"worker" => worker.to_string()
|
|
||||||
)
|
|
||||||
.set(count as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_discovery_watcher_error() {
|
|
||||||
counter!("sgl_router_discovery_watcher_errors_total").increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_discovery_watcher_restart() {
|
|
||||||
counter!("sgl_router_discovery_watcher_restarts_total").increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO delete the metrics (instead of setting them to zero)
|
|
||||||
pub fn remove_worker_metrics(worker_url: &str) {
|
|
||||||
gauge!("sgl_router_cb_consecutive_failures","worker" => worker_url.to_string()).set(0.0);
|
|
||||||
gauge!("sgl_router_cb_consecutive_successes","worker" => worker_url.to_string()).set(0.0);
|
|
||||||
gauge!("sgl_router_running_requests","worker" => worker_url.to_string()).set(0.0);
|
|
||||||
gauge!("sgl_router_tree_size","worker" => worker_url.to_string()).set(0.0);
|
|
||||||
|
|
||||||
// Zero for these metrics have special valid meaning, thus we set to -1 temporarily
|
|
||||||
// (and will remove them completely after https://github.com/metrics-rs/metrics/issues/653)
|
|
||||||
gauge!("sgl_router_cb_state","worker" => worker_url.to_string()).set(-1.0);
|
|
||||||
gauge!("sgl_router_worker_health","worker" => worker_url.to_string()).set(-1.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_job_queue_depth(depth: usize) {
|
|
||||||
gauge!("sgl_router_job_queue_depth").set(depth as f64);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_job_duration(job_type: &'static str, duration: Duration) {
|
|
||||||
histogram!("sgl_router_job_duration_seconds",
|
|
||||||
"job_type" => job_type
|
|
||||||
)
|
|
||||||
.record(duration.as_secs_f64());
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_job_success(job_type: &'static str) {
|
|
||||||
counter!("sgl_router_job_success_total",
|
|
||||||
"job_type" => job_type
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_job_failure(job_type: &'static str) {
|
|
||||||
counter!("sgl_router_job_failure_total",
|
|
||||||
"job_type" => job_type
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_job_queue_full() {
|
|
||||||
counter!("sgl_router_job_queue_full_total").increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_job_shutdown_rejected() {
|
|
||||||
counter!("sgl_router_job_shutdown_rejected_total").increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is different from the following:
|
|
||||||
// * sgl_router_requests_total: bump when a request is handled and response is to be returned, thus very different from this.
|
|
||||||
// * sgl_router_processed_requests_total: bump when routing decision is made.
|
|
||||||
// Here we want a metric to directly reflect user's experience ("I am sending a request")
|
|
||||||
// when viewing the router as a blackbox, and is bumped immediately when the request arrives.
|
|
||||||
// TODO: add route name
|
|
||||||
pub fn record_http_request() {
|
|
||||||
counter!("sgl_router_http_requests_total").increment(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn record_http_status_code(status_code: u16, error_code: &str) {
|
|
||||||
counter!("sgl_router_http_responses_total",
|
|
||||||
"status_code" => status_code.to_string(),
|
|
||||||
"error_code" => error_code.to_string()
|
|
||||||
)
|
|
||||||
.increment(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
|
||||||
// SMG Metrics - New layered architecture
|
|
||||||
// ============================================================================
|
|
||||||
|
|
||||||
/// Label constants for consistent metric labeling
|
/// Label constants for consistent metric labeling
|
||||||
pub mod smg_labels {
|
pub mod metrics_labels {
|
||||||
// Router types
|
// Router types
|
||||||
pub const ROUTER_OPENAI: &str = "openai";
|
pub const ROUTER_OPENAI: &str = "openai";
|
||||||
pub const ROUTER_HTTP: &str = "http";
|
pub const ROUTER_HTTP: &str = "http";
|
||||||
@@ -808,12 +286,6 @@ pub mod smg_labels {
|
|||||||
pub const REGISTRATION_SUCCESS: &str = "success";
|
pub const REGISTRATION_SUCCESS: &str = "success";
|
||||||
pub const REGISTRATION_FAILED: &str = "failed";
|
pub const REGISTRATION_FAILED: &str = "failed";
|
||||||
pub const REGISTRATION_DUPLICATE: &str = "duplicate";
|
pub const REGISTRATION_DUPLICATE: &str = "duplicate";
|
||||||
|
|
||||||
// Deregistration reasons
|
|
||||||
pub const DEREGISTRATION_HEALTH_CHECK_FAILED: &str = "health_check_failed";
|
|
||||||
pub const DEREGISTRATION_TIMEOUT: &str = "timeout";
|
|
||||||
pub const DEREGISTRATION_MANUAL: &str = "manual";
|
|
||||||
pub const DEREGISTRATION_SHUTDOWN: &str = "shutdown";
|
|
||||||
pub const DEREGISTRATION_POD_DELETED: &str = "pod_deleted";
|
pub const DEREGISTRATION_POD_DELETED: &str = "pod_deleted";
|
||||||
|
|
||||||
// Rate limit results
|
// Rate limit results
|
||||||
@@ -835,19 +307,10 @@ pub mod smg_labels {
|
|||||||
pub const ERROR_BACKEND: &str = "backend_error";
|
pub const ERROR_BACKEND: &str = "backend_error";
|
||||||
pub const ERROR_VALIDATION: &str = "validation_error";
|
pub const ERROR_VALIDATION: &str = "validation_error";
|
||||||
pub const ERROR_INTERNAL: &str = "internal_error";
|
pub const ERROR_INTERNAL: &str = "internal_error";
|
||||||
|
|
||||||
// Pipeline stages (gRPC router)
|
|
||||||
pub const STAGE_PREPARATION: &str = "preparation";
|
|
||||||
pub const STAGE_WORKER_SELECTION: &str = "worker_selection";
|
|
||||||
pub const STAGE_CLIENT_ACQUISITION: &str = "client_acquisition";
|
|
||||||
pub const STAGE_REQUEST_BUILDING: &str = "request_building";
|
|
||||||
pub const STAGE_DISPATCH_METADATA: &str = "dispatch_metadata";
|
|
||||||
pub const STAGE_REQUEST_EXECUTION: &str = "request_execution";
|
|
||||||
pub const STAGE_RESPONSE_PROCESSING: &str = "response_processing";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// SMG Metrics helper struct for the new layered metrics architecture
|
/// SMG Metrics helper struct for the new layered metrics architecture
|
||||||
pub struct SmgMetrics;
|
pub struct Metrics;
|
||||||
|
|
||||||
/// Parameters for recording streaming metrics.
|
/// Parameters for recording streaming metrics.
|
||||||
pub struct StreamingMetricsParams<'a> {
|
pub struct StreamingMetricsParams<'a> {
|
||||||
@@ -869,7 +332,7 @@ pub struct StreamingMetricsParams<'a> {
|
|||||||
pub output_tokens: u64,
|
pub output_tokens: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SmgMetrics {
|
impl Metrics {
|
||||||
/// Record an HTTP request
|
/// Record an HTTP request
|
||||||
pub fn record_http_request(method: &str, path: &str, status_class: &str) {
|
pub fn record_http_request(method: &str, path: &str, status_class: &str) {
|
||||||
counter!(
|
counter!(
|
||||||
@@ -1151,7 +614,7 @@ impl SmgMetrics {
|
|||||||
"backend_type" => backend_type,
|
"backend_type" => backend_type,
|
||||||
"model" => model.clone(),
|
"model" => model.clone(),
|
||||||
"endpoint" => endpoint,
|
"endpoint" => endpoint,
|
||||||
"token_type" => smg_labels::TOKEN_INPUT
|
"token_type" => metrics_labels::TOKEN_INPUT
|
||||||
)
|
)
|
||||||
.increment(input);
|
.increment(input);
|
||||||
}
|
}
|
||||||
@@ -1163,7 +626,7 @@ impl SmgMetrics {
|
|||||||
"backend_type" => backend_type,
|
"backend_type" => backend_type,
|
||||||
"model" => model,
|
"model" => model,
|
||||||
"endpoint" => endpoint,
|
"endpoint" => endpoint,
|
||||||
"token_type" => smg_labels::TOKEN_OUTPUT
|
"token_type" => metrics_labels::TOKEN_OUTPUT
|
||||||
)
|
)
|
||||||
.increment(output_tokens);
|
.increment(output_tokens);
|
||||||
}
|
}
|
||||||
@@ -1628,7 +1091,7 @@ mod tests {
|
|||||||
let _matching_metrics = [
|
let _matching_metrics = [
|
||||||
"request_duration_seconds",
|
"request_duration_seconds",
|
||||||
"response_duration_seconds",
|
"response_duration_seconds",
|
||||||
"sgl_router_request_duration_seconds",
|
"smg_request_duration_seconds",
|
||||||
];
|
];
|
||||||
|
|
||||||
let _non_matching_metrics = ["duration_total", "duration_seconds_total", "other_metric"];
|
let _non_matching_metrics = ["duration_total", "duration_seconds_total", "other_metric"];
|
||||||
@@ -1680,37 +1143,6 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_metrics_static_methods() {
|
|
||||||
RouterMetrics::record_request("/generate");
|
|
||||||
RouterMetrics::record_request_duration(Duration::from_millis(100));
|
|
||||||
RouterMetrics::record_request_error("/generate", "timeout");
|
|
||||||
RouterMetrics::record_retry("/generate");
|
|
||||||
|
|
||||||
RouterMetrics::set_worker_health("http://worker1", true);
|
|
||||||
RouterMetrics::record_processed_request("http://worker1");
|
|
||||||
|
|
||||||
RouterMetrics::record_policy_decision("random", "http://worker1");
|
|
||||||
RouterMetrics::record_cache_hit();
|
|
||||||
RouterMetrics::record_cache_miss();
|
|
||||||
RouterMetrics::set_tree_size("http://worker1", 1000);
|
|
||||||
RouterMetrics::record_load_balancing_event();
|
|
||||||
RouterMetrics::set_load_range(20, 5);
|
|
||||||
|
|
||||||
RouterMetrics::record_pd_request("/v1/chat/completions");
|
|
||||||
RouterMetrics::record_pd_request_duration("/v1/chat/completions", Duration::from_secs(1));
|
|
||||||
RouterMetrics::record_pd_prefill_request("http://prefill1");
|
|
||||||
RouterMetrics::record_pd_decode_request("http://decode1");
|
|
||||||
RouterMetrics::record_pd_error("invalid_request");
|
|
||||||
RouterMetrics::record_pd_prefill_error("http://prefill1");
|
|
||||||
RouterMetrics::record_pd_decode_error("http://decode1");
|
|
||||||
RouterMetrics::record_pd_stream_error("http://decode1");
|
|
||||||
|
|
||||||
RouterMetrics::record_discovery_update(3, 1);
|
|
||||||
RouterMetrics::record_generate_duration(Duration::from_secs(2));
|
|
||||||
RouterMetrics::set_running_requests("http://worker1", 15);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_port_already_in_use() {
|
fn test_port_already_in_use() {
|
||||||
let port = 29123;
|
let port = 29123;
|
||||||
@@ -1739,74 +1171,4 @@ mod tests {
|
|||||||
|
|
||||||
assert_eq!(socket_addr.to_string(), "127.0.0.1:29000");
|
assert_eq!(socket_addr.to_string(), "127.0.0.1:29000");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_concurrent_metric_updates() {
|
|
||||||
use std::{
|
|
||||||
sync::{
|
|
||||||
atomic::{AtomicBool, Ordering},
|
|
||||||
Arc,
|
|
||||||
},
|
|
||||||
thread,
|
|
||||||
};
|
|
||||||
|
|
||||||
let done = Arc::new(AtomicBool::new(false));
|
|
||||||
let mut handles = vec![];
|
|
||||||
|
|
||||||
for i in 0..3 {
|
|
||||||
let done_clone = done.clone();
|
|
||||||
let handle = thread::spawn(move || {
|
|
||||||
let worker = format!("http://worker{}", i);
|
|
||||||
while !done_clone.load(Ordering::Relaxed) {
|
|
||||||
RouterMetrics::record_processed_request(&worker);
|
|
||||||
thread::sleep(Duration::from_millis(1));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
handles.push(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(10));
|
|
||||||
done.store(true, Ordering::Relaxed);
|
|
||||||
|
|
||||||
for handle in handles {
|
|
||||||
handle.join().unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_empty_string_metrics() {
|
|
||||||
RouterMetrics::record_request("");
|
|
||||||
RouterMetrics::set_worker_health("", true);
|
|
||||||
RouterMetrics::record_policy_decision("", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_very_long_metric_labels() {
|
|
||||||
let long_label = "a".repeat(1000);
|
|
||||||
|
|
||||||
RouterMetrics::record_request("/very_long_test_route");
|
|
||||||
RouterMetrics::set_worker_health(&long_label, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_special_characters_in_labels() {
|
|
||||||
let special_labels = [
|
|
||||||
"test/with/slashes",
|
|
||||||
"test-with-dashes",
|
|
||||||
"test_with_underscores",
|
|
||||||
"test.with.dots",
|
|
||||||
"test:with:colons",
|
|
||||||
];
|
|
||||||
|
|
||||||
for label in special_labels {
|
|
||||||
RouterMetrics::record_request(label);
|
|
||||||
RouterMetrics::set_worker_health(label, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_extreme_metric_values() {
|
|
||||||
RouterMetrics::record_request_duration(Duration::from_nanos(1));
|
|
||||||
RouterMetrics::record_request_duration(Duration::from_secs(86400));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@ use rand::Rng;
|
|||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use super::{get_healthy_worker_indices, tree::Tree, CacheAwareConfig, LoadBalancingPolicy};
|
use super::{get_healthy_worker_indices, tree::Tree, CacheAwareConfig, LoadBalancingPolicy};
|
||||||
use crate::{core::Worker, observability::metrics::RouterMetrics};
|
use crate::core::Worker;
|
||||||
|
|
||||||
/// Cache-aware routing policy
|
/// Cache-aware routing policy
|
||||||
///
|
///
|
||||||
@@ -135,11 +135,6 @@ impl CacheAwarePolicy {
|
|||||||
let tree = tree_ref.value();
|
let tree = tree_ref.value();
|
||||||
tree.evict_tenant_by_size(max_tree_size);
|
tree.evict_tenant_by_size(max_tree_size);
|
||||||
|
|
||||||
// Update tree size metrics per worker (tenant)
|
|
||||||
for entry in tree.tenant_char_count.iter() {
|
|
||||||
RouterMetrics::set_tree_size(entry.key(), *entry.value());
|
|
||||||
}
|
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Cache eviction completed for model {}, max_size: {}",
|
"Cache eviction completed for model {}, max_size: {}",
|
||||||
model_id, max_tree_size
|
model_id, max_tree_size
|
||||||
@@ -274,9 +269,6 @@ impl CacheAwarePolicy {
|
|||||||
max_load, min_load, worker_loads
|
max_load, min_load, worker_loads
|
||||||
);
|
);
|
||||||
|
|
||||||
RouterMetrics::record_load_balancing_event();
|
|
||||||
RouterMetrics::set_load_range(max_load, min_load);
|
|
||||||
|
|
||||||
// Use shortest queue when imbalanced
|
// Use shortest queue when imbalanced
|
||||||
let min_load_idx = healthy_indices
|
let min_load_idx = healthy_indices
|
||||||
.iter()
|
.iter()
|
||||||
@@ -302,8 +294,6 @@ impl CacheAwarePolicy {
|
|||||||
|
|
||||||
// Increment processed counter
|
// Increment processed counter
|
||||||
workers[min_load_idx].increment_processed();
|
workers[min_load_idx].increment_processed();
|
||||||
RouterMetrics::record_processed_request(workers[min_load_idx].url());
|
|
||||||
RouterMetrics::record_policy_decision(self.name(), workers[min_load_idx].url());
|
|
||||||
|
|
||||||
Some(min_load_idx)
|
Some(min_load_idx)
|
||||||
}
|
}
|
||||||
@@ -369,10 +359,8 @@ impl LoadBalancingPolicy for CacheAwarePolicy {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let selected_url = if match_rate > self.config.cache_threshold {
|
let selected_url = if match_rate > self.config.cache_threshold {
|
||||||
RouterMetrics::record_cache_hit();
|
|
||||||
matched_worker.to_string()
|
matched_worker.to_string()
|
||||||
} else {
|
} else {
|
||||||
RouterMetrics::record_cache_miss();
|
|
||||||
let min_load_idx = *healthy_indices
|
let min_load_idx = *healthy_indices
|
||||||
.iter()
|
.iter()
|
||||||
.min_by_key(|&&idx| workers[idx].load())?;
|
.min_by_key(|&&idx| workers[idx].load())?;
|
||||||
@@ -388,8 +376,6 @@ impl LoadBalancingPolicy for CacheAwarePolicy {
|
|||||||
|
|
||||||
// Increment processed counter
|
// Increment processed counter
|
||||||
workers[selected_idx].increment_processed();
|
workers[selected_idx].increment_processed();
|
||||||
RouterMetrics::record_processed_request(&selected_url);
|
|
||||||
RouterMetrics::record_policy_decision(self.name(), &selected_url);
|
|
||||||
|
|
||||||
return Some(selected_idx);
|
return Some(selected_idx);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use rand::Rng;
|
|||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use super::{get_healthy_worker_indices, LoadBalancingPolicy};
|
use super::{get_healthy_worker_indices, LoadBalancingPolicy};
|
||||||
use crate::{core::Worker, observability::metrics::RouterMetrics};
|
use crate::core::Worker;
|
||||||
|
|
||||||
/// Power-of-two choices policy
|
/// Power-of-two choices policy
|
||||||
///
|
///
|
||||||
@@ -100,8 +100,6 @@ impl LoadBalancingPolicy for PowerOfTwoPolicy {
|
|||||||
|
|
||||||
// Increment processed counter
|
// Increment processed counter
|
||||||
workers[selected_idx].increment_processed();
|
workers[selected_idx].increment_processed();
|
||||||
RouterMetrics::record_processed_request(workers[selected_idx].url());
|
|
||||||
RouterMetrics::record_policy_decision(self.name(), workers[selected_idx].url());
|
|
||||||
|
|
||||||
Some(selected_idx)
|
Some(selected_idx)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use std::sync::Arc;
|
|||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
|
|
||||||
use super::{get_healthy_worker_indices, LoadBalancingPolicy};
|
use super::{get_healthy_worker_indices, LoadBalancingPolicy};
|
||||||
use crate::{core::Worker, observability::metrics::RouterMetrics};
|
use crate::core::Worker;
|
||||||
|
|
||||||
/// Random selection policy
|
/// Random selection policy
|
||||||
///
|
///
|
||||||
@@ -33,10 +33,7 @@ impl LoadBalancingPolicy for RandomPolicy {
|
|||||||
|
|
||||||
let mut rng = rand::rng();
|
let mut rng = rand::rng();
|
||||||
let random_idx = rng.random_range(0..healthy_indices.len());
|
let random_idx = rng.random_range(0..healthy_indices.len());
|
||||||
let worker = workers[healthy_indices[random_idx]].url();
|
|
||||||
|
|
||||||
RouterMetrics::record_processed_request(worker);
|
|
||||||
RouterMetrics::record_policy_decision(self.name(), worker);
|
|
||||||
Some(healthy_indices[random_idx])
|
Some(healthy_indices[random_idx])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use std::sync::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use super::{get_healthy_worker_indices, LoadBalancingPolicy};
|
use super::{get_healthy_worker_indices, LoadBalancingPolicy};
|
||||||
use crate::{core::Worker, observability::metrics::RouterMetrics};
|
use crate::core::Worker;
|
||||||
|
|
||||||
/// Round-robin selection policy
|
/// Round-robin selection policy
|
||||||
///
|
///
|
||||||
@@ -39,10 +39,7 @@ impl LoadBalancingPolicy for RoundRobinPolicy {
|
|||||||
// Get and increment counter atomically
|
// Get and increment counter atomically
|
||||||
let count = self.counter.fetch_add(1, Ordering::Relaxed);
|
let count = self.counter.fetch_add(1, Ordering::Relaxed);
|
||||||
let selected_idx = count % healthy_indices.len();
|
let selected_idx = count % healthy_indices.len();
|
||||||
let worker = workers[healthy_indices[selected_idx]].url();
|
|
||||||
|
|
||||||
RouterMetrics::record_processed_request(worker);
|
|
||||||
RouterMetrics::record_policy_decision(self.name(), worker);
|
|
||||||
Some(healthy_indices[selected_idx])
|
Some(healthy_indices[selected_idx])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ use tracing::{error, warn};
|
|||||||
use super::PipelineStage;
|
use super::PipelineStage;
|
||||||
use crate::{
|
use crate::{
|
||||||
core::{ConnectionMode, Worker, WorkerRegistry, WorkerType},
|
core::{ConnectionMode, Worker, WorkerRegistry, WorkerType},
|
||||||
observability::metrics::{smg_labels, SmgMetrics},
|
observability::metrics::{metrics_labels, Metrics},
|
||||||
policies::PolicyRegistry,
|
policies::PolicyRegistry,
|
||||||
routers::{
|
routers::{
|
||||||
error,
|
error,
|
||||||
@@ -150,9 +150,9 @@ impl WorkerSelectionStage {
|
|||||||
let selected = available[idx].clone();
|
let selected = available[idx].clone();
|
||||||
|
|
||||||
// Record worker selection metric
|
// Record worker selection metric
|
||||||
SmgMetrics::record_worker_selection(
|
Metrics::record_worker_selection(
|
||||||
smg_labels::WORKER_REGULAR,
|
metrics_labels::WORKER_REGULAR,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
model_id.unwrap_or("default"),
|
model_id.unwrap_or("default"),
|
||||||
policy.name(),
|
policy.name(),
|
||||||
);
|
);
|
||||||
@@ -210,15 +210,15 @@ impl WorkerSelectionStage {
|
|||||||
let policy_name = policy.name();
|
let policy_name = policy.name();
|
||||||
|
|
||||||
// Record worker selection metrics for both prefill and decode
|
// Record worker selection metrics for both prefill and decode
|
||||||
SmgMetrics::record_worker_selection(
|
Metrics::record_worker_selection(
|
||||||
smg_labels::WORKER_PREFILL,
|
metrics_labels::WORKER_PREFILL,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
model,
|
model,
|
||||||
policy_name,
|
policy_name,
|
||||||
);
|
);
|
||||||
SmgMetrics::record_worker_selection(
|
Metrics::record_worker_selection(
|
||||||
smg_labels::WORKER_DECODE,
|
metrics_labels::WORKER_DECODE,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
model,
|
model,
|
||||||
policy_name,
|
policy_name,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ use uuid::Uuid;
|
|||||||
use crate::{
|
use crate::{
|
||||||
data_connector::{ConversationItemStorage, ConversationStorage, ResponseId, ResponseStorage},
|
data_connector::{ConversationItemStorage, ConversationStorage, ResponseId, ResponseStorage},
|
||||||
mcp::{self, McpManager},
|
mcp::{self, McpManager},
|
||||||
observability::metrics::{smg_labels, SmgMetrics},
|
observability::metrics::{metrics_labels, Metrics},
|
||||||
protocols::{
|
protocols::{
|
||||||
common::{Function, ToolCall, ToolChoice, ToolChoiceValue, Usage},
|
common::{Function, ToolCall, ToolChoice, ToolChoiceValue, Usage},
|
||||||
responses::{
|
responses::{
|
||||||
@@ -326,7 +326,7 @@ async fn execute_with_mcp_loop(
|
|||||||
iteration_count += 1;
|
iteration_count += 1;
|
||||||
|
|
||||||
// Record tool loop iteration metric
|
// Record tool loop iteration metric
|
||||||
SmgMetrics::record_mcp_tool_iteration(¤t_request.model);
|
Metrics::record_mcp_tool_iteration(¤t_request.model);
|
||||||
|
|
||||||
// Safety check: prevent infinite loops
|
// Safety check: prevent infinite loops
|
||||||
if iteration_count > MAX_TOOL_ITERATIONS {
|
if iteration_count > MAX_TOOL_ITERATIONS {
|
||||||
@@ -770,7 +770,7 @@ async fn execute_mcp_tool_loop_streaming(
|
|||||||
iteration_count += 1;
|
iteration_count += 1;
|
||||||
|
|
||||||
// Record tool loop iteration metric
|
// Record tool loop iteration metric
|
||||||
SmgMetrics::record_mcp_tool_iteration(¤t_request.model);
|
Metrics::record_mcp_tool_iteration(¤t_request.model);
|
||||||
|
|
||||||
// Safety check: prevent infinite loops
|
// Safety check: prevent infinite loops
|
||||||
if iteration_count > MAX_TOOL_ITERATIONS {
|
if iteration_count > MAX_TOOL_ITERATIONS {
|
||||||
@@ -1253,18 +1253,18 @@ async fn execute_mcp_tools(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Record MCP tool metrics
|
// Record MCP tool metrics
|
||||||
SmgMetrics::record_mcp_tool_duration(
|
Metrics::record_mcp_tool_duration(
|
||||||
model_id,
|
model_id,
|
||||||
&tool_call.function.name,
|
&tool_call.function.name,
|
||||||
tool_duration,
|
tool_duration,
|
||||||
);
|
);
|
||||||
SmgMetrics::record_mcp_tool_call(
|
Metrics::record_mcp_tool_call(
|
||||||
model_id,
|
model_id,
|
||||||
&tool_call.function.name,
|
&tool_call.function.name,
|
||||||
if is_error {
|
if is_error {
|
||||||
smg_labels::RESULT_ERROR
|
metrics_labels::RESULT_ERROR
|
||||||
} else {
|
} else {
|
||||||
smg_labels::RESULT_SUCCESS
|
metrics_labels::RESULT_SUCCESS
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -1301,15 +1301,15 @@ async fn execute_mcp_tools(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Record MCP tool metrics
|
// Record MCP tool metrics
|
||||||
SmgMetrics::record_mcp_tool_duration(
|
Metrics::record_mcp_tool_duration(
|
||||||
model_id,
|
model_id,
|
||||||
&tool_call.function.name,
|
&tool_call.function.name,
|
||||||
tool_duration,
|
tool_duration,
|
||||||
);
|
);
|
||||||
SmgMetrics::record_mcp_tool_call(
|
Metrics::record_mcp_tool_call(
|
||||||
model_id,
|
model_id,
|
||||||
&tool_call.function.name,
|
&tool_call.function.name,
|
||||||
smg_labels::RESULT_ERROR,
|
metrics_labels::RESULT_ERROR,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Return error result to model (let it handle gracefully)
|
// Return error result to model (let it handle gracefully)
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
grpc_client::sglang_proto::generate_complete::MatchedStop::{MatchedStopStr, MatchedTokenId},
|
grpc_client::sglang_proto::generate_complete::MatchedStop::{MatchedStopStr, MatchedTokenId},
|
||||||
observability::metrics::{smg_labels, SmgMetrics, StreamingMetricsParams},
|
observability::metrics::{metrics_labels, Metrics, StreamingMetricsParams},
|
||||||
protocols::{
|
protocols::{
|
||||||
chat::{
|
chat::{
|
||||||
ChatCompletionRequest, ChatCompletionStreamResponse, ChatMessageDelta, ChatStreamChoice,
|
ChatCompletionRequest, ChatCompletionStreamResponse, ChatMessageDelta, ChatStreamChoice,
|
||||||
@@ -315,11 +315,11 @@ impl HarmonyStreamingProcessor {
|
|||||||
grpc_stream.mark_completed();
|
grpc_stream.mark_completed();
|
||||||
|
|
||||||
// Record streaming metrics
|
// Record streaming metrics
|
||||||
SmgMetrics::record_streaming_metrics(StreamingMetricsParams {
|
Metrics::record_streaming_metrics(StreamingMetricsParams {
|
||||||
router_type: smg_labels::ROUTER_GRPC,
|
router_type: metrics_labels::ROUTER_GRPC,
|
||||||
backend_type: smg_labels::BACKEND_HARMONY,
|
backend_type: metrics_labels::BACKEND_HARMONY,
|
||||||
model_id: &original_request.model,
|
model_id: &original_request.model,
|
||||||
endpoint: smg_labels::ENDPOINT_CHAT,
|
endpoint: metrics_labels::ENDPOINT_CHAT,
|
||||||
ttft: first_token_time.map(|t| t.duration_since(start_time)),
|
ttft: first_token_time.map(|t| t.duration_since(start_time)),
|
||||||
generation_duration: start_time.elapsed(),
|
generation_duration: start_time.elapsed(),
|
||||||
input_tokens: Some(total_prompt as u64),
|
input_tokens: Some(total_prompt as u64),
|
||||||
@@ -474,11 +474,11 @@ impl HarmonyStreamingProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Record streaming metrics
|
// Record streaming metrics
|
||||||
SmgMetrics::record_streaming_metrics(StreamingMetricsParams {
|
Metrics::record_streaming_metrics(StreamingMetricsParams {
|
||||||
router_type: smg_labels::ROUTER_GRPC,
|
router_type: metrics_labels::ROUTER_GRPC,
|
||||||
backend_type: smg_labels::BACKEND_HARMONY,
|
backend_type: metrics_labels::BACKEND_HARMONY,
|
||||||
model_id: &original_request.model,
|
model_id: &original_request.model,
|
||||||
endpoint: smg_labels::ENDPOINT_CHAT,
|
endpoint: metrics_labels::ENDPOINT_CHAT,
|
||||||
ttft: first_token_time.map(|t| t.duration_since(start_time)),
|
ttft: first_token_time.map(|t| t.duration_since(start_time)),
|
||||||
generation_duration: start_time.elapsed(),
|
generation_duration: start_time.elapsed(),
|
||||||
input_tokens: Some(total_prompt as u64),
|
input_tokens: Some(total_prompt as u64),
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ use super::{
|
|||||||
};
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
core::WorkerRegistry,
|
core::WorkerRegistry,
|
||||||
observability::metrics::{smg_labels, SmgMetrics},
|
observability::metrics::{metrics_labels, Metrics},
|
||||||
policies::PolicyRegistry,
|
policies::PolicyRegistry,
|
||||||
protocols::{
|
protocols::{
|
||||||
chat::{ChatCompletionRequest, ChatCompletionResponse},
|
chat::{ChatCompletionRequest, ChatCompletionResponse},
|
||||||
@@ -65,7 +65,7 @@ impl RequestPipeline {
|
|||||||
reasoning_parser_factory,
|
reasoning_parser_factory,
|
||||||
configured_tool_parser,
|
configured_tool_parser,
|
||||||
configured_reasoning_parser,
|
configured_reasoning_parser,
|
||||||
smg_labels::BACKEND_REGULAR,
|
metrics_labels::BACKEND_REGULAR,
|
||||||
));
|
));
|
||||||
|
|
||||||
let stages: Vec<Box<dyn PipelineStage>> = vec![
|
let stages: Vec<Box<dyn PipelineStage>> = vec![
|
||||||
@@ -84,7 +84,7 @@ impl RequestPipeline {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
stages: Arc::new(stages),
|
stages: Arc::new(stages),
|
||||||
backend_type: smg_labels::BACKEND_REGULAR,
|
backend_type: metrics_labels::BACKEND_REGULAR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +114,7 @@ impl RequestPipeline {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
stages: Arc::new(stages),
|
stages: Arc::new(stages),
|
||||||
backend_type: smg_labels::BACKEND_REGULAR,
|
backend_type: metrics_labels::BACKEND_REGULAR,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,7 +144,7 @@ impl RequestPipeline {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
stages: Arc::new(stages),
|
stages: Arc::new(stages),
|
||||||
backend_type: smg_labels::BACKEND_PD,
|
backend_type: metrics_labels::BACKEND_PD,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,7 +172,7 @@ impl RequestPipeline {
|
|||||||
reasoning_parser_factory,
|
reasoning_parser_factory,
|
||||||
configured_tool_parser,
|
configured_tool_parser,
|
||||||
configured_reasoning_parser,
|
configured_reasoning_parser,
|
||||||
smg_labels::BACKEND_PD,
|
metrics_labels::BACKEND_PD,
|
||||||
));
|
));
|
||||||
|
|
||||||
let stages: Vec<Box<dyn PipelineStage>> = vec![
|
let stages: Vec<Box<dyn PipelineStage>> = vec![
|
||||||
@@ -191,7 +191,7 @@ impl RequestPipeline {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
stages: Arc::new(stages),
|
stages: Arc::new(stages),
|
||||||
backend_type: smg_labels::BACKEND_PD,
|
backend_type: metrics_labels::BACKEND_PD,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,12 +209,12 @@ impl RequestPipeline {
|
|||||||
let streaming = request.stream;
|
let streaming = request.stream;
|
||||||
|
|
||||||
// Record request start
|
// Record request start
|
||||||
SmgMetrics::record_router_request(
|
Metrics::record_router_request(
|
||||||
smg_labels::ROUTER_GRPC,
|
metrics_labels::ROUTER_GRPC,
|
||||||
self.backend_type,
|
self.backend_type,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
&request_for_metrics.model,
|
&request_for_metrics.model,
|
||||||
smg_labels::ENDPOINT_CHAT,
|
metrics_labels::ENDPOINT_CHAT,
|
||||||
streaming,
|
streaming,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -224,24 +224,24 @@ impl RequestPipeline {
|
|||||||
match stage.execute(&mut ctx).await {
|
match stage.execute(&mut ctx).await {
|
||||||
Ok(Some(response)) => {
|
Ok(Some(response)) => {
|
||||||
// Stage completed with streaming response - record success and return
|
// Stage completed with streaming response - record success and return
|
||||||
SmgMetrics::record_router_duration(
|
Metrics::record_router_duration(
|
||||||
smg_labels::ROUTER_GRPC,
|
metrics_labels::ROUTER_GRPC,
|
||||||
self.backend_type,
|
self.backend_type,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
&request_for_metrics.model,
|
&request_for_metrics.model,
|
||||||
smg_labels::ENDPOINT_CHAT,
|
metrics_labels::ENDPOINT_CHAT,
|
||||||
start.elapsed(),
|
start.elapsed(),
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
Ok(None) => continue,
|
Ok(None) => continue,
|
||||||
Err(response) => {
|
Err(response) => {
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_GRPC,
|
metrics_labels::ROUTER_GRPC,
|
||||||
self.backend_type,
|
self.backend_type,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
&request_for_metrics.model,
|
&request_for_metrics.model,
|
||||||
smg_labels::ENDPOINT_CHAT,
|
metrics_labels::ENDPOINT_CHAT,
|
||||||
error_type_from_status(response.status()),
|
error_type_from_status(response.status()),
|
||||||
);
|
);
|
||||||
error!(
|
error!(
|
||||||
@@ -256,12 +256,12 @@ impl RequestPipeline {
|
|||||||
|
|
||||||
match ctx.state.response.final_response {
|
match ctx.state.response.final_response {
|
||||||
Some(FinalResponse::Chat(response)) => {
|
Some(FinalResponse::Chat(response)) => {
|
||||||
SmgMetrics::record_router_duration(
|
Metrics::record_router_duration(
|
||||||
smg_labels::ROUTER_GRPC,
|
metrics_labels::ROUTER_GRPC,
|
||||||
self.backend_type,
|
self.backend_type,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
&request_for_metrics.model,
|
&request_for_metrics.model,
|
||||||
smg_labels::ENDPOINT_CHAT,
|
metrics_labels::ENDPOINT_CHAT,
|
||||||
start.elapsed(),
|
start.elapsed(),
|
||||||
);
|
);
|
||||||
axum::Json(response).into_response()
|
axum::Json(response).into_response()
|
||||||
@@ -271,13 +271,13 @@ impl RequestPipeline {
|
|||||||
function = "execute_chat",
|
function = "execute_chat",
|
||||||
"Wrong response type: expected Chat, got Generate"
|
"Wrong response type: expected Chat, got Generate"
|
||||||
);
|
);
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_GRPC,
|
metrics_labels::ROUTER_GRPC,
|
||||||
self.backend_type,
|
self.backend_type,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
&request_for_metrics.model,
|
&request_for_metrics.model,
|
||||||
smg_labels::ENDPOINT_CHAT,
|
metrics_labels::ENDPOINT_CHAT,
|
||||||
smg_labels::ERROR_INTERNAL,
|
metrics_labels::ERROR_INTERNAL,
|
||||||
);
|
);
|
||||||
error::internal_error("wrong_response_type", "Internal error: wrong response type")
|
error::internal_error("wrong_response_type", "Internal error: wrong response type")
|
||||||
}
|
}
|
||||||
@@ -286,13 +286,13 @@ impl RequestPipeline {
|
|||||||
function = "execute_chat",
|
function = "execute_chat",
|
||||||
"No response produced by pipeline"
|
"No response produced by pipeline"
|
||||||
);
|
);
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_GRPC,
|
metrics_labels::ROUTER_GRPC,
|
||||||
self.backend_type,
|
self.backend_type,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
&request_for_metrics.model,
|
&request_for_metrics.model,
|
||||||
smg_labels::ENDPOINT_CHAT,
|
metrics_labels::ENDPOINT_CHAT,
|
||||||
smg_labels::ERROR_INTERNAL,
|
metrics_labels::ERROR_INTERNAL,
|
||||||
);
|
);
|
||||||
error::internal_error("no_response_produced", "No response produced")
|
error::internal_error("no_response_produced", "No response produced")
|
||||||
}
|
}
|
||||||
@@ -314,12 +314,12 @@ impl RequestPipeline {
|
|||||||
let streaming = request.stream;
|
let streaming = request.stream;
|
||||||
|
|
||||||
// Record request start
|
// Record request start
|
||||||
SmgMetrics::record_router_request(
|
Metrics::record_router_request(
|
||||||
smg_labels::ROUTER_GRPC,
|
metrics_labels::ROUTER_GRPC,
|
||||||
self.backend_type,
|
self.backend_type,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
model_for_metrics.as_deref().unwrap_or("unknown"),
|
model_for_metrics.as_deref().unwrap_or("unknown"),
|
||||||
smg_labels::ENDPOINT_GENERATE,
|
metrics_labels::ENDPOINT_GENERATE,
|
||||||
streaming,
|
streaming,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -328,24 +328,24 @@ impl RequestPipeline {
|
|||||||
for stage in self.stages.iter() {
|
for stage in self.stages.iter() {
|
||||||
match stage.execute(&mut ctx).await {
|
match stage.execute(&mut ctx).await {
|
||||||
Ok(Some(response)) => {
|
Ok(Some(response)) => {
|
||||||
SmgMetrics::record_router_duration(
|
Metrics::record_router_duration(
|
||||||
smg_labels::ROUTER_GRPC,
|
metrics_labels::ROUTER_GRPC,
|
||||||
self.backend_type,
|
self.backend_type,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
model_for_metrics.as_deref().unwrap_or("unknown"),
|
model_for_metrics.as_deref().unwrap_or("unknown"),
|
||||||
smg_labels::ENDPOINT_GENERATE,
|
metrics_labels::ENDPOINT_GENERATE,
|
||||||
start.elapsed(),
|
start.elapsed(),
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
Ok(None) => continue,
|
Ok(None) => continue,
|
||||||
Err(response) => {
|
Err(response) => {
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_GRPC,
|
metrics_labels::ROUTER_GRPC,
|
||||||
self.backend_type,
|
self.backend_type,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
model_for_metrics.as_deref().unwrap_or("unknown"),
|
model_for_metrics.as_deref().unwrap_or("unknown"),
|
||||||
smg_labels::ENDPOINT_GENERATE,
|
metrics_labels::ENDPOINT_GENERATE,
|
||||||
error_type_from_status(response.status()),
|
error_type_from_status(response.status()),
|
||||||
);
|
);
|
||||||
error!(
|
error!(
|
||||||
@@ -360,12 +360,12 @@ impl RequestPipeline {
|
|||||||
|
|
||||||
match ctx.state.response.final_response {
|
match ctx.state.response.final_response {
|
||||||
Some(FinalResponse::Generate(response)) => {
|
Some(FinalResponse::Generate(response)) => {
|
||||||
SmgMetrics::record_router_duration(
|
Metrics::record_router_duration(
|
||||||
smg_labels::ROUTER_GRPC,
|
metrics_labels::ROUTER_GRPC,
|
||||||
self.backend_type,
|
self.backend_type,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
model_for_metrics.as_deref().unwrap_or("unknown"),
|
model_for_metrics.as_deref().unwrap_or("unknown"),
|
||||||
smg_labels::ENDPOINT_GENERATE,
|
metrics_labels::ENDPOINT_GENERATE,
|
||||||
start.elapsed(),
|
start.elapsed(),
|
||||||
);
|
);
|
||||||
axum::Json(response).into_response()
|
axum::Json(response).into_response()
|
||||||
@@ -375,13 +375,13 @@ impl RequestPipeline {
|
|||||||
function = "execute_generate",
|
function = "execute_generate",
|
||||||
"Wrong response type: expected Generate, got Chat"
|
"Wrong response type: expected Generate, got Chat"
|
||||||
);
|
);
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_GRPC,
|
metrics_labels::ROUTER_GRPC,
|
||||||
self.backend_type,
|
self.backend_type,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
model_for_metrics.as_deref().unwrap_or("unknown"),
|
model_for_metrics.as_deref().unwrap_or("unknown"),
|
||||||
smg_labels::ENDPOINT_GENERATE,
|
metrics_labels::ENDPOINT_GENERATE,
|
||||||
smg_labels::ERROR_INTERNAL,
|
metrics_labels::ERROR_INTERNAL,
|
||||||
);
|
);
|
||||||
error::internal_error("wrong_response_type", "Internal error: wrong response type")
|
error::internal_error("wrong_response_type", "Internal error: wrong response type")
|
||||||
}
|
}
|
||||||
@@ -390,13 +390,13 @@ impl RequestPipeline {
|
|||||||
function = "execute_generate",
|
function = "execute_generate",
|
||||||
"No response produced by pipeline"
|
"No response produced by pipeline"
|
||||||
);
|
);
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_GRPC,
|
metrics_labels::ROUTER_GRPC,
|
||||||
self.backend_type,
|
self.backend_type,
|
||||||
smg_labels::CONNECTION_GRPC,
|
metrics_labels::CONNECTION_GRPC,
|
||||||
model_for_metrics.as_deref().unwrap_or("unknown"),
|
model_for_metrics.as_deref().unwrap_or("unknown"),
|
||||||
smg_labels::ENDPOINT_GENERATE,
|
metrics_labels::ENDPOINT_GENERATE,
|
||||||
smg_labels::ERROR_INTERNAL,
|
metrics_labels::ERROR_INTERNAL,
|
||||||
);
|
);
|
||||||
error::internal_error("no_response_produced", "No response produced")
|
error::internal_error("no_response_produced", "No response produced")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use uuid::Uuid;
|
|||||||
use super::conversions;
|
use super::conversions;
|
||||||
use crate::{
|
use crate::{
|
||||||
mcp::{self, McpManager},
|
mcp::{self, McpManager},
|
||||||
observability::metrics::{smg_labels, SmgMetrics},
|
observability::metrics::{metrics_labels, Metrics},
|
||||||
protocols::{
|
protocols::{
|
||||||
chat::{
|
chat::{
|
||||||
ChatChoice, ChatCompletionMessage, ChatCompletionRequest, ChatCompletionResponse,
|
ChatChoice, ChatCompletionMessage, ChatCompletionRequest, ChatCompletionResponse,
|
||||||
@@ -285,7 +285,7 @@ pub(super) async fn execute_tool_loop(
|
|||||||
state.iteration += 1;
|
state.iteration += 1;
|
||||||
|
|
||||||
// Record tool loop iteration metric
|
// Record tool loop iteration metric
|
||||||
SmgMetrics::record_mcp_tool_iteration(¤t_request.model);
|
Metrics::record_mcp_tool_iteration(¤t_request.model);
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"Tool loop iteration {}: found {} tool call(s)",
|
"Tool loop iteration {}: found {} tool call(s)",
|
||||||
@@ -408,18 +408,18 @@ pub(super) async fn execute_tool_loop(
|
|||||||
let tool_duration = tool_start.elapsed();
|
let tool_duration = tool_start.elapsed();
|
||||||
|
|
||||||
// Record MCP tool metrics
|
// Record MCP tool metrics
|
||||||
SmgMetrics::record_mcp_tool_duration(
|
Metrics::record_mcp_tool_duration(
|
||||||
¤t_request.model,
|
¤t_request.model,
|
||||||
&tool_name,
|
&tool_name,
|
||||||
tool_duration,
|
tool_duration,
|
||||||
);
|
);
|
||||||
SmgMetrics::record_mcp_tool_call(
|
Metrics::record_mcp_tool_call(
|
||||||
¤t_request.model,
|
¤t_request.model,
|
||||||
&tool_name,
|
&tool_name,
|
||||||
if success {
|
if success {
|
||||||
smg_labels::RESULT_SUCCESS
|
metrics_labels::RESULT_SUCCESS
|
||||||
} else {
|
} else {
|
||||||
smg_labels::RESULT_ERROR
|
metrics_labels::RESULT_ERROR
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -665,7 +665,7 @@ async fn execute_tool_loop_streaming_internal(
|
|||||||
state.iteration += 1;
|
state.iteration += 1;
|
||||||
|
|
||||||
// Record tool loop iteration metric
|
// Record tool loop iteration metric
|
||||||
SmgMetrics::record_mcp_tool_iteration(&model);
|
Metrics::record_mcp_tool_iteration(&model);
|
||||||
|
|
||||||
if state.iteration > MAX_ITERATIONS {
|
if state.iteration > MAX_ITERATIONS {
|
||||||
return Err(format!(
|
return Err(format!(
|
||||||
@@ -928,14 +928,14 @@ async fn execute_tool_loop_streaming_internal(
|
|||||||
let tool_duration = tool_start.elapsed();
|
let tool_duration = tool_start.elapsed();
|
||||||
|
|
||||||
// Record MCP tool metrics
|
// Record MCP tool metrics
|
||||||
SmgMetrics::record_mcp_tool_duration(&model, &tool_name, tool_duration);
|
Metrics::record_mcp_tool_duration(&model, &tool_name, tool_duration);
|
||||||
SmgMetrics::record_mcp_tool_call(
|
Metrics::record_mcp_tool_call(
|
||||||
&model,
|
&model,
|
||||||
&tool_name,
|
&tool_name,
|
||||||
if success {
|
if success {
|
||||||
smg_labels::RESULT_SUCCESS
|
metrics_labels::RESULT_SUCCESS
|
||||||
} else {
|
} else {
|
||||||
smg_labels::RESULT_ERROR
|
metrics_labels::RESULT_ERROR
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ use tracing::{debug, error, warn};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
grpc_client::sglang_proto::generate_complete::MatchedStop::{MatchedStopStr, MatchedTokenId},
|
grpc_client::sglang_proto::generate_complete::MatchedStop::{MatchedStopStr, MatchedTokenId},
|
||||||
observability::metrics::{smg_labels, SmgMetrics, StreamingMetricsParams},
|
observability::metrics::{metrics_labels, Metrics, StreamingMetricsParams},
|
||||||
protocols::{
|
protocols::{
|
||||||
chat::{ChatCompletionRequest, ChatCompletionStreamResponse},
|
chat::{ChatCompletionRequest, ChatCompletionStreamResponse},
|
||||||
common::{
|
common::{
|
||||||
@@ -573,11 +573,11 @@ impl StreamingProcessor {
|
|||||||
// Record streaming metrics
|
// Record streaming metrics
|
||||||
let total_prompt: u32 = prompt_tokens.values().sum();
|
let total_prompt: u32 = prompt_tokens.values().sum();
|
||||||
let total_completion: u32 = completion_tokens.values().sum();
|
let total_completion: u32 = completion_tokens.values().sum();
|
||||||
SmgMetrics::record_streaming_metrics(StreamingMetricsParams {
|
Metrics::record_streaming_metrics(StreamingMetricsParams {
|
||||||
router_type: smg_labels::ROUTER_GRPC,
|
router_type: metrics_labels::ROUTER_GRPC,
|
||||||
backend_type: self.backend_type,
|
backend_type: self.backend_type,
|
||||||
model_id: model,
|
model_id: model,
|
||||||
endpoint: smg_labels::ENDPOINT_CHAT,
|
endpoint: metrics_labels::ENDPOINT_CHAT,
|
||||||
ttft: first_token_time.map(|t| t.duration_since(start_time)),
|
ttft: first_token_time.map(|t| t.duration_since(start_time)),
|
||||||
generation_duration: start_time.elapsed(),
|
generation_duration: start_time.elapsed(),
|
||||||
input_tokens: Some(total_prompt as u64),
|
input_tokens: Some(total_prompt as u64),
|
||||||
@@ -1015,11 +1015,11 @@ impl StreamingProcessor {
|
|||||||
total_completion: u32,
|
total_completion: u32,
|
||||||
ctx: &GenerateStreamContext,
|
ctx: &GenerateStreamContext,
|
||||||
) {
|
) {
|
||||||
SmgMetrics::record_streaming_metrics(StreamingMetricsParams {
|
Metrics::record_streaming_metrics(StreamingMetricsParams {
|
||||||
router_type: smg_labels::ROUTER_GRPC,
|
router_type: metrics_labels::ROUTER_GRPC,
|
||||||
backend_type: ctx.backend_type,
|
backend_type: ctx.backend_type,
|
||||||
model_id: &ctx.model,
|
model_id: &ctx.model,
|
||||||
endpoint: smg_labels::ENDPOINT_GENERATE,
|
endpoint: metrics_labels::ENDPOINT_GENERATE,
|
||||||
ttft: first_token_time.map(|t| t.duration_since(start_time)),
|
ttft: first_token_time.map(|t| t.duration_since(start_time)),
|
||||||
generation_duration: start_time.elapsed(),
|
generation_duration: start_time.elapsed(),
|
||||||
input_tokens: None, // generate endpoint doesn't expose prompt tokens in streaming
|
input_tokens: None, // generate endpoint doesn't expose prompt tokens in streaming
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ use super::{
|
|||||||
use crate::{
|
use crate::{
|
||||||
core::Worker,
|
core::Worker,
|
||||||
grpc_client::sglang_proto::{InputLogProbs, OutputLogProbs},
|
grpc_client::sglang_proto::{InputLogProbs, OutputLogProbs},
|
||||||
observability::metrics::smg_labels,
|
observability::metrics::metrics_labels,
|
||||||
protocols::{
|
protocols::{
|
||||||
chat::{ChatCompletionRequest, ChatMessage},
|
chat::{ChatCompletionRequest, ChatMessage},
|
||||||
common::{
|
common::{
|
||||||
@@ -966,11 +966,11 @@ pub fn parse_finish_reason(reason_str: &str, completion_tokens: i32) -> Generate
|
|||||||
/// Map route path to endpoint label for metrics
|
/// Map route path to endpoint label for metrics
|
||||||
pub fn route_to_endpoint(route: &str) -> &'static str {
|
pub fn route_to_endpoint(route: &str) -> &'static str {
|
||||||
match route {
|
match route {
|
||||||
"/v1/chat/completions" => smg_labels::ENDPOINT_CHAT,
|
"/v1/chat/completions" => metrics_labels::ENDPOINT_CHAT,
|
||||||
"/generate" => smg_labels::ENDPOINT_GENERATE,
|
"/generate" => metrics_labels::ENDPOINT_GENERATE,
|
||||||
"/v1/completions" => smg_labels::ENDPOINT_COMPLETIONS,
|
"/v1/completions" => metrics_labels::ENDPOINT_COMPLETIONS,
|
||||||
"/v1/rerank" => smg_labels::ENDPOINT_RERANK,
|
"/v1/rerank" => metrics_labels::ENDPOINT_RERANK,
|
||||||
"/v1/responses" => smg_labels::ENDPOINT_RESPONSES,
|
"/v1/responses" => metrics_labels::ENDPOINT_RESPONSES,
|
||||||
_ => "other",
|
_ => "other",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -978,11 +978,11 @@ pub fn route_to_endpoint(route: &str) -> &'static str {
|
|||||||
/// Map HTTP status code to error type label for metrics
|
/// Map HTTP status code to error type label for metrics
|
||||||
pub fn error_type_from_status(status: StatusCode) -> &'static str {
|
pub fn error_type_from_status(status: StatusCode) -> &'static str {
|
||||||
match status.as_u16() {
|
match status.as_u16() {
|
||||||
400 => smg_labels::ERROR_VALIDATION,
|
400 => metrics_labels::ERROR_VALIDATION,
|
||||||
404 => smg_labels::ERROR_NO_WORKERS,
|
404 => metrics_labels::ERROR_NO_WORKERS,
|
||||||
408 | 504 => smg_labels::ERROR_TIMEOUT,
|
408 | 504 => metrics_labels::ERROR_TIMEOUT,
|
||||||
500..=599 => smg_labels::ERROR_BACKEND,
|
500..=599 => metrics_labels::ERROR_BACKEND,
|
||||||
_ => smg_labels::ERROR_INTERNAL,
|
_ => metrics_labels::ERROR_INTERNAL,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
observability::{
|
observability::{
|
||||||
events::{self, Event},
|
events::{self, Event},
|
||||||
metrics::{smg_labels, RouterMetrics, SmgMetrics},
|
metrics::{metrics_labels, Metrics},
|
||||||
otel_trace::inject_trace_context_http,
|
otel_trace::inject_trace_context_http,
|
||||||
},
|
},
|
||||||
policies::{LoadBalancingPolicy, PolicyRegistry},
|
policies::{LoadBalancingPolicy, PolicyRegistry},
|
||||||
@@ -165,7 +165,6 @@ impl PDRouter {
|
|||||||
|
|
||||||
fn handle_server_selection_error(error: String) -> Response {
|
fn handle_server_selection_error(error: String) -> Response {
|
||||||
error!("Failed to select PD pair error={}", error);
|
error!("Failed to select PD pair error={}", error);
|
||||||
RouterMetrics::record_pd_error("server_selection");
|
|
||||||
error::service_unavailable(
|
error::service_unavailable(
|
||||||
"server_selection_failed",
|
"server_selection_failed",
|
||||||
format!("No available servers: {}", error),
|
format!("No available servers: {}", error),
|
||||||
@@ -283,10 +282,10 @@ impl PDRouter {
|
|||||||
let endpoint = route_to_endpoint(route);
|
let endpoint = route_to_endpoint(route);
|
||||||
|
|
||||||
// Record request start (Layer 2)
|
// Record request start (Layer 2)
|
||||||
SmgMetrics::record_router_request(
|
Metrics::record_router_request(
|
||||||
smg_labels::ROUTER_HTTP,
|
metrics_labels::ROUTER_HTTP,
|
||||||
smg_labels::BACKEND_PD,
|
metrics_labels::BACKEND_PD,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
endpoint,
|
endpoint,
|
||||||
context.is_stream,
|
context.is_stream,
|
||||||
@@ -308,7 +307,6 @@ impl PDRouter {
|
|||||||
{
|
{
|
||||||
Ok(pair) => pair,
|
Ok(pair) => pair,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
RouterMetrics::record_pd_error("server_selection");
|
|
||||||
return Self::handle_server_selection_error(e);
|
return Self::handle_server_selection_error(e);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -353,14 +351,14 @@ impl PDRouter {
|
|||||||
// Record worker errors for server errors (5xx)
|
// Record worker errors for server errors (5xx)
|
||||||
if status.is_server_error() {
|
if status.is_server_error() {
|
||||||
let error_type = error_type_from_status(status);
|
let error_type = error_type_from_status(status);
|
||||||
SmgMetrics::record_worker_error(
|
Metrics::record_worker_error(
|
||||||
smg_labels::WORKER_PREFILL,
|
metrics_labels::WORKER_PREFILL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
error_type,
|
error_type,
|
||||||
);
|
);
|
||||||
SmgMetrics::record_worker_error(
|
Metrics::record_worker_error(
|
||||||
smg_labels::WORKER_DECODE,
|
metrics_labels::WORKER_DECODE,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
error_type,
|
error_type,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -371,17 +369,14 @@ impl PDRouter {
|
|||||||
},
|
},
|
||||||
|res, _attempt| is_retryable_status(res.status()),
|
|res, _attempt| is_retryable_status(res.status()),
|
||||||
|delay, attempt| {
|
|delay, attempt| {
|
||||||
RouterMetrics::record_retry(route);
|
|
||||||
RouterMetrics::record_retry_backoff_duration(delay, attempt);
|
|
||||||
// Layer 3 worker metrics (PD mode uses both prefill and decode workers)
|
// Layer 3 worker metrics (PD mode uses both prefill and decode workers)
|
||||||
SmgMetrics::record_worker_retry(smg_labels::WORKER_PREFILL, endpoint);
|
Metrics::record_worker_retry(metrics_labels::WORKER_PREFILL, endpoint);
|
||||||
SmgMetrics::record_worker_retry(smg_labels::WORKER_DECODE, endpoint);
|
Metrics::record_worker_retry(metrics_labels::WORKER_DECODE, endpoint);
|
||||||
SmgMetrics::record_worker_retry_backoff(attempt, delay);
|
Metrics::record_worker_retry_backoff(attempt, delay);
|
||||||
},
|
},
|
||||||
|| {
|
|| {
|
||||||
RouterMetrics::record_retries_exhausted(route);
|
Metrics::record_worker_retries_exhausted(metrics_labels::WORKER_PREFILL, endpoint);
|
||||||
SmgMetrics::record_worker_retries_exhausted(smg_labels::WORKER_PREFILL, endpoint);
|
Metrics::record_worker_retries_exhausted(metrics_labels::WORKER_DECODE, endpoint);
|
||||||
SmgMetrics::record_worker_retries_exhausted(smg_labels::WORKER_DECODE, endpoint);
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
@@ -389,19 +384,19 @@ impl PDRouter {
|
|||||||
// Record Layer 2 metrics
|
// Record Layer 2 metrics
|
||||||
let duration = start_time.elapsed();
|
let duration = start_time.elapsed();
|
||||||
if response.status().is_success() {
|
if response.status().is_success() {
|
||||||
SmgMetrics::record_router_duration(
|
Metrics::record_router_duration(
|
||||||
smg_labels::ROUTER_HTTP,
|
metrics_labels::ROUTER_HTTP,
|
||||||
smg_labels::BACKEND_PD,
|
metrics_labels::BACKEND_PD,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
endpoint,
|
endpoint,
|
||||||
duration,
|
duration,
|
||||||
);
|
);
|
||||||
} else if !is_retryable_status(response.status()) {
|
} else if !is_retryable_status(response.status()) {
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_HTTP,
|
metrics_labels::ROUTER_HTTP,
|
||||||
smg_labels::BACKEND_PD,
|
metrics_labels::BACKEND_PD,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
endpoint,
|
endpoint,
|
||||||
error_type_from_status(response.status()),
|
error_type_from_status(response.status()),
|
||||||
@@ -533,7 +528,7 @@ impl PDRouter {
|
|||||||
context: PDRequestContext<'_>,
|
context: PDRequestContext<'_>,
|
||||||
prefill: &dyn Worker,
|
prefill: &dyn Worker,
|
||||||
decode: &dyn Worker,
|
decode: &dyn Worker,
|
||||||
start_time: Instant,
|
_start_time: Instant,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
// For non-streaming: use guard for automatic load management
|
// For non-streaming: use guard for automatic load management
|
||||||
// For streaming: load will be managed in create_streaming_response
|
// For streaming: load will be managed in create_streaming_response
|
||||||
@@ -577,12 +572,6 @@ impl PDRouter {
|
|||||||
|
|
||||||
events::RequestReceivedEvent {}.emit();
|
events::RequestReceivedEvent {}.emit();
|
||||||
|
|
||||||
let duration = start_time.elapsed();
|
|
||||||
RouterMetrics::record_pd_request_duration(context.route, duration);
|
|
||||||
RouterMetrics::record_pd_request(context.route);
|
|
||||||
RouterMetrics::record_pd_prefill_request(prefill.url());
|
|
||||||
RouterMetrics::record_pd_decode_request(decode.url());
|
|
||||||
|
|
||||||
// Process decode response
|
// Process decode response
|
||||||
match decode_result {
|
match decode_result {
|
||||||
Ok(res) => {
|
Ok(res) => {
|
||||||
@@ -591,7 +580,6 @@ impl PDRouter {
|
|||||||
debug!("Decode response status: {}", status);
|
debug!("Decode response status: {}", status);
|
||||||
|
|
||||||
if !status.is_success() {
|
if !status.is_success() {
|
||||||
RouterMetrics::record_pd_decode_error(decode.url());
|
|
||||||
error!(
|
error!(
|
||||||
"Decode server returned error status decode_url={} status={}",
|
"Decode server returned error status decode_url={} status={}",
|
||||||
decode.url(),
|
decode.url(),
|
||||||
@@ -691,7 +679,6 @@ impl PDRouter {
|
|||||||
error = %e,
|
error = %e,
|
||||||
"Decode request failed"
|
"Decode request failed"
|
||||||
);
|
);
|
||||||
RouterMetrics::record_pd_decode_error(decode.url());
|
|
||||||
error::bad_gateway("decode_server_error", format!("Decode server error: {}", e))
|
error::bad_gateway("decode_server_error", format!("Decode server error: {}", e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -754,15 +741,15 @@ impl PDRouter {
|
|||||||
|
|
||||||
// Record worker selection metrics (Layer 3)
|
// Record worker selection metrics (Layer 3)
|
||||||
let model = model_id.unwrap_or("default");
|
let model = model_id.unwrap_or("default");
|
||||||
SmgMetrics::record_worker_selection(
|
Metrics::record_worker_selection(
|
||||||
smg_labels::WORKER_PREFILL,
|
metrics_labels::WORKER_PREFILL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
prefill_policy.name(),
|
prefill_policy.name(),
|
||||||
);
|
);
|
||||||
SmgMetrics::record_worker_selection(
|
Metrics::record_worker_selection(
|
||||||
smg_labels::WORKER_DECODE,
|
metrics_labels::WORKER_DECODE,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
decode_policy.name(),
|
decode_policy.name(),
|
||||||
);
|
);
|
||||||
@@ -862,7 +849,6 @@ impl PDRouter {
|
|||||||
Err(e) => {
|
Err(e) => {
|
||||||
if let Some(ref url) = decode_url {
|
if let Some(ref url) = decode_url {
|
||||||
error!("Stream error from decode server {}: {}", url, e);
|
error!("Stream error from decode server {}: {}", url, e);
|
||||||
RouterMetrics::record_pd_stream_error(url);
|
|
||||||
}
|
}
|
||||||
let _ = tx.send(Err(format!("Stream error: {}", e)));
|
let _ = tx.send(Err(format!("Stream error: {}", e)));
|
||||||
break;
|
break;
|
||||||
@@ -957,7 +943,6 @@ impl PDRouter {
|
|||||||
let prefill_response = match prefill_result {
|
let prefill_response = match prefill_result {
|
||||||
Ok(response) => response,
|
Ok(response) => response,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
RouterMetrics::record_pd_prefill_error(prefill_url);
|
|
||||||
error!(
|
error!(
|
||||||
"Prefill server failed (CRITICAL) prefill_url={} error={}. Decode will timeout without prefill KV cache.",
|
"Prefill server failed (CRITICAL) prefill_url={} error={}. Decode will timeout without prefill KV cache.",
|
||||||
prefill_url,
|
prefill_url,
|
||||||
@@ -980,8 +965,6 @@ impl PDRouter {
|
|||||||
|
|
||||||
// Check if prefill succeeded
|
// Check if prefill succeeded
|
||||||
if !prefill_status.is_success() {
|
if !prefill_status.is_success() {
|
||||||
RouterMetrics::record_pd_prefill_error(prefill_url);
|
|
||||||
|
|
||||||
// Get error body from prefill
|
// Get error body from prefill
|
||||||
let error_msg = prefill_response
|
let error_msg = prefill_response
|
||||||
.text()
|
.text()
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ use crate::{
|
|||||||
},
|
},
|
||||||
observability::{
|
observability::{
|
||||||
events::{self, Event},
|
events::{self, Event},
|
||||||
metrics::{smg_labels, RouterMetrics, SmgMetrics},
|
metrics::{metrics_labels, Metrics},
|
||||||
otel_trace::inject_trace_context_http,
|
otel_trace::inject_trace_context_http,
|
||||||
},
|
},
|
||||||
policies::PolicyRegistry,
|
policies::PolicyRegistry,
|
||||||
@@ -159,9 +159,9 @@ impl Router {
|
|||||||
let idx = policy.select_worker(&available, text)?;
|
let idx = policy.select_worker(&available, text)?;
|
||||||
|
|
||||||
// Record worker selection metric (Layer 3)
|
// Record worker selection metric (Layer 3)
|
||||||
SmgMetrics::record_worker_selection(
|
Metrics::record_worker_selection(
|
||||||
smg_labels::WORKER_REGULAR,
|
metrics_labels::WORKER_REGULAR,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model_id.unwrap_or("default"),
|
model_id.unwrap_or("default"),
|
||||||
policy.name(),
|
policy.name(),
|
||||||
);
|
);
|
||||||
@@ -183,10 +183,10 @@ impl Router {
|
|||||||
let endpoint = route_to_endpoint(route);
|
let endpoint = route_to_endpoint(route);
|
||||||
|
|
||||||
// Record request start (Layer 2)
|
// Record request start (Layer 2)
|
||||||
SmgMetrics::record_router_request(
|
Metrics::record_router_request(
|
||||||
smg_labels::ROUTER_HTTP,
|
metrics_labels::ROUTER_HTTP,
|
||||||
smg_labels::BACKEND_REGULAR,
|
metrics_labels::BACKEND_REGULAR,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
endpoint,
|
endpoint,
|
||||||
is_stream,
|
is_stream,
|
||||||
@@ -196,55 +196,39 @@ impl Router {
|
|||||||
&self.retry_config,
|
&self.retry_config,
|
||||||
// operation per attempt
|
// operation per attempt
|
||||||
|_: u32| async {
|
|_: u32| async {
|
||||||
let res = self
|
self.route_typed_request_once(headers, typed_req, route, model_id, is_stream, &text)
|
||||||
.route_typed_request_once(headers, typed_req, route, model_id, is_stream, &text)
|
.await
|
||||||
.await;
|
|
||||||
|
|
||||||
// Need to be outside `route_typed_request_once` because that function has multiple return paths
|
|
||||||
RouterMetrics::record_attempt_http_response(
|
|
||||||
route,
|
|
||||||
res.status().as_u16(),
|
|
||||||
extract_error_code_from_response(&res),
|
|
||||||
);
|
|
||||||
|
|
||||||
res
|
|
||||||
},
|
},
|
||||||
// should_retry predicate
|
// should_retry predicate
|
||||||
|res, _attempt| is_retryable_status(res.status()),
|
|res, _attempt| is_retryable_status(res.status()),
|
||||||
// on_backoff hook
|
// on_backoff hook
|
||||||
|delay, attempt| {
|
|delay, attempt| {
|
||||||
RouterMetrics::record_retry(route);
|
|
||||||
RouterMetrics::record_retry_backoff_duration(delay, attempt);
|
|
||||||
// Layer 3 worker metrics
|
// Layer 3 worker metrics
|
||||||
SmgMetrics::record_worker_retry(smg_labels::WORKER_REGULAR, endpoint);
|
Metrics::record_worker_retry(metrics_labels::WORKER_REGULAR, endpoint);
|
||||||
SmgMetrics::record_worker_retry_backoff(attempt, delay);
|
Metrics::record_worker_retry_backoff(attempt, delay);
|
||||||
},
|
},
|
||||||
// on_exhausted hook
|
// on_exhausted hook
|
||||||
|| {
|
|| {
|
||||||
RouterMetrics::record_retries_exhausted(route);
|
Metrics::record_worker_retries_exhausted(metrics_labels::WORKER_REGULAR, endpoint);
|
||||||
SmgMetrics::record_worker_retries_exhausted(smg_labels::WORKER_REGULAR, endpoint);
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
if response.status().is_success() {
|
if response.status().is_success() {
|
||||||
let duration = start.elapsed();
|
let duration = start.elapsed();
|
||||||
RouterMetrics::record_request(route);
|
Metrics::record_router_duration(
|
||||||
RouterMetrics::record_generate_duration(duration);
|
metrics_labels::ROUTER_HTTP,
|
||||||
SmgMetrics::record_router_duration(
|
metrics_labels::BACKEND_REGULAR,
|
||||||
smg_labels::ROUTER_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
smg_labels::BACKEND_REGULAR,
|
|
||||||
smg_labels::CONNECTION_HTTP,
|
|
||||||
model,
|
model,
|
||||||
endpoint,
|
endpoint,
|
||||||
duration,
|
duration,
|
||||||
);
|
);
|
||||||
} else if !is_retryable_status(response.status()) {
|
} else if !is_retryable_status(response.status()) {
|
||||||
RouterMetrics::record_request_error(route, "non_retryable_error");
|
Metrics::record_router_error(
|
||||||
SmgMetrics::record_router_error(
|
metrics_labels::ROUTER_HTTP,
|
||||||
smg_labels::ROUTER_HTTP,
|
metrics_labels::BACKEND_REGULAR,
|
||||||
smg_labels::BACKEND_REGULAR,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
smg_labels::CONNECTION_HTTP,
|
|
||||||
model,
|
model,
|
||||||
endpoint,
|
endpoint,
|
||||||
error_type_from_status(response.status()),
|
error_type_from_status(response.status()),
|
||||||
@@ -266,7 +250,6 @@ impl Router {
|
|||||||
let worker = match self.select_worker_for_model(model_id, Some(text)) {
|
let worker = match self.select_worker_for_model(model_id, Some(text)) {
|
||||||
Some(w) => w,
|
Some(w) => w,
|
||||||
None => {
|
None => {
|
||||||
RouterMetrics::record_request_error(route, "no_available_workers");
|
|
||||||
return error::service_unavailable(
|
return error::service_unavailable(
|
||||||
"no_available_workers",
|
"no_available_workers",
|
||||||
"No available workers (all circuits open or unhealthy)",
|
"No available workers (all circuits open or unhealthy)",
|
||||||
@@ -310,9 +293,9 @@ impl Router {
|
|||||||
|
|
||||||
// Record worker errors for server errors (5xx)
|
// Record worker errors for server errors (5xx)
|
||||||
if status.is_server_error() {
|
if status.is_server_error() {
|
||||||
SmgMetrics::record_worker_error(
|
Metrics::record_worker_error(
|
||||||
smg_labels::WORKER_REGULAR,
|
metrics_labels::WORKER_REGULAR,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
error_type_from_status(status),
|
error_type_from_status(status),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -687,8 +670,6 @@ fn convert_reqwest_error(e: reqwest::Error) -> Response {
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
use crate::routers::error::extract_error_code_from_response;
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl RouterTrait for Router {
|
impl RouterTrait for Router {
|
||||||
fn as_any(&self) -> &dyn std::any::Any {
|
fn as_any(&self) -> &dyn std::any::Any {
|
||||||
@@ -772,22 +753,8 @@ impl RouterTrait for Router {
|
|||||||
body: &EmbeddingRequest,
|
body: &EmbeddingRequest,
|
||||||
model_id: Option<&str>,
|
model_id: Option<&str>,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
// Record embeddings-specific metrics in addition to general request metrics
|
self.route_typed_request(headers, body, "/v1/embeddings", model_id)
|
||||||
let start = Instant::now();
|
.await
|
||||||
let res = self
|
|
||||||
.route_typed_request(headers, body, "/v1/embeddings", model_id)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Embedding specific metrics
|
|
||||||
if res.status().is_success() {
|
|
||||||
RouterMetrics::record_embeddings_request();
|
|
||||||
RouterMetrics::record_embeddings_duration(start.elapsed());
|
|
||||||
} else {
|
|
||||||
let error_type = format!("http_{}", res.status().as_u16());
|
|
||||||
RouterMetrics::record_embeddings_error(&error_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn route_classify(
|
async fn route_classify(
|
||||||
@@ -796,22 +763,8 @@ impl RouterTrait for Router {
|
|||||||
body: &ClassifyRequest,
|
body: &ClassifyRequest,
|
||||||
model_id: Option<&str>,
|
model_id: Option<&str>,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
// Record classification-specific metrics in addition to general request metrics
|
self.route_typed_request(headers, body, "/v1/classify", model_id)
|
||||||
let start = Instant::now();
|
.await
|
||||||
let res = self
|
|
||||||
.route_typed_request(headers, body, "/v1/classify", model_id)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
// Classification specific metrics
|
|
||||||
if res.status().is_success() {
|
|
||||||
RouterMetrics::record_classify_request();
|
|
||||||
RouterMetrics::record_classify_duration(start.elapsed());
|
|
||||||
} else {
|
|
||||||
let error_type = format!("http_{}", res.status().as_u16());
|
|
||||||
RouterMetrics::record_classify_error(&error_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
res
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn route_rerank(
|
async fn route_rerank(
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ use crate::{
|
|||||||
app_context::AppContext,
|
app_context::AppContext,
|
||||||
core::{model_type::Endpoint, ModelCard, ProviderType, RuntimeType, Worker, WorkerRegistry},
|
core::{model_type::Endpoint, ModelCard, ProviderType, RuntimeType, Worker, WorkerRegistry},
|
||||||
data_connector::{ConversationId, ListParams, ResponseId, SortOrder},
|
data_connector::{ConversationId, ListParams, ResponseId, SortOrder},
|
||||||
observability::metrics::{smg_labels, SmgMetrics},
|
observability::metrics::{metrics_labels, Metrics},
|
||||||
protocols::{
|
protocols::{
|
||||||
chat::ChatCompletionRequest,
|
chat::ChatCompletionRequest,
|
||||||
responses::{
|
responses::{
|
||||||
@@ -583,12 +583,12 @@ impl crate::routers::RouterTrait for OpenAIRouter {
|
|||||||
let streaming = body.stream;
|
let streaming = body.stream;
|
||||||
|
|
||||||
// Record request start
|
// Record request start
|
||||||
SmgMetrics::record_router_request(
|
Metrics::record_router_request(
|
||||||
smg_labels::ROUTER_OPENAI,
|
metrics_labels::ROUTER_OPENAI,
|
||||||
smg_labels::BACKEND_EXTERNAL,
|
metrics_labels::BACKEND_EXTERNAL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
smg_labels::ENDPOINT_CHAT,
|
metrics_labels::ENDPOINT_CHAT,
|
||||||
streaming,
|
streaming,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -600,13 +600,13 @@ impl crate::routers::RouterTrait for OpenAIRouter {
|
|||||||
{
|
{
|
||||||
Ok(w) => w,
|
Ok(w) => w,
|
||||||
Err(response) => {
|
Err(response) => {
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_OPENAI,
|
metrics_labels::ROUTER_OPENAI,
|
||||||
smg_labels::BACKEND_EXTERNAL,
|
metrics_labels::BACKEND_EXTERNAL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
smg_labels::ENDPOINT_CHAT,
|
metrics_labels::ENDPOINT_CHAT,
|
||||||
smg_labels::ERROR_NO_WORKERS,
|
metrics_labels::ERROR_NO_WORKERS,
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -615,13 +615,13 @@ impl crate::routers::RouterTrait for OpenAIRouter {
|
|||||||
let mut payload = match to_value(body) {
|
let mut payload = match to_value(body) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_OPENAI,
|
metrics_labels::ROUTER_OPENAI,
|
||||||
smg_labels::BACKEND_EXTERNAL,
|
metrics_labels::BACKEND_EXTERNAL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
smg_labels::ENDPOINT_CHAT,
|
metrics_labels::ENDPOINT_CHAT,
|
||||||
smg_labels::ERROR_VALIDATION,
|
metrics_labels::ERROR_VALIDATION,
|
||||||
);
|
);
|
||||||
return error_responses::bad_request(format!("Failed to serialize request: {}", e));
|
return error_responses::bad_request(format!("Failed to serialize request: {}", e));
|
||||||
}
|
}
|
||||||
@@ -629,13 +629,13 @@ impl crate::routers::RouterTrait for OpenAIRouter {
|
|||||||
|
|
||||||
let provider = self.get_provider_arc_for_worker(worker.as_ref(), model_id);
|
let provider = self.get_provider_arc_for_worker(worker.as_ref(), model_id);
|
||||||
if let Err(e) = provider.transform_request(&mut payload, Endpoint::Chat) {
|
if let Err(e) = provider.transform_request(&mut payload, Endpoint::Chat) {
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_OPENAI,
|
metrics_labels::ROUTER_OPENAI,
|
||||||
smg_labels::BACKEND_EXTERNAL,
|
metrics_labels::BACKEND_EXTERNAL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
smg_labels::ENDPOINT_CHAT,
|
metrics_labels::ENDPOINT_CHAT,
|
||||||
smg_labels::ERROR_VALIDATION,
|
metrics_labels::ERROR_VALIDATION,
|
||||||
);
|
);
|
||||||
return error_responses::bad_request(format!("Provider transform error: {}", e));
|
return error_responses::bad_request(format!("Provider transform error: {}", e));
|
||||||
}
|
}
|
||||||
@@ -672,13 +672,13 @@ impl crate::routers::RouterTrait for OpenAIRouter {
|
|||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
worker.circuit_breaker().record_failure();
|
worker.circuit_breaker().record_failure();
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_OPENAI,
|
metrics_labels::ROUTER_OPENAI,
|
||||||
smg_labels::BACKEND_EXTERNAL,
|
metrics_labels::BACKEND_EXTERNAL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
smg_labels::ENDPOINT_CHAT,
|
metrics_labels::ENDPOINT_CHAT,
|
||||||
smg_labels::ERROR_BACKEND,
|
metrics_labels::ERROR_BACKEND,
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
StatusCode::SERVICE_UNAVAILABLE,
|
StatusCode::SERVICE_UNAVAILABLE,
|
||||||
@@ -696,12 +696,12 @@ impl crate::routers::RouterTrait for OpenAIRouter {
|
|||||||
match resp.bytes().await {
|
match resp.bytes().await {
|
||||||
Ok(body) => {
|
Ok(body) => {
|
||||||
worker.circuit_breaker().record_success();
|
worker.circuit_breaker().record_success();
|
||||||
SmgMetrics::record_router_duration(
|
Metrics::record_router_duration(
|
||||||
smg_labels::ROUTER_OPENAI,
|
metrics_labels::ROUTER_OPENAI,
|
||||||
smg_labels::BACKEND_EXTERNAL,
|
metrics_labels::BACKEND_EXTERNAL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
smg_labels::ENDPOINT_CHAT,
|
metrics_labels::ENDPOINT_CHAT,
|
||||||
start.elapsed(),
|
start.elapsed(),
|
||||||
);
|
);
|
||||||
let mut response = Response::new(Body::from(body));
|
let mut response = Response::new(Body::from(body));
|
||||||
@@ -713,13 +713,13 @@ impl crate::routers::RouterTrait for OpenAIRouter {
|
|||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
worker.circuit_breaker().record_failure();
|
worker.circuit_breaker().record_failure();
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_OPENAI,
|
metrics_labels::ROUTER_OPENAI,
|
||||||
smg_labels::BACKEND_EXTERNAL,
|
metrics_labels::BACKEND_EXTERNAL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
smg_labels::ENDPOINT_CHAT,
|
metrics_labels::ENDPOINT_CHAT,
|
||||||
smg_labels::ERROR_BACKEND,
|
metrics_labels::ERROR_BACKEND,
|
||||||
);
|
);
|
||||||
(
|
(
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
@@ -730,12 +730,12 @@ impl crate::routers::RouterTrait for OpenAIRouter {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// For streaming, record duration at start since we can't track completion
|
// For streaming, record duration at start since we can't track completion
|
||||||
SmgMetrics::record_router_duration(
|
Metrics::record_router_duration(
|
||||||
smg_labels::ROUTER_OPENAI,
|
metrics_labels::ROUTER_OPENAI,
|
||||||
smg_labels::BACKEND_EXTERNAL,
|
metrics_labels::BACKEND_EXTERNAL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
smg_labels::ENDPOINT_CHAT,
|
metrics_labels::ENDPOINT_CHAT,
|
||||||
start.elapsed(),
|
start.elapsed(),
|
||||||
);
|
);
|
||||||
let stream = resp.bytes_stream();
|
let stream = resp.bytes_stream();
|
||||||
@@ -776,12 +776,12 @@ impl crate::routers::RouterTrait for OpenAIRouter {
|
|||||||
let streaming = body.stream.unwrap_or(false);
|
let streaming = body.stream.unwrap_or(false);
|
||||||
|
|
||||||
// Record request start
|
// Record request start
|
||||||
SmgMetrics::record_router_request(
|
Metrics::record_router_request(
|
||||||
smg_labels::ROUTER_OPENAI,
|
metrics_labels::ROUTER_OPENAI,
|
||||||
smg_labels::BACKEND_EXTERNAL,
|
metrics_labels::BACKEND_EXTERNAL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
smg_labels::ENDPOINT_RESPONSES,
|
metrics_labels::ENDPOINT_RESPONSES,
|
||||||
streaming,
|
streaming,
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -793,13 +793,13 @@ impl crate::routers::RouterTrait for OpenAIRouter {
|
|||||||
{
|
{
|
||||||
Ok(w) => w,
|
Ok(w) => w,
|
||||||
Err(response) => {
|
Err(response) => {
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_OPENAI,
|
metrics_labels::ROUTER_OPENAI,
|
||||||
smg_labels::BACKEND_EXTERNAL,
|
metrics_labels::BACKEND_EXTERNAL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
smg_labels::ENDPOINT_RESPONSES,
|
metrics_labels::ENDPOINT_RESPONSES,
|
||||||
smg_labels::ERROR_NO_WORKERS,
|
metrics_labels::ERROR_NO_WORKERS,
|
||||||
);
|
);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
@@ -853,13 +853,13 @@ impl crate::routers::RouterTrait for OpenAIRouter {
|
|||||||
.get_conversation(&conv_id)
|
.get_conversation(&conv_id)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_OPENAI,
|
metrics_labels::ROUTER_OPENAI,
|
||||||
smg_labels::BACKEND_EXTERNAL,
|
metrics_labels::BACKEND_EXTERNAL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
smg_labels::ENDPOINT_RESPONSES,
|
metrics_labels::ENDPOINT_RESPONSES,
|
||||||
smg_labels::ERROR_VALIDATION,
|
metrics_labels::ERROR_VALIDATION,
|
||||||
);
|
);
|
||||||
return error_responses::not_found("conversation", &conv_id.0);
|
return error_responses::not_found("conversation", &conv_id.0);
|
||||||
}
|
}
|
||||||
@@ -970,13 +970,13 @@ impl crate::routers::RouterTrait for OpenAIRouter {
|
|||||||
let mut payload = match to_value(&request_body) {
|
let mut payload = match to_value(&request_body) {
|
||||||
Ok(v) => v,
|
Ok(v) => v,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_OPENAI,
|
metrics_labels::ROUTER_OPENAI,
|
||||||
smg_labels::BACKEND_EXTERNAL,
|
metrics_labels::BACKEND_EXTERNAL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
smg_labels::ENDPOINT_RESPONSES,
|
metrics_labels::ENDPOINT_RESPONSES,
|
||||||
smg_labels::ERROR_VALIDATION,
|
metrics_labels::ERROR_VALIDATION,
|
||||||
);
|
);
|
||||||
return error_responses::bad_request(format!("Failed to serialize request: {}", e));
|
return error_responses::bad_request(format!("Failed to serialize request: {}", e));
|
||||||
}
|
}
|
||||||
@@ -984,13 +984,13 @@ impl crate::routers::RouterTrait for OpenAIRouter {
|
|||||||
|
|
||||||
let provider = self.get_provider_arc_for_worker(worker.as_ref(), model_id);
|
let provider = self.get_provider_arc_for_worker(worker.as_ref(), model_id);
|
||||||
if let Err(e) = provider.transform_request(&mut payload, Endpoint::Responses) {
|
if let Err(e) = provider.transform_request(&mut payload, Endpoint::Responses) {
|
||||||
SmgMetrics::record_router_error(
|
Metrics::record_router_error(
|
||||||
smg_labels::ROUTER_OPENAI,
|
metrics_labels::ROUTER_OPENAI,
|
||||||
smg_labels::BACKEND_EXTERNAL,
|
metrics_labels::BACKEND_EXTERNAL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
smg_labels::ENDPOINT_RESPONSES,
|
metrics_labels::ENDPOINT_RESPONSES,
|
||||||
smg_labels::ERROR_VALIDATION,
|
metrics_labels::ERROR_VALIDATION,
|
||||||
);
|
);
|
||||||
return error_responses::bad_request(format!("Provider transform error: {}", e));
|
return error_responses::bad_request(format!("Provider transform error: {}", e));
|
||||||
}
|
}
|
||||||
@@ -1021,12 +1021,12 @@ impl crate::routers::RouterTrait for OpenAIRouter {
|
|||||||
|
|
||||||
// Record duration only for successful requests (errors tracked inside handlers)
|
// Record duration only for successful requests (errors tracked inside handlers)
|
||||||
if response.status().is_success() {
|
if response.status().is_success() {
|
||||||
SmgMetrics::record_router_duration(
|
Metrics::record_router_duration(
|
||||||
smg_labels::ROUTER_OPENAI,
|
metrics_labels::ROUTER_OPENAI,
|
||||||
smg_labels::BACKEND_EXTERNAL,
|
metrics_labels::BACKEND_EXTERNAL,
|
||||||
smg_labels::CONNECTION_HTTP,
|
metrics_labels::CONNECTION_HTTP,
|
||||||
model,
|
model,
|
||||||
smg_labels::ENDPOINT_RESPONSES,
|
metrics_labels::ENDPOINT_RESPONSES,
|
||||||
start.elapsed(),
|
start.elapsed(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use tracing::{debug, error, info, warn};
|
|||||||
use crate::{
|
use crate::{
|
||||||
app_context::AppContext,
|
app_context::AppContext,
|
||||||
core::Job,
|
core::Job,
|
||||||
observability::metrics::{smg_labels, RouterMetrics, SmgMetrics},
|
observability::metrics::{metrics_labels, Metrics},
|
||||||
protocols::worker_spec::WorkerConfigRequest,
|
protocols::worker_spec::WorkerConfigRequest,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -302,7 +302,6 @@ pub async fn start_service_discovery(
|
|||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Error in Kubernetes watcher: {}", err);
|
error!("Error in Kubernetes watcher: {}", err);
|
||||||
RouterMetrics::record_discovery_watcher_error();
|
|
||||||
warn!(
|
warn!(
|
||||||
"Retrying in {} seconds with exponential backoff",
|
"Retrying in {} seconds with exponential backoff",
|
||||||
retry_delay.as_secs()
|
retry_delay.as_secs()
|
||||||
@@ -317,7 +316,6 @@ pub async fn start_service_discovery(
|
|||||||
"Kubernetes watcher exited, restarting in {} seconds",
|
"Kubernetes watcher exited, restarting in {} seconds",
|
||||||
config_arc.check_interval.as_secs()
|
config_arc.check_interval.as_secs()
|
||||||
);
|
);
|
||||||
RouterMetrics::record_discovery_watcher_restart();
|
|
||||||
time::sleep(config_arc.check_interval).await;
|
time::sleep(config_arc.check_interval).await;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -412,17 +410,16 @@ async fn handle_pod_event(
|
|||||||
match job_queue.submit(job).await {
|
match job_queue.submit(job).await {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
debug!("Worker addition job submitted for: {}", worker_url);
|
debug!("Worker addition job submitted for: {}", worker_url);
|
||||||
RouterMetrics::record_discovery_update(1, 0);
|
|
||||||
|
|
||||||
// Layer 4: Record successful registration from K8s discovery
|
// Layer 4: Record successful registration from K8s discovery
|
||||||
SmgMetrics::record_discovery_registration(
|
Metrics::record_discovery_registration(
|
||||||
smg_labels::DISCOVERY_KUBERNETES,
|
metrics_labels::DISCOVERY_KUBERNETES,
|
||||||
smg_labels::REGISTRATION_SUCCESS,
|
metrics_labels::REGISTRATION_SUCCESS,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update workers discovered gauge (using count from initial lock)
|
// Update workers discovered gauge (using count from initial lock)
|
||||||
SmgMetrics::set_discovery_workers_discovered(
|
Metrics::set_discovery_workers_discovered(
|
||||||
smg_labels::DISCOVERY_KUBERNETES,
|
metrics_labels::DISCOVERY_KUBERNETES,
|
||||||
tracked_count,
|
tracked_count,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -433,9 +430,9 @@ async fn handle_pod_event(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Layer 4: Record failed registration
|
// Layer 4: Record failed registration
|
||||||
SmgMetrics::record_discovery_registration(
|
Metrics::record_discovery_registration(
|
||||||
smg_labels::DISCOVERY_KUBERNETES,
|
metrics_labels::DISCOVERY_KUBERNETES,
|
||||||
smg_labels::REGISTRATION_FAILED,
|
metrics_labels::REGISTRATION_FAILED,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Ok(mut tracker) = tracked_pods.lock() {
|
if let Ok(mut tracker) = tracked_pods.lock() {
|
||||||
@@ -451,9 +448,9 @@ async fn handle_pod_event(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Pod already tracked - this is a duplicate event
|
// Pod already tracked - this is a duplicate event
|
||||||
SmgMetrics::record_discovery_registration(
|
Metrics::record_discovery_registration(
|
||||||
smg_labels::DISCOVERY_KUBERNETES,
|
metrics_labels::DISCOVERY_KUBERNETES,
|
||||||
smg_labels::REGISTRATION_DUPLICATE,
|
metrics_labels::REGISTRATION_DUPLICATE,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -498,17 +495,16 @@ async fn handle_pod_deletion(
|
|||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
debug!("Submitted worker removal job for {}", worker_url);
|
debug!("Submitted worker removal job for {}", worker_url);
|
||||||
RouterMetrics::record_discovery_update(0, 1);
|
|
||||||
|
|
||||||
// Layer 4: Record deregistration from K8s pod deletion
|
// Layer 4: Record deregistration from K8s pod deletion
|
||||||
SmgMetrics::record_discovery_deregistration(
|
Metrics::record_discovery_deregistration(
|
||||||
smg_labels::DISCOVERY_KUBERNETES,
|
metrics_labels::DISCOVERY_KUBERNETES,
|
||||||
smg_labels::DEREGISTRATION_POD_DELETED,
|
metrics_labels::DEREGISTRATION_POD_DELETED,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Update workers discovered gauge (using count from initial lock)
|
// Update workers discovered gauge (using count from initial lock)
|
||||||
SmgMetrics::set_discovery_workers_discovered(
|
Metrics::set_discovery_workers_discovered(
|
||||||
smg_labels::DISCOVERY_KUBERNETES,
|
metrics_labels::DISCOVERY_KUBERNETES,
|
||||||
remaining_count,
|
remaining_count,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user