core/auth/tokens/monitoring.py

190 lines
6.9 KiB
Python
Raw Normal View History

2025-06-02 18:50:58 +00:00
"""
Статистика и мониторинг системы токенов
"""
import asyncio
from typing import Any, Dict
from services.redis import redis as redis_adapter
from utils.logger import root_logger as logger
from .base import BaseTokenManager
from .types import SCAN_BATCH_SIZE
class TokenMonitoring(BaseTokenManager):
"""
Класс для мониторинга и статистики токенов
"""
async def get_token_statistics(self) -> Dict[str, Any]:
"""
Получает статистику по токенам для мониторинга
Returns:
Dict: Статистика токенов
"""
stats = {
"session_tokens": 0,
"verification_tokens": 0,
"oauth_access_tokens": 0,
"oauth_refresh_tokens": 0,
"user_sessions": 0,
"memory_usage": 0,
}
try:
# Считаем токены по типам используя SCAN
patterns = {
"session_tokens": "session:*",
"verification_tokens": "verification_token:*",
"oauth_access_tokens": "oauth_access:*",
"oauth_refresh_tokens": "oauth_refresh:*",
"user_sessions": "user_sessions:*",
}
count_tasks = [self._count_keys_by_pattern(pattern) for pattern in patterns.values()]
counts = await asyncio.gather(*count_tasks)
for (stat_name, _), count in zip(patterns.items(), counts):
stats[stat_name] = count
# Получаем информацию о памяти Redis
memory_info = await redis_adapter.execute("INFO", "MEMORY")
stats["memory_usage"] = memory_info.get("used_memory", 0)
except Exception as e:
logger.error(f"Ошибка получения статистики токенов: {e}")
return stats
async def _count_keys_by_pattern(self, pattern: str) -> int:
"""Подсчет ключей по паттерну используя SCAN"""
count = 0
cursor = 0
while True:
cursor, keys = await redis_adapter.execute("scan", cursor, pattern, SCAN_BATCH_SIZE)
count += len(keys)
if cursor == 0:
break
return count
async def optimize_memory_usage(self) -> Dict[str, Any]:
"""
Оптимизирует использование памяти Redis
Returns:
Dict: Результаты оптимизации
"""
results = {"cleaned_expired": 0, "optimized_structures": 0, "memory_saved": 0}
try:
# Очищаем истекшие токены
from .batch import BatchTokenOperations
batch_ops = BatchTokenOperations()
cleaned = await batch_ops.cleanup_expired_tokens()
results["cleaned_expired"] = cleaned
# Оптимизируем структуры данных
optimized = await self._optimize_data_structures()
results["optimized_structures"] = optimized
# Запускаем сборку мусора Redis
await redis_adapter.execute("MEMORY", "PURGE")
logger.info(f"Оптимизация памяти завершена: {results}")
except Exception as e:
logger.error(f"Ошибка оптимизации памяти: {e}")
return results
async def _optimize_data_structures(self) -> int:
"""Оптимизирует структуры данных Redis"""
optimized_count = 0
cursor = 0
# Оптимизируем пользовательские списки сессий
while True:
cursor, keys = await redis_adapter.execute("scan", cursor, "user_sessions:*", SCAN_BATCH_SIZE)
for key in keys:
try:
# Проверяем размер множества
size = await redis_adapter.execute("scard", key)
if size == 0:
await redis_adapter.delete(key)
optimized_count += 1
elif size > 100: # Слишком много сессий у одного пользователя
# Оставляем только последние 50 сессий
members = await redis_adapter.execute("smembers", key)
if len(members) > 50:
members_list = list(members)
to_remove = members_list[:-50]
if to_remove:
await redis_adapter.srem(key, *to_remove)
optimized_count += len(to_remove)
except Exception as e:
logger.error(f"Ошибка оптимизации ключа {key}: {e}")
continue
if cursor == 0:
break
return optimized_count
async def health_check(self) -> Dict[str, Any]:
"""
Проверка здоровья системы токенов
Returns:
Dict: Результаты проверки
"""
health: Dict[str, Any] = {
"status": "healthy",
"redis_connected": False,
"token_operations": False,
"errors": [],
}
try:
# Проверяем подключение к Redis
await redis_adapter.ping()
health["redis_connected"] = True
# Тестируем основные операции с токенами
from .sessions import SessionTokenManager
session_manager = SessionTokenManager()
test_user_id = "health_check_user"
test_token = await session_manager.create_session(test_user_id)
if test_token:
# Проверяем валидацию
valid, _ = await session_manager.validate_session_token(test_token)
if valid:
# Проверяем отзыв
revoked = await session_manager.revoke_session_token(test_token)
if revoked:
health["token_operations"] = True
else:
health["errors"].append("Failed to revoke test token") # type: ignore[misc]
else:
health["errors"].append("Failed to validate test token") # type: ignore[misc]
else:
health["errors"].append("Failed to create test token") # type: ignore[misc]
except Exception as e:
health["errors"].append(f"Health check error: {e}") # type: ignore[misc]
if health["errors"]:
health["status"] = "unhealthy"
return health