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,151 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
import hashlib
|
||||
import json
|
||||
from dataclasses import dataclass, field, asdict
|
||||
from typing import Dict, Any, Optional, List
|
||||
|
||||
|
||||
def _now_ts() -> int:
|
||||
return int(time.time())
|
||||
|
||||
|
||||
def _gen_nonce(prefix: str = "stats") -> str:
|
||||
base = f"{prefix}:{_now_ts()}:{time.time_ns()}"
|
||||
return hashlib.sha256(base.encode("utf-8")).hexdigest()[:16]
|
||||
|
||||
|
||||
@dataclass
|
||||
class SystemMetrics:
|
||||
cpu_percent: Optional[float] = None
|
||||
cpu_load_avg_1m: Optional[float] = None
|
||||
cpu_load_avg_5m: Optional[float] = None
|
||||
cpu_load_avg_15m: Optional[float] = None
|
||||
|
||||
mem_total_mb: Optional[float] = None
|
||||
mem_used_mb: Optional[float] = None
|
||||
mem_available_mb: Optional[float] = None
|
||||
mem_percent: Optional[float] = None
|
||||
|
||||
disk_total_mb: Optional[float] = None
|
||||
disk_used_mb: Optional[float] = None
|
||||
disk_free_mb: Optional[float] = None
|
||||
disk_percent: Optional[float] = None
|
||||
|
||||
io_read_mb_s: Optional[float] = None
|
||||
io_write_mb_s: Optional[float] = None
|
||||
|
||||
net_sent_kb_s: Optional[float] = None
|
||||
net_recv_kb_s: Optional[float] = None
|
||||
|
||||
uptime_seconds: Optional[int] = None
|
||||
timestamp: int = field(default_factory=_now_ts)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return asdict(self)
|
||||
|
||||
@staticmethod
|
||||
def from_dict(data: Dict[str, Any]) -> "SystemMetrics":
|
||||
return SystemMetrics(**data)
|
||||
|
||||
|
||||
@dataclass
|
||||
class AppMetrics:
|
||||
total_conversions: int = 0
|
||||
total_requests: int = 0
|
||||
total_errors: int = 0
|
||||
slow_ops_count: int = 0
|
||||
avg_response_ms: Optional[float] = None
|
||||
p95_response_ms: Optional[float] = None
|
||||
p99_response_ms: Optional[float] = None
|
||||
details: Dict[str, Any] = field(default_factory=dict)
|
||||
timestamp: int = field(default_factory=_now_ts)
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return asdict(self)
|
||||
|
||||
@staticmethod
|
||||
def from_dict(data: Dict[str, Any]) -> "AppMetrics":
|
||||
return AppMetrics(**data)
|
||||
|
||||
|
||||
@dataclass
|
||||
class NodeStats:
|
||||
node_id: str
|
||||
public_key: str
|
||||
system: SystemMetrics
|
||||
app: AppMetrics
|
||||
known_content_items: Optional[int] = None
|
||||
available_content_items: Optional[int] = None
|
||||
|
||||
protocol_version: str = "stats-gossip-v1"
|
||||
timestamp: int = field(default_factory=_now_ts)
|
||||
nonce: str = field(default_factory=_gen_nonce)
|
||||
signature: Optional[str] = None # ed25519
|
||||
|
||||
def to_dict(self, include_signature: bool = True) -> Dict[str, Any]:
|
||||
data = {
|
||||
"node_id": self.node_id,
|
||||
"public_key": self.public_key,
|
||||
"system": self.system.to_dict(),
|
||||
"app": self.app.to_dict(),
|
||||
"known_content_items": self.known_content_items,
|
||||
"available_content_items": self.available_content_items,
|
||||
"protocol_version": self.protocol_version,
|
||||
"timestamp": self.timestamp,
|
||||
"nonce": self.nonce,
|
||||
}
|
||||
if include_signature:
|
||||
data["signature"] = self.signature
|
||||
return data
|
||||
|
||||
@staticmethod
|
||||
def canonical_payload(data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
# Для подписи удаляем signature и сортируем
|
||||
payload = dict(data)
|
||||
payload.pop("signature", None)
|
||||
return payload
|
||||
|
||||
@staticmethod
|
||||
def to_signable_json(data: Dict[str, Any]) -> str:
|
||||
payload = NodeStats.canonical_payload(data)
|
||||
return json.dumps(payload, sort_keys=True, ensure_ascii=False)
|
||||
|
||||
@staticmethod
|
||||
def from_dict(data: Dict[str, Any]) -> "NodeStats":
|
||||
return NodeStats(
|
||||
node_id=data["node_id"],
|
||||
public_key=data["public_key"],
|
||||
system=SystemMetrics.from_dict(data["system"]),
|
||||
app=AppMetrics.from_dict(data["app"]),
|
||||
known_content_items=data.get("known_content_items"),
|
||||
available_content_items=data.get("available_content_items"),
|
||||
protocol_version=data.get("protocol_version", "stats-gossip-v1"),
|
||||
timestamp=data.get("timestamp", _now_ts()),
|
||||
nonce=data.get("nonce", _gen_nonce()),
|
||||
signature=data.get("signature"),
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
class NetworkStats:
|
||||
# Сводная статистика по сети
|
||||
node_count: int
|
||||
active_nodes: int
|
||||
avg_uptime_seconds: Optional[float] = None
|
||||
avg_cpu_percent: Optional[float] = None
|
||||
avg_mem_percent: Optional[float] = None
|
||||
avg_latency_ms: Optional[float] = None
|
||||
total_available_content: Optional[int] = None
|
||||
health_score: Optional[float] = None # 0..100
|
||||
|
||||
timestamp: int = field(default_factory=_now_ts)
|
||||
nodes: List[Dict[str, Any]] = field(default_factory=list) # список упрощенных NodeStats резюме
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
return asdict(self)
|
||||
|
||||
@staticmethod
|
||||
def from_dict(data: Dict[str, Any]) -> "NetworkStats":
|
||||
return NetworkStats(**data)
|
||||
Reference in new issue
Block a user