0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

科学と神々株式会社Advent Calendar 2024

Day 18

~エラーの伝播制御とリカバリーメカニズム~

Last updated at Posted at 2024-12-17

第18回:エラーハンドリング(後編)

~エラーの伝播制御とリカバリーメカニズム~

はじめに

前回のエラー値の設計を踏まえ、今回はエラーの伝播制御とリカバリーメカニズムについて解説します。

エラーの伝播制御

// エラー伝播コントローラー
pub struct ErrorPropagationController {
    policies: HashMap<TypeId, Box<dyn PropagationPolicy>>,
    interceptors: Vec<Box<dyn ErrorInterceptor>>,
    context_tracker: ContextTracker,
}

impl ErrorPropagationController {
    pub async fn handle_error<E: Error + 'static>(
        &self,
        error: E,
        context: &ExecutionContext,
    ) -> Result<(), E> {
        // エラーインターセプト
        for interceptor in &self.interceptors {
            if interceptor.should_intercept(&error) {
                return interceptor.handle_error(error, context).await;
            }
        }
        
        // 伝播ポリシーの適用
        if let Some(policy) = self.get_policy::<E>() {
            match policy.decide(&error) {
                PropagationDecision::Propagate => Err(error),
                PropagationDecision::Handle(handler) => handler.handle_error(error, context).await,
                PropagationDecision::Suppress => Ok(()),
            }
        } else {
            // デフォルトの伝播動作
            Err(error)
        }
    }
}

// 伝播ポリシー
pub trait PropagationPolicy: Send + Sync {
    fn decide<E: Error>(&self, error: &E) -> PropagationDecision<E>;
}

// エラーインターセプター
pub trait ErrorInterceptor: Send + Sync {
    fn should_intercept<E: Error>(&self, error: &E) -> bool;
    async fn handle_error<E: Error>(&self, error: E, context: &ExecutionContext) -> Result<(), E>;
}

リカバリーメカニズム

// リカバリーマネージャー
pub struct RecoveryManager {
    strategies: Vec<Box<dyn RecoveryStrategy>>,
    state_manager: StateManager,
    recovery_log: RecoveryLog,
}

impl RecoveryManager {
    pub async fn attempt_recovery<E: Error>(
        &mut self,
        error: &E,
        context: &ExecutionContext,
    ) -> Result<(), RecoveryError> {
        // 状態の保存
        let checkpoint = self.state_manager.create_checkpoint()?;
        
        // 利用可能な回復戦略を試行
        for strategy in &self.strategies {
            if strategy.can_handle(error) {
                match strategy.recover(error, context).await {
                    Ok(()) => {
                        self.recovery_log.record_success(error, strategy.name());
                        return Ok(());
                    }
                    Err(e) => {
                        self.recovery_log.record_failure(error, strategy.name(), &e);
                        // 状態を復元して次の戦略を試行
                        self.state_manager.restore_checkpoint(checkpoint.clone())?;
                    }
                }
            }
        }
        
        Err(RecoveryError::NoValidStrategy)
    }
}

// リカバリー戦略
pub trait RecoveryStrategy: Send + Sync {
    fn name(&self) -> &str;
    fn can_handle<E: Error>(&self, error: &E) -> bool;
    async fn recover<E: Error>(&self, error: &E, context: &ExecutionContext) -> Result<(), RecoveryError>;
}

// 自動リトライ戦略
pub struct RetryStrategy {
    max_attempts: u32,
    backoff: Box<dyn BackoffStrategy>,
}

impl RecoveryStrategy for RetryStrategy {
    async fn recover<E: Error>(&self, error: &E, context: &ExecutionContext) -> Result<(), RecoveryError> {
        let mut attempts = 0;
        
        while attempts < self.max_attempts {
            match context.retry_operation().await {
                Ok(()) => return Ok(()),
                Err(_) => {
                    attempts += 1;
                    self.backoff.wait(attempts).await;
                }
            }
        }
        
        Err(RecoveryError::MaxAttemptsExceeded)
    }
}

デバッグ支援

// エラーデバッグサポート
pub struct ErrorDebugSupport {
    trace_collector: TraceCollector,
    analyzer: ErrorAnalyzer,
    reporter: ErrorReporter,
}

impl ErrorDebugSupport {
    pub async fn analyze_error<E: Error>(
        &self,
        error: &E,
        context: &ExecutionContext,
    ) -> DebugReport {
        // エラートレースの収集
        let trace = self.trace_collector.collect_trace(error, context).await?;
        
        // エラーの分析
        let analysis = self.analyzer.analyze(&trace)?;
        
        // レポートの生成
        self.reporter.generate_report(analysis)
    }
}

// トレース収集
pub struct TraceCollector {
    collectors: Vec<Box<dyn TraceCollectorPlugin>>,
}

impl TraceCollector {
    pub async fn collect_trace<E: Error>(
        &self,
        error: &E,
        context: &ExecutionContext,
    ) -> Result<ErrorTrace> {
        let mut trace = ErrorTrace::new(error);
        
        for collector in &self.collectors {
            collector.collect_information(&mut trace, context).await?;
        }
        
        Ok(trace)
    }
}

実装例:エラーリカバリーシステムの実装

// エラーリカバリーシステムの実装例
pub struct ErrorRecoverySystem {
    propagation_controller: ErrorPropagationController,
    recovery_manager: RecoveryManager,
    debug_support: ErrorDebugSupport,
}

impl ErrorRecoverySystem {
    pub async fn handle_error<E: Error + 'static>(
        &mut self,
        error: E,
        context: &ExecutionContext,
    ) -> Result<(), E> {
        // エラーの伝播制御
        if let Err(error) = self.propagation_controller.handle_error(error, context).await {
            // リカバリーの試行
            if let Err(recovery_error) = self.recovery_manager.attempt_recovery(&error, context).await {
                // リカバリー失敗時のデバッグ情報収集
                let debug_report = self.debug_support.analyze_error(&error, context).await?;
                log::error!("Recovery failed: {:?}\nDebug report: {:?}", recovery_error, debug_report);
                return Err(error);
            }
        }
        Ok(())
    }
}

// 使用例
async fn example_usage() -> Result<(), AppError> {
    let mut system = ErrorRecoverySystem::new();
    
    // リカバリー戦略の登録
    system.recovery_manager.add_strategy(Box::new(RetryStrategy::new(3)));
    system.recovery_manager.add_strategy(Box::new(FallbackStrategy::new()));
    
    // エラーが発生する可能性のある操作
    let result = complex_operation().await;
    
    match result {
        Ok(value) => Ok(value),
        Err(error) => {
            let context = ExecutionContext::current();
            system.handle_error(error, &context).await
        }
    }
}

// リカバリー可能な操作の例
async fn complex_operation() -> Result<(), AppError> {
    let mut recovery_points = Vec::new();
    
    // 操作の実行
    for step in operation_steps() {
        if step.is_critical() {
            // 重要なステップの前にリカバリーポイントを作成
            recovery_points.push(create_recovery_point().await?);
        }
        
        match step.execute().await {
            Ok(()) => continue,
            Err(error) => {
                // エラー発生時の回復処理
                if let Some(point) = recovery_points.last() {
                    restore_recovery_point(point).await?;
                    // 再試行ロジック
                }
                return Err(error.into());
            }
        }
    }
    
    Ok(())
}

今回のまとめ

  1. 効果的なエラー伝播制御の実装
  2. 柔軟なリカバリーメカニズム
  3. 包括的なデバッグ支援
  4. 実用的なエラーリカバリーシステム

次回予告

第19回では、型システムの応用について解説します。状態遷移の型安全な表現と依存型のエミュレーションについて詳しく見ていきます。

参考資料

  • Advanced Error Handling Patterns
  • System Recovery Strategies
  • Debugging Distributed Systems
  • Resilient Error Management
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?