Files
AccountForger-neuerUpload/application/use_cases/adaptive_rate_limit_use_case.py
Claude Project Manager 04585e95b6 Initial commit
2025-08-01 23:50:28 +02:00

221 Zeilen
8.7 KiB
Python

"""
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