""" Adaptive Rate Limit Use Case - Passt Geschwindigkeit dynamisch an """ import logging from typing import Dict, Any, Optional from datetime import datetime, timedelta from domain.services.rate_limit_service import IRateLimitService from domain.value_objects.action_timing import ActionTiming, ActionType from domain.entities.rate_limit_policy import RateLimitPolicy logger = logging.getLogger("adaptive_rate_limit_use_case") class AdaptiveRateLimitUseCase: """ Use Case für adaptive Geschwindigkeitsanpassung basierend auf Systemverhalten. Analysiert Response-Zeiten, passt Delays dynamisch an und erkennt Anomalien. """ def __init__(self, rate_limit_service: IRateLimitService): self.rate_limit_service = rate_limit_service self.anomaly_threshold = 2.0 # Standardabweichungen für Anomalie self.adaptation_interval = timedelta(minutes=5) self.last_adaptation = {} def execute(self, action_type: ActionType, context: Optional[Dict[str, Any]] = None) -> float: """ Führt adaptive Rate Limiting Logik aus. Args: action_type: Typ der auszuführenden Aktion context: Zusätzlicher Kontext (z.B. Session-ID, Platform) Returns: Optimale Verzögerung in Sekunden """ # Prüfe ob Adaptation notwendig ist if self._should_adapt(action_type): self._adapt_policy(action_type) # Berechne Delay mit aktuellem Policy delay = self.rate_limit_service.calculate_delay(action_type, context) # Warte wenn nötig actual_wait = self.rate_limit_service.wait_if_needed(action_type) logger.debug(f"Adaptive delay for {action_type.value}: {delay:.2f}s (waited: {actual_wait:.2f}s)") return delay def record_timing(self, timing: ActionTiming) -> None: """ Zeichnet Timing auf und triggert ggf. Anpassungen. Args: timing: Timing-Informationen der ausgeführten Aktion """ # Zeichne Timing auf self.rate_limit_service.record_action(timing) # Analysiere auf Anomalien if self._is_anomaly(timing): logger.warning(f"Anomaly detected for {timing.action_type.value}: " f"duration={timing.duration}s, success={timing.success}") self._handle_anomaly(timing) def _should_adapt(self, action_type: ActionType) -> bool: """Prüft ob Policy angepasst werden sollte""" last = self.last_adaptation.get(action_type, datetime.min) return datetime.now() - last > self.adaptation_interval def _adapt_policy(self, action_type: ActionType) -> None: """Passt Policy basierend auf gesammelten Daten an""" # Hole Statistiken stats = self.rate_limit_service.get_statistics( action_type, timeframe=timedelta(hours=1) ) if not stats or 'success_rate' not in stats: return current_policy = self.rate_limit_service.get_policy(action_type) success_rate = stats['success_rate'] avg_duration = stats.get('avg_duration_ms', 0) / 1000.0 # Neue Policy-Parameter berechnen new_policy = self._calculate_new_policy( current_policy, success_rate, avg_duration ) if new_policy != current_policy: self.rate_limit_service.update_policy(action_type, new_policy) logger.info(f"Adapted policy for {action_type.value}: " f"min_delay={new_policy.min_delay:.2f}, " f"max_delay={new_policy.max_delay:.2f}") self.last_adaptation[action_type] = datetime.now() def _calculate_new_policy(self, current: RateLimitPolicy, success_rate: float, avg_duration: float) -> RateLimitPolicy: """Berechnet neue Policy-Parameter""" # Kopiere aktuelle Policy new_min = current.min_delay new_max = current.max_delay new_backoff = current.backoff_multiplier # Anpassung basierend auf Erfolgsrate if success_rate < 0.7: # Niedrige Erfolgsrate # Erhöhe Delays signifikant new_min = min(new_min * 1.3, 10.0) new_max = min(new_max * 1.3, 30.0) new_backoff = min(new_backoff * 1.1, 3.0) elif success_rate < 0.85: # Mittlere Erfolgsrate # Moderate Erhöhung new_min = min(new_min * 1.1, 10.0) new_max = min(new_max * 1.1, 30.0) elif success_rate > 0.95: # Hohe Erfolgsrate # Vorsichtige Verringerung if avg_duration < current.min_delay * 0.8: new_min = max(new_min * 0.9, 0.1) new_max = max(new_max * 0.9, new_min * 3) # Stelle sicher dass max > min new_max = max(new_max, new_min * 2) return RateLimitPolicy( min_delay=round(new_min, 2), max_delay=round(new_max, 2), adaptive=current.adaptive, backoff_multiplier=round(new_backoff, 2), max_retries=current.max_retries ) def _is_anomaly(self, timing: ActionTiming) -> bool: """Erkennt ob ein Timing eine Anomalie darstellt""" # Hole Statistiken für Vergleich stats = self.rate_limit_service.get_statistics( timing.action_type, timeframe=timedelta(hours=1) ) if not stats or 'avg_duration_ms' not in stats: return False avg_duration = stats['avg_duration_ms'] / 1000.0 # Sehr langsame Requests sind Anomalien if timing.duration > avg_duration * self.anomaly_threshold: return True # Fehler nach mehreren Erfolgen sind Anomalien if not timing.success and stats.get('success_rate', 0) > 0.9: return True return False def _handle_anomaly(self, timing: ActionTiming) -> None: """Behandelt erkannte Anomalien""" # Sofortige Policy-Anpassung bei kritischen Anomalien if not timing.success and timing.error_message: if any(indicator in timing.error_message.lower() for indicator in ['rate limit', 'too many', 'blocked']): # Rate Limit erkannt - sofort anpassen current_policy = self.rate_limit_service.get_policy(timing.action_type) emergency_policy = RateLimitPolicy( min_delay=min(current_policy.min_delay * 2, 10.0), max_delay=min(current_policy.max_delay * 2, 30.0), adaptive=current_policy.adaptive, backoff_multiplier=min(current_policy.backoff_multiplier * 1.5, 3.0), max_retries=current_policy.max_retries ) self.rate_limit_service.update_policy(timing.action_type, emergency_policy) logger.warning(f"Emergency policy update for {timing.action_type.value} due to rate limit") def get_recommendations(self) -> Dict[str, Any]: """Gibt Empfehlungen basierend auf aktuellen Metriken""" recommendations = { 'actions': [], 'warnings': [], 'optimizations': [] } # Analysiere alle Action Types for action_type in ActionType: stats = self.rate_limit_service.get_statistics( action_type, timeframe=timedelta(hours=24) ) if not stats or stats.get('total_actions', 0) < 10: continue success_rate = stats.get('success_rate', 0) avg_retries = stats.get('avg_retry_count', 0) # Empfehlungen basierend auf Metriken if success_rate < 0.5: recommendations['warnings'].append( f"{action_type.value}: Sehr niedrige Erfolgsrate ({success_rate:.1%})" ) recommendations['actions'].append( f"Erhöhe Delays für {action_type.value} oder prüfe auf Blocking" ) if avg_retries > 2: recommendations['warnings'].append( f"{action_type.value}: Hohe Retry-Rate ({avg_retries:.1f})" ) if success_rate > 0.98 and stats.get('avg_duration_ms', 0) < 500: recommendations['optimizations'].append( f"{action_type.value}: Könnte schneller ausgeführt werden" ) return recommendations