safety: add clippy deny(unwrap_used, expect_used) to api_gateway, backtesting_service

Add #![deny(clippy::unwrap_used, clippy::expect_used)] to api_gateway and
backtesting_service crate roots. The ml and common crates already had these
deny attributes.

Production code fixes:
- api_gateway: replace expect with unwrap_or for cache stats and LRU eviction
- api_gateway: add #[allow] on NonZeroU32 constants and Prometheus metrics
- backtesting_service: replace expect/unwrap with ok_or_else/? for OHLCV
  bar bounds checks and chrono timestamp resampling
- backtesting_service: add #[allow] on Prometheus Lazy metric statics

Test code: cfg_attr(test, allow) at crate root for both crates.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jgrusewski
2026-02-21 19:49:13 +01:00
parent d35c45e654
commit 15c094c7ee
8 changed files with 38 additions and 13 deletions

View File

@@ -204,7 +204,7 @@ impl LocalRevocationCache {
let misses = self.misses.load(std::sync::atomic::Ordering::Relaxed);
let total = hits
.checked_add(misses)
.expect("Cache stats overflow: hits + misses exceeds u64");
.unwrap_or(u64::MAX);
let hit_rate = if total > 0 {
let rate = (hits as f64 / total as f64) * 100.0;
// f64 multiplication is safe, check for valid result

View File

@@ -25,13 +25,19 @@ use x509_parser::{
};
// --- Prometheus Metrics ---
// SAFETY: Prometheus metric registration panics only if duplicate names exist,
// which is a fatal misconfiguration that should crash at startup.
#[allow(clippy::unwrap_used)]
static OCSP_REQUESTS_TOTAL: Lazy<IntCounter> =
Lazy::new(|| register_int_counter!("ocsp_requests_total", "Total OCSP requests sent").unwrap());
#[allow(clippy::unwrap_used)]
static OCSP_CACHE_HITS: Lazy<IntCounter> =
Lazy::new(|| register_int_counter!("ocsp_cache_hits_total", "Total OCSP cache hits").unwrap());
#[allow(clippy::unwrap_used)]
static OCSP_CACHE_MISSES: Lazy<IntCounter> = Lazy::new(|| {
register_int_counter!("ocsp_cache_misses_total", "Total OCSP cache misses").unwrap()
});
#[allow(clippy::unwrap_used)]
static OCSP_REVOKED_TOTAL: Lazy<IntCounter> = Lazy::new(|| {
register_int_counter!(
"ocsp_revoked_certs_total",
@@ -39,9 +45,11 @@ static OCSP_REVOKED_TOTAL: Lazy<IntCounter> = Lazy::new(|| {
)
.unwrap()
});
#[allow(clippy::unwrap_used)]
static OCSP_REQUEST_FAILURES: Lazy<IntCounter> = Lazy::new(|| {
register_int_counter!("ocsp_request_failures_total", "Total failed OCSP requests").unwrap()
});
#[allow(clippy::unwrap_used)]
static OCSP_RESPONSE_VALIDATION_FAILURES: Lazy<IntCounter> = Lazy::new(|| {
register_int_counter!(
"ocsp_response_validation_failures_total",
@@ -49,6 +57,7 @@ static OCSP_RESPONSE_VALIDATION_FAILURES: Lazy<IntCounter> = Lazy::new(|| {
)
.unwrap()
});
#[allow(clippy::unwrap_used)]
static OCSP_REQUEST_LATENCY: Lazy<Histogram> = Lazy::new(|| {
register_histogram!("ocsp_request_latency_seconds", "Latency of OCSP requests").unwrap()
});

View File

@@ -75,10 +75,14 @@ impl MlTradingProxy {
/// - Connection pooling managed by tonic::transport::Channel
pub fn new(client: TradingServiceClient<tonic::transport::Channel>) -> Self {
// Create rate limiter for predictions: 100 requests per minute per user
// SAFETY: NonZeroU32::new on positive constants always returns Some
#[allow(clippy::unwrap_used)]
let quota_predictions = Quota::per_minute(NonZeroU32::new(100).unwrap());
let rate_limiter_predictions = Arc::new(GovernorRateLimiter::keyed(quota_predictions));
// Create rate limiter for performance queries: 20 requests per minute per user (expensive)
// SAFETY: NonZeroU32::new on positive constants always returns Some
#[allow(clippy::unwrap_used)]
let quota_performance = Quota::per_minute(NonZeroU32::new(20).unwrap());
let rate_limiter_performance = Arc::new(GovernorRateLimiter::keyed(quota_performance));

View File

@@ -9,6 +9,9 @@
//! - Performance monitoring
//! - Token bucket rate limiting with <50ns cache hits
#![deny(clippy::unwrap_used, clippy::expect_used)]
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
// Include generated protobuf code FIRST (needed by other modules)
pub mod foxhunt {
pub mod tli {

View File

@@ -344,7 +344,7 @@ impl RateLimiter {
let num_to_evict = self
.max_cache_size
.checked_div(10)
.expect("Division by zero in LRU eviction");
.unwrap_or(1);
// Collect entries with their last access time (lock-free iteration)
let mut entries: Vec<_> = self

View File

@@ -330,13 +330,14 @@ impl DbnMarketDataRepository {
return Err(anyhow::anyhow!("No data found for symbol: {}", symbol));
}
// SAFETY: bars is non-empty (validated by is_empty check above)
let first = bars
.first()
.expect("INVARIANT: bars is non-empty (validated above)")
.ok_or_else(|| anyhow::anyhow!("INVARIANT violated: bars is empty after check"))?
.timestamp;
let last = bars
.last()
.expect("INVARIANT: bars is non-empty (validated above)")
.ok_or_else(|| anyhow::anyhow!("INVARIANT violated: bars is empty after check"))?
.timestamp;
debug!("Date range for {}: {} to {}", symbol, first, last);
@@ -372,11 +373,11 @@ impl DbnMarketDataRepository {
let bucket_start = bar
.timestamp
.with_minute((bar.timestamp.minute() / target_minutes) * target_minutes)
.unwrap()
.ok_or_else(|| anyhow::anyhow!("Invalid minute in timestamp resampling"))?
.with_second(0)
.unwrap()
.ok_or_else(|| anyhow::anyhow!("Invalid second in timestamp resampling"))?
.with_nanosecond(0)
.unwrap();
.ok_or_else(|| anyhow::anyhow!("Invalid nanosecond in timestamp resampling"))?;
// Start new bucket or add to existing
match &mut current_bucket {
@@ -391,11 +392,11 @@ impl DbnMarketDataRepository {
.with_minute(
(first_bar.timestamp.minute() / target_minutes) * target_minutes,
)
.unwrap()
.ok_or_else(|| anyhow::anyhow!("Invalid minute in timestamp resampling"))?
.with_second(0)
.unwrap()
.ok_or_else(|| anyhow::anyhow!("Invalid second in timestamp resampling"))?
.with_nanosecond(0)
.unwrap();
.ok_or_else(|| anyhow::anyhow!("Invalid nanosecond in timestamp resampling"))?;
if bucket_start == first_bucket_start {
// Same bucket
@@ -436,9 +437,10 @@ impl DbnMarketDataRepository {
#[allow(clippy::indexing_slicing)] // Bounds checked above: !is_empty()
let first = &bucket[0];
// SAFETY: bucket is non-empty (validated by is_empty check above)
let last = bucket
.last()
.expect("INVARIANT: bucket is non-empty (validated above)");
.ok_or_else(|| anyhow::anyhow!("INVARIANT violated: bucket is empty after check"))?;
// Calculate OHLCV
let open = first.open;

View File

@@ -4,8 +4,8 @@
//! for testing purposes.
#![warn(missing_docs)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::expect_used)]
#![deny(clippy::unwrap_used, clippy::expect_used)]
#![cfg_attr(test, allow(clippy::unwrap_used, clippy::expect_used))]
/// Performance analysis and metrics
pub mod performance;

View File

@@ -5,7 +5,11 @@
use once_cell::sync::Lazy;
use prometheus::{register_counter, register_gauge, Counter, Gauge};
// SAFETY: Prometheus metric registration panics only if duplicate names exist,
// which is a fatal misconfiguration that should crash at startup.
/// Service uptime in seconds
#[allow(clippy::expect_used)]
pub static SERVICE_UPTIME: Lazy<Gauge> = Lazy::new(|| {
register_gauge!(
"backtesting_service_uptime_seconds",
@@ -15,6 +19,7 @@ pub static SERVICE_UPTIME: Lazy<Gauge> = Lazy::new(|| {
});
/// Total number of backtests started
#[allow(clippy::expect_used)]
pub static BACKTESTS_STARTED: Lazy<Counter> = Lazy::new(|| {
register_counter!(
"backtesting_backtests_started_total",
@@ -24,6 +29,7 @@ pub static BACKTESTS_STARTED: Lazy<Counter> = Lazy::new(|| {
});
/// Total number of backtests completed
#[allow(clippy::expect_used)]
pub static BACKTESTS_COMPLETED: Lazy<Counter> = Lazy::new(|| {
register_counter!(
"backtesting_backtests_completed_total",
@@ -35,6 +41,7 @@ pub static BACKTESTS_COMPLETED: Lazy<Counter> = Lazy::new(|| {
});
/// Total number of backtest errors
#[allow(clippy::expect_used)]
pub static BACKTEST_ERRORS: Lazy<Counter> = Lazy::new(|| {
register_counter!(
"backtesting_errors_total",