fixes global
This commit is contained in:
1 parent
13dc4f39c8
commit
cad0f6aebe
64 files changed
+10379
-254
No files matched your search
@@ -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()
|
||||
@@ -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
|
||||
Reference in new issue
Block a user