Initial commit
Dieser Commit ist enthalten in:
221
application/use_cases/adaptive_rate_limit_use_case.py
Normale Datei
221
application/use_cases/adaptive_rate_limit_use_case.py
Normale Datei
@ -0,0 +1,221 @@
|
||||
"""
|
||||
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
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren