fixes global

This commit is contained in:
user committed 2025-08-08 09:14:18 +03:00
1 parent 13dc4f39c8
commit cad0f6aebe
64 files changed
+10379 -254

No files matched your search

+94
View File
@@ -0,0 +1,94 @@
from __future__ import annotations
import asyncio
import logging
from typing import Optional, Dict
from app.core.converter.conversion_manager import ConversionManager
from app.core.models.converter.conversion_models import ConversionStatus, ConversionResult
logger = logging.getLogger(__name__)
class ConversionDaemon:
"""
Фоновый обработчик очереди конвертации.
Запускает планировщик, мониторит активные задачи и выполняет очистку завершённых.
"""
def __init__(self, manager: Optional[ConversionManager] = None) -> None:
self._manager = manager or ConversionManager()
self._shutdown = asyncio.Event()
self._monitor_interval = 2.0
self._cleanup_interval = 60.0
# локальное состояние для мониторинга
self._last_status: Dict[str, str] = {}
async def process_queue(self) -> None:
"""
Главный цикл планировщика: извлекает задачи из очереди и запускает обработку.
"""
logger.info("ConversionDaemon: starting scheduler loop")
try:
await self._manager.run_scheduler(self._shutdown)
except asyncio.CancelledError:
logger.info("ConversionDaemon: scheduler cancelled")
except Exception as e:
logger.exception("ConversionDaemon: scheduler error: %s", e)
async def monitor_conversions(self) -> None:
"""
Мониторинг статусов задач для логов и метрик.
"""
logger.info("ConversionDaemon: starting monitor loop")
try:
while not self._shutdown.is_set():
# Здесь можно подключить внешний реестр задач, если потребуется
# В текущей реализации ConversionManager хранит результаты локально.
# Логика мониторинга будет простой: статусы будут проверяться по известным task_id,
# которые могли бы сохраняться в каком-либо реестре. Для демо делаем заглушку.
await asyncio.sleep(self._monitor_interval)
except asyncio.CancelledError:
logger.info("ConversionDaemon: monitor cancelled")
except Exception as e:
logger.exception("ConversionDaemon: monitor error: %s", e)
async def cleanup_completed(self) -> None:
"""
Периодическая очистка ресурсов (логи/временные файлы) по завершённым задачам.
"""
logger.info("ConversionDaemon: starting cleanup loop")
try:
while not self._shutdown.is_set():
# В этой версии упрощённо ничего не чистим, т.к. хранение файлов управляется извне.
# Точку расширения оставляем для будущего: удаление временных входных/выходных файлов.
await asyncio.sleep(self._cleanup_interval)
except asyncio.CancelledError:
logger.info("ConversionDaemon: cleanup cancelled")
except Exception as e:
logger.exception("ConversionDaemon: cleanup error: %s", e)
async def run(self) -> None:
"""
Запускает три корутины: планировщик, монитор, очистку.
"""
logger.info("ConversionDaemon: run()")
tasks = [
asyncio.create_task(self.process_queue()),
asyncio.create_task(self.monitor_conversions()),
asyncio.create_task(self.cleanup_completed()),
]
try:
await asyncio.gather(*tasks)
finally:
for t in tasks:
if not t.done():
t.cancel()
def stop(self) -> None:
"""
Инициирует завершение фоновых задач.
"""
logger.info("ConversionDaemon: stop() called")
self._shutdown.set()
+122
View File
@@ -0,0 +1,122 @@
from __future__ import annotations
import asyncio
import logging
import os
from typing import Callable, Awaitable, List, Optional
from app.core.crypto import get_ed25519_manager
from app.core.models.stats.metrics_models import NodeStats
from app.core.stats.metrics_collector import MetricsCollector
from app.core.stats.stats_aggregator import StatsAggregator
from app.core.stats.gossip_manager import GossipManager
logger = logging.getLogger(__name__)
class StatsDaemon:
"""
Фоновый сервис статистики:
- периодически собирает локальные метрики
- сохраняет в агрегатор
- периодически рассылает gossip статистику пирам
"""
def __init__(
self,
collector: Optional[MetricsCollector] = None,
aggregator: Optional[StatsAggregator] = None,
gossip: Optional[GossipManager] = None,
collect_interval_sec: int = 10,
gossip_interval_sec: int = 30,
peers_provider: Optional[Callable[[], Awaitable[List[str]]]] = None,
) -> None:
self.collector = collector or MetricsCollector()
self.aggregator = aggregator or StatsAggregator()
self.gossip = gossip or GossipManager()
self.collect_interval_sec = max(1, collect_interval_sec)
self.gossip_interval_sec = max(5, gossip_interval_sec)
self.peers_provider = peers_provider
self._collect_task: Optional[asyncio.Task] = None
self._gossip_task: Optional[asyncio.Task] = None
self._stopping = asyncio.Event()
async def start(self) -> None:
logger.info("StatsDaemon starting")
self._stopping.clear()
self._collect_task = asyncio.create_task(self.periodic_collection(), name="stats_collect_loop")
self._gossip_task = asyncio.create_task(self.periodic_gossip(), name="stats_gossip_loop")
logger.info("StatsDaemon started")
async def stop(self) -> None:
logger.info("StatsDaemon stopping")
self._stopping.set()
tasks = [t for t in [self._collect_task, self._gossip_task] if t]
for t in tasks:
t.cancel()
for t in tasks:
try:
await t
except asyncio.CancelledError:
pass
except Exception as e:
logger.warning("StatsDaemon task stop error: %s", e)
logger.info("StatsDaemon stopped")
async def periodic_collection(self) -> None:
"""
Периодический сбор локальных метрик и сохранение в агрегатор.
"""
crypto = get_ed25519_manager()
node_id = crypto.node_id
public_key = crypto.public_key_hex
while not self._stopping.is_set():
try:
system, app = await self.collector.get_current_stats()
# можно дополнить доступным контентом из локального индекса, пока None
node_stats = NodeStats(
node_id=node_id,
public_key=public_key,
system=system,
app=app,
known_content_items=None,
available_content_items=None,
)
await self.aggregator.add_local_snapshot(node_stats)
except Exception as e:
logger.exception("periodic_collection error: %s", e)
try:
await asyncio.wait_for(self._stopping.wait(), timeout=self.collect_interval_sec)
except asyncio.TimeoutError:
continue
async def periodic_gossip(self) -> None:
"""
Периодическая рассылка статистики пирам.
"""
while not self._stopping.is_set():
try:
# peers
peers: List[str] = []
if self.peers_provider:
try:
peers = await self.peers_provider()
await self.aggregator.set_known_peers(peers)
except Exception as e:
logger.warning("peers_provider error: %s", e)
latest = await self.aggregator.get_latest_local()
if latest and peers:
# подписать актуальный слепок
signed_stats = await self.aggregator.build_local_signed_stats()
await self.gossip.broadcast_stats(peers, signed_stats)
except Exception as e:
logger.exception("periodic_gossip error: %s", e)
try:
await asyncio.wait_for(self._stopping.wait(), timeout=self.gossip_interval_sec)
except asyncio.TimeoutError:
continue