feat(ml): WAVE 29 DQN Codebase Cleanup & Refactoring Campaign
BREAKING CHANGES: - Removed orphaned dqn.rs monolithic trainer (4,975 lines) - Removed orphaned dqn_ensemble.rs module (816 lines) - Removed orphaned tft.rs and tft_complete_int8_integration_test.rs - TFT trainer split into modular directory structure DQN Module Refactoring: - Split trainers/dqn.rs into modular structure (config.rs, statistics.rs, trainer.rs) - Fixed hyperopt 39D search space (continuous params only) - Boolean flags (use_dueling, use_double_dqn, use_per, use_noisy_nets) are now FIXED architectural decisions - use_distributional defaults to false (Candle BUG #36 - scatter_add gradient issues) Clean Module Structure: - ml/src/trainers/dqn/ directory with proper mod.rs exports - ml/src/trainers/tft/ directory with config.rs, types.rs, model.rs, trainer.rs, tests.rs - All P0 features validated: TD-error clamping, batch diversity, LR scheduler, priority staleness Documentation: - Added comprehensive docs in docs/codebase-cleanup/ - ADR-001 for DQN refactoring decisions - Rainbow DQN component matrix and quick reference guides Build Status: Compiles with zero errors 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
201
common/src/error/macros.rs
Normal file
201
common/src/error/macros.rs
Normal file
@@ -0,0 +1,201 @@
|
||||
//! Macros for reducing service error boilerplate
|
||||
//!
|
||||
//! This module provides convenience macros to generate common error handling code
|
||||
//! and reduce repetition across service-specific error implementations.
|
||||
|
||||
/// Generate a service error enum with CommonError wrapper and convenience constructors
|
||||
///
|
||||
/// This macro generates:
|
||||
/// - An enum with a `Common(CommonError)` variant plus custom variants
|
||||
/// - Constructor methods for each variant
|
||||
/// - Automatic `From<CommonError>` implementation
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use common::error::define_service_error;
|
||||
///
|
||||
/// define_service_error! {
|
||||
/// MyServiceError {
|
||||
/// /// Custom error variant
|
||||
/// CustomError { field: String, value: i32 },
|
||||
///
|
||||
/// /// Another custom variant
|
||||
/// AnotherError(String),
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! define_service_error {
|
||||
(
|
||||
$(#[$meta:meta])*
|
||||
$name:ident {
|
||||
$(
|
||||
$(#[$variant_meta:meta])*
|
||||
$variant:ident $({ $($field:ident : $field_ty:ty),* $(,)? })?
|
||||
$( ( $($tuple_ty:ty),* $(,)? ) )?
|
||||
),* $(,)?
|
||||
}
|
||||
) => {
|
||||
$(#[$meta])*
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum $name {
|
||||
/// Common error with context
|
||||
#[error("Service error: {0}")]
|
||||
Common(#[from] $crate::error::CommonError),
|
||||
|
||||
$(
|
||||
$(#[$variant_meta])*
|
||||
#[error("{}", stringify!($variant))]
|
||||
$variant $({ $($field: $field_ty,)* })? $( ( $($tuple_ty,)* ) )?,
|
||||
)*
|
||||
}
|
||||
|
||||
impl From<$name> for $crate::error::CommonError {
|
||||
fn from(err: $name) -> Self {
|
||||
use $crate::error::ServiceErrorExt;
|
||||
err.to_common_error()
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Implement common convenience constructors for a service error
|
||||
///
|
||||
/// Generates constructors for common error types using `CommonError` builders.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use common::error::impl_common_error_constructors;
|
||||
///
|
||||
/// impl_common_error_constructors!(MyServiceError);
|
||||
///
|
||||
/// // Now you can use:
|
||||
/// // MyServiceError::network("connection failed")
|
||||
/// // MyServiceError::configuration("missing key")
|
||||
/// // MyServiceError::validation("field", "invalid value")
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! impl_common_error_constructors {
|
||||
($error_type:ty) => {
|
||||
impl $error_type {
|
||||
/// Create network error using CommonError
|
||||
pub fn network<M: Into<String>>(message: M) -> Self {
|
||||
Self::Common($crate::error::CommonError::network(message))
|
||||
}
|
||||
|
||||
/// Create configuration error using CommonError
|
||||
pub fn configuration<M: Into<String>>(message: M) -> Self {
|
||||
Self::Common($crate::error::CommonError::config(message))
|
||||
}
|
||||
|
||||
/// Create validation error using CommonError
|
||||
pub fn validation<F: Into<String>, M: Into<String>>(field: F, message: M) -> Self {
|
||||
Self::Common($crate::error::CommonError::validation(format!(
|
||||
"{}: {}",
|
||||
field.into(),
|
||||
message.into()
|
||||
)))
|
||||
}
|
||||
|
||||
/// Create timeout error using CommonError
|
||||
pub fn timeout(actual_ms: u64, max_ms: u64) -> Self {
|
||||
Self::Common($crate::error::CommonError::timeout(actual_ms, max_ms))
|
||||
}
|
||||
|
||||
/// Create resource exhausted error using CommonError
|
||||
pub fn resource_exhausted<R: Into<String>>(resource: R) -> Self {
|
||||
Self::Common($crate::error::CommonError::resource_exhausted(resource))
|
||||
}
|
||||
|
||||
/// Create internal error using CommonError
|
||||
pub fn internal<M: Into<String>>(message: M) -> Self {
|
||||
Self::Common($crate::error::CommonError::internal(message))
|
||||
}
|
||||
|
||||
/// Create authentication error using CommonError
|
||||
pub fn authentication<M: Into<String>>(message: M) -> Self {
|
||||
Self::Common($crate::error::CommonError::service(
|
||||
$crate::error::ErrorCategory::Authentication,
|
||||
message.into(),
|
||||
))
|
||||
}
|
||||
|
||||
/// Create serialization error using CommonError
|
||||
pub fn serialization<M: Into<String>>(message: M) -> Self {
|
||||
Self::Common($crate::error::CommonError::serialization(message))
|
||||
}
|
||||
|
||||
/// Create not found error using CommonError
|
||||
pub fn not_found<R: Into<String>, I: Into<String>>(resource: R, identifier: I) -> Self {
|
||||
Self::Common($crate::error::CommonError::service(
|
||||
$crate::error::ErrorCategory::Resource,
|
||||
format!("{} not found: {}", resource.into(), identifier.into()),
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Implement standard error conversions for service errors
|
||||
///
|
||||
/// Generates `From` implementations for common error types.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use common::error::impl_error_conversions;
|
||||
///
|
||||
/// impl_error_conversions!(MyServiceError, [std::io::Error, serde_json::Error]);
|
||||
/// ```
|
||||
#[macro_export]
|
||||
macro_rules! impl_error_conversions {
|
||||
($error_type:ty, [$($from_type:ty),* $(,)?]) => {
|
||||
$(
|
||||
impl From<$from_type> for $error_type {
|
||||
fn from(err: $from_type) -> Self {
|
||||
Self::Common($crate::error::CommonError::internal(format!("{}", err)))
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::error::{CommonError, ErrorCategory};
|
||||
|
||||
define_service_error! {
|
||||
TestError {
|
||||
CustomError { message: String },
|
||||
TupleError(String, i32),
|
||||
}
|
||||
}
|
||||
|
||||
impl_common_error_constructors!(TestError);
|
||||
|
||||
#[test]
|
||||
fn test_define_service_error() {
|
||||
let _custom = TestError::CustomError {
|
||||
message: "test".to_string(),
|
||||
};
|
||||
let _tuple = TestError::TupleError("test".to_string(), 42);
|
||||
let _common = TestError::Common(CommonError::network("test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_common_error_constructors() {
|
||||
let _network = TestError::network("connection failed");
|
||||
let _config = TestError::configuration("missing key");
|
||||
let _validation = TestError::validation("field", "invalid");
|
||||
let _timeout = TestError::timeout(5000, 2000);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_conversion() {
|
||||
let common_err = CommonError::network("test");
|
||||
let service_err = TestError::Common(common_err);
|
||||
let _back_to_common: CommonError = service_err.into();
|
||||
}
|
||||
}
|
||||
201
common/src/error/service_error.rs
Normal file
201
common/src/error/service_error.rs
Normal file
@@ -0,0 +1,201 @@
|
||||
//! Service error trait for standardized error handling across all services
|
||||
//!
|
||||
//! This module provides a common trait that all service-specific errors implement,
|
||||
//! enabling consistent error handling, metrics, and retry logic across the codebase.
|
||||
|
||||
use super::{CommonError, ErrorCategory, ErrorSeverity, RetryStrategy};
|
||||
|
||||
/// Common trait for service-specific error types
|
||||
///
|
||||
/// Implement this trait to provide standardized error handling for your service.
|
||||
/// This enables consistent metrics collection, retry logic, and error categorization.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use common::error::{ServiceErrorExt, CommonError, ErrorCategory, ErrorSeverity, RetryStrategy};
|
||||
///
|
||||
/// #[derive(Debug, thiserror::Error)]
|
||||
/// pub enum MyServiceError {
|
||||
/// #[error("Common error: {0}")]
|
||||
/// Common(#[from] CommonError),
|
||||
///
|
||||
/// #[error("Custom error: {0}")]
|
||||
/// Custom(String),
|
||||
/// }
|
||||
///
|
||||
/// impl ServiceErrorExt for MyServiceError {
|
||||
/// fn error_code(&self) -> &'static str {
|
||||
/// match self {
|
||||
/// Self::Common(_) => "MY_COMMON_ERROR",
|
||||
/// Self::Custom(_) => "MY_CUSTOM_ERROR",
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn category(&self) -> ErrorCategory {
|
||||
/// match self {
|
||||
/// Self::Common(err) => err.category(),
|
||||
/// Self::Custom(_) => ErrorCategory::System,
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn severity(&self) -> ErrorSeverity {
|
||||
/// match self {
|
||||
/// Self::Common(err) => err.severity(),
|
||||
/// Self::Custom(_) => ErrorSeverity::Error,
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn retry_strategy(&self) -> RetryStrategy {
|
||||
/// match self {
|
||||
/// Self::Common(err) => err.retry_strategy(),
|
||||
/// Self::Custom(_) => RetryStrategy::Linear { base_delay_ms: 1000 },
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// fn to_common_error(self) -> CommonError {
|
||||
/// match self {
|
||||
/// Self::Common(err) => err,
|
||||
/// Self::Custom(msg) => CommonError::internal(msg),
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub trait ServiceErrorExt: std::error::Error + Send + Sync {
|
||||
/// Get unique error code for monitoring and alerting
|
||||
///
|
||||
/// Error codes should be:
|
||||
/// - Uppercase with underscores (e.g., "ML_MODEL_TRAINING_ERROR")
|
||||
/// - Service-prefixed (e.g., "ML_", "RISK_", "DATA_", "TLI_")
|
||||
/// - Descriptive and unique within the service
|
||||
fn error_code(&self) -> &'static str;
|
||||
|
||||
/// Get error category for classification and metrics
|
||||
fn category(&self) -> ErrorCategory;
|
||||
|
||||
/// Get error severity level for prioritization and alerting
|
||||
fn severity(&self) -> ErrorSeverity;
|
||||
|
||||
/// Get retry strategy for automatic error recovery
|
||||
fn retry_strategy(&self) -> RetryStrategy;
|
||||
|
||||
/// Convert service-specific error to CommonError for interoperability
|
||||
///
|
||||
/// This enables seamless integration with common error handling infrastructure
|
||||
/// and allows service errors to be used in generic contexts.
|
||||
fn to_common_error(self) -> CommonError;
|
||||
|
||||
/// Check if error is retryable based on retry strategy
|
||||
fn is_retryable(&self) -> bool {
|
||||
!matches!(self.retry_strategy(), RetryStrategy::NoRetry)
|
||||
}
|
||||
}
|
||||
|
||||
/// Extension trait for Result types using service errors
|
||||
pub trait ServiceResultExt<T, E: ServiceErrorExt> {
|
||||
/// Map service error to CommonError
|
||||
fn map_to_common(self) -> Result<T, CommonError>;
|
||||
|
||||
/// Extract error code if Result is Err
|
||||
fn error_code(&self) -> Option<&'static str>;
|
||||
|
||||
/// Check if error is retryable
|
||||
fn is_retryable(&self) -> bool;
|
||||
}
|
||||
|
||||
impl<T, E: ServiceErrorExt> ServiceResultExt<T, E> for Result<T, E> {
|
||||
fn map_to_common(self) -> Result<T, CommonError> {
|
||||
self.map_err(ServiceErrorExt::to_common_error)
|
||||
}
|
||||
|
||||
fn error_code(&self) -> Option<&'static str> {
|
||||
self.as_ref().err().map(ServiceErrorExt::error_code)
|
||||
}
|
||||
|
||||
fn is_retryable(&self) -> bool {
|
||||
self.as_ref().err().is_some_and(ServiceErrorExt::is_retryable)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum TestServiceError {
|
||||
#[error("Common error: {0}")]
|
||||
Common(#[from] CommonError),
|
||||
|
||||
#[error("Test error: {0}")]
|
||||
Test(String),
|
||||
}
|
||||
|
||||
impl ServiceErrorExt for TestServiceError {
|
||||
fn error_code(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Common(_) => "TEST_COMMON_ERROR",
|
||||
Self::Test(_) => "TEST_CUSTOM_ERROR",
|
||||
}
|
||||
}
|
||||
|
||||
fn category(&self) -> ErrorCategory {
|
||||
match self {
|
||||
Self::Common(err) => err.category(),
|
||||
Self::Test(_) => ErrorCategory::System,
|
||||
}
|
||||
}
|
||||
|
||||
fn severity(&self) -> ErrorSeverity {
|
||||
match self {
|
||||
Self::Common(err) => err.severity(),
|
||||
Self::Test(_) => ErrorSeverity::Error,
|
||||
}
|
||||
}
|
||||
|
||||
fn retry_strategy(&self) -> RetryStrategy {
|
||||
match self {
|
||||
Self::Common(err) => err.retry_strategy(),
|
||||
Self::Test(_) => RetryStrategy::NoRetry,
|
||||
}
|
||||
}
|
||||
|
||||
fn to_common_error(self) -> CommonError {
|
||||
match self {
|
||||
Self::Common(err) => err,
|
||||
Self::Test(msg) => CommonError::internal(msg),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_service_error_trait() {
|
||||
let error = TestServiceError::Test("test error".to_string());
|
||||
|
||||
assert_eq!(error.error_code(), "TEST_CUSTOM_ERROR");
|
||||
assert_eq!(error.category(), ErrorCategory::System);
|
||||
assert_eq!(error.severity(), ErrorSeverity::Error);
|
||||
assert!(!error.is_retryable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_common_error_delegation() {
|
||||
let common_err = CommonError::config("test config error");
|
||||
let error = TestServiceError::Common(common_err);
|
||||
|
||||
assert_eq!(error.error_code(), "TEST_COMMON_ERROR");
|
||||
assert_eq!(error.category(), ErrorCategory::Configuration);
|
||||
assert_eq!(error.severity(), ErrorSeverity::Critical);
|
||||
assert!(!error.is_retryable());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_result_extensions() {
|
||||
let ok_result: Result<i32, TestServiceError> = Ok(42);
|
||||
assert!(ok_result.error_code().is_none());
|
||||
assert!(!ok_result.is_retryable());
|
||||
|
||||
let err_result: Result<i32, TestServiceError> = Err(TestServiceError::Test("error".to_string()));
|
||||
assert_eq!(err_result.error_code(), Some("TEST_CUSTOM_ERROR"));
|
||||
assert!(!err_result.is_retryable());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user