第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(())
}
今回のまとめ
- 効果的なエラー伝播制御の実装
- 柔軟なリカバリーメカニズム
- 包括的なデバッグ支援
- 実用的なエラーリカバリーシステム
次回予告
第19回では、型システムの応用について解説します。状態遷移の型安全な表現と依存型のエミュレーションについて詳しく見ていきます。
参考資料
- Advanced Error Handling Patterns
- System Recovery Strategies
- Debugging Distributed Systems
- Resilient Error Management