hierottu GPU-tukea
This commit is contained in:
272
network-poc/hub/src/db.rs
Normal file
272
network-poc/hub/src/db.rs
Normal file
@@ -0,0 +1,272 @@
|
||||
use rusqlite::{Connection, params};
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub struct NodeDb {
|
||||
conn: Mutex<Connection>,
|
||||
}
|
||||
|
||||
impl NodeDb {
|
||||
pub fn new(path: &str) -> Self {
|
||||
let conn = Connection::open(path).expect("SQLite-tietokantaa ei voitu avata");
|
||||
|
||||
conn.execute_batch("
|
||||
CREATE TABLE IF NOT EXISTS node_sessions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_id INTEGER NOT NULL,
|
||||
ip TEXT NOT NULL,
|
||||
node_type TEXT NOT NULL DEFAULT 'browser',
|
||||
connected_at TEXT NOT NULL,
|
||||
disconnected_at TEXT,
|
||||
|
||||
-- Järjestelmätiedot
|
||||
platform TEXT,
|
||||
hostname TEXT,
|
||||
os TEXT,
|
||||
cpu_cores INTEGER,
|
||||
cpu_model TEXT,
|
||||
ram_mb INTEGER,
|
||||
|
||||
-- GPU
|
||||
gpu_name TEXT,
|
||||
gpu_vendor TEXT,
|
||||
gpu_backend TEXT,
|
||||
vram_total_mb INTEGER,
|
||||
vram_used_mb INTEGER,
|
||||
gpu_temp_c INTEGER,
|
||||
gpu_util_pct INTEGER,
|
||||
|
||||
-- Varaus
|
||||
allocated_gb INTEGER,
|
||||
|
||||
-- WebGPU-tuki
|
||||
has_webgpu BOOLEAN,
|
||||
|
||||
-- Tehtävätilastot
|
||||
tasks_completed INTEGER DEFAULT 0
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS pair_results (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
node_id INTEGER NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
en_text TEXT,
|
||||
fi_text TEXT,
|
||||
en_tokens INTEGER,
|
||||
fi_tokens INTEGER,
|
||||
en_chars_per_token REAL,
|
||||
fi_chars_per_token REAL,
|
||||
overhead_pct REAL,
|
||||
duration_ms INTEGER
|
||||
);
|
||||
").expect("Tietokantataulujen luonti epäonnistui");
|
||||
|
||||
NodeDb { conn: Mutex::new(conn) }
|
||||
}
|
||||
|
||||
pub fn insert_session(
|
||||
&self,
|
||||
node_id: u64,
|
||||
ip: &str,
|
||||
node_type: &str,
|
||||
auth_data: &serde_json::Value,
|
||||
) -> i64 {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let now = chrono::Utc::now().to_rfc3339();
|
||||
|
||||
// Selainsolmun tiedot
|
||||
let platform = auth_data.get("platform").and_then(|v| v.as_str());
|
||||
let cpu_cores = auth_data.get("cpu_cores").and_then(|v| v.as_u64());
|
||||
let ram = auth_data.get("device_memory_gb").and_then(|v| v.as_f64()).map(|v| (v * 1024.0) as i64);
|
||||
let allocated = auth_data.get("allocated_gb").and_then(|v| v.as_u64());
|
||||
|
||||
// GPU (selain)
|
||||
let gpu_vendor = auth_data.get("gpu").and_then(|g| g.get("vendor")).and_then(|v| v.as_str());
|
||||
let gpu_desc = auth_data.get("gpu").and_then(|g| g.get("description")).and_then(|v| v.as_str());
|
||||
let gpu_backend = if gpu_vendor.is_some() { Some("WebGPU") } else { None };
|
||||
let has_webgpu = gpu_vendor.is_some();
|
||||
|
||||
// Natiivi-solmun tiedot
|
||||
let sys = auth_data.get("system");
|
||||
let hostname = sys.and_then(|s| s.get("hostname")).and_then(|v| v.as_str());
|
||||
let os = sys.and_then(|s| s.get("os")).and_then(|v| v.as_str());
|
||||
let native_cores = sys.and_then(|s| s.get("cpu_cores")).and_then(|v| v.as_u64());
|
||||
let cpu_model = sys.and_then(|s| s.get("cpu_model")).and_then(|v| v.as_str());
|
||||
let native_ram = sys.and_then(|s| s.get("ram_total_mb")).and_then(|v| v.as_u64());
|
||||
|
||||
// GPU (natiivi — ensimmäinen GPU)
|
||||
let gpu = auth_data.get("gpus").and_then(|v| v.as_array()).and_then(|a| a.first());
|
||||
let native_gpu_name = gpu.and_then(|g| g.get("name")).and_then(|v| v.as_str());
|
||||
let native_gpu_vendor = gpu.and_then(|g| g.get("vendor")).and_then(|v| v.as_str());
|
||||
let native_gpu_backend = gpu.and_then(|g| g.get("backend")).and_then(|v| v.as_str());
|
||||
let vram_total = gpu.and_then(|g| g.get("vram_total_mb")).and_then(|v| v.as_u64());
|
||||
let vram_used = gpu.and_then(|g| g.get("vram_used_mb")).and_then(|v| v.as_u64());
|
||||
let gpu_temp = gpu.and_then(|g| g.get("temperature_c")).and_then(|v| v.as_u64());
|
||||
let gpu_util = gpu.and_then(|g| g.get("gpu_util_pct")).and_then(|v| v.as_u64());
|
||||
|
||||
conn.execute(
|
||||
"INSERT INTO node_sessions (
|
||||
node_id, ip, node_type, connected_at,
|
||||
platform, hostname, os, cpu_cores, cpu_model, ram_mb,
|
||||
gpu_name, gpu_vendor, gpu_backend, vram_total_mb, vram_used_mb, gpu_temp_c, gpu_util_pct,
|
||||
allocated_gb, has_webgpu
|
||||
) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12,?13,?14,?15,?16,?17,?18,?19)",
|
||||
params![
|
||||
node_id as i64, ip, node_type, now,
|
||||
platform, hostname, os,
|
||||
native_cores.or(cpu_cores).map(|v| v as i64),
|
||||
cpu_model,
|
||||
native_ram.map(|v| v as i64).or(ram),
|
||||
native_gpu_name.or(gpu_desc),
|
||||
native_gpu_vendor.or(gpu_vendor),
|
||||
native_gpu_backend.or(gpu_backend),
|
||||
vram_total.map(|v| v as i64),
|
||||
vram_used.map(|v| v as i64),
|
||||
gpu_temp.map(|v| v as i64),
|
||||
gpu_util.map(|v| v as i64),
|
||||
allocated.map(|v| v as i64),
|
||||
has_webgpu,
|
||||
],
|
||||
).expect("Session insert epäonnistui");
|
||||
|
||||
conn.last_insert_rowid()
|
||||
}
|
||||
|
||||
pub fn close_session(&self, node_id: u64) {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let now = chrono::Utc::now().to_rfc3339();
|
||||
let _ = conn.execute(
|
||||
"UPDATE node_sessions SET disconnected_at = ?1 WHERE node_id = ?2 AND disconnected_at IS NULL",
|
||||
params![now, node_id as i64],
|
||||
);
|
||||
}
|
||||
|
||||
pub fn increment_tasks(&self, node_id: u64) {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let _ = conn.execute(
|
||||
"UPDATE node_sessions SET tasks_completed = tasks_completed + 1 WHERE node_id = ?1 AND disconnected_at IS NULL",
|
||||
params![node_id as i64],
|
||||
);
|
||||
}
|
||||
|
||||
pub fn get_sessions(&self, limit: u32) -> Vec<serde_json::Value> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT id, node_id, ip, node_type, connected_at, disconnected_at,
|
||||
platform, hostname, os, cpu_cores, cpu_model, ram_mb,
|
||||
gpu_name, gpu_vendor, gpu_backend, vram_total_mb, gpu_temp_c, gpu_util_pct,
|
||||
allocated_gb, has_webgpu, tasks_completed
|
||||
FROM node_sessions ORDER BY id DESC LIMIT ?1"
|
||||
).unwrap();
|
||||
|
||||
stmt.query_map(params![limit], |row| {
|
||||
Ok(serde_json::json!({
|
||||
"id": row.get::<_, i64>(0)?,
|
||||
"node_id": row.get::<_, i64>(1)?,
|
||||
"ip": row.get::<_, String>(2)?,
|
||||
"node_type": row.get::<_, String>(3)?,
|
||||
"connected_at": row.get::<_, String>(4)?,
|
||||
"disconnected_at": row.get::<_, Option<String>>(5)?,
|
||||
"platform": row.get::<_, Option<String>>(6)?,
|
||||
"hostname": row.get::<_, Option<String>>(7)?,
|
||||
"os": row.get::<_, Option<String>>(8)?,
|
||||
"cpu_cores": row.get::<_, Option<i64>>(9)?,
|
||||
"cpu_model": row.get::<_, Option<String>>(10)?,
|
||||
"ram_mb": row.get::<_, Option<i64>>(11)?,
|
||||
"gpu_name": row.get::<_, Option<String>>(12)?,
|
||||
"gpu_vendor": row.get::<_, Option<String>>(13)?,
|
||||
"gpu_backend": row.get::<_, Option<String>>(14)?,
|
||||
"vram_total_mb": row.get::<_, Option<i64>>(15)?,
|
||||
"gpu_temp_c": row.get::<_, Option<i64>>(16)?,
|
||||
"gpu_util_pct": row.get::<_, Option<i64>>(17)?,
|
||||
"allocated_gb": row.get::<_, Option<i64>>(18)?,
|
||||
"has_webgpu": row.get::<_, Option<bool>>(19)?,
|
||||
"tasks_completed": row.get::<_, i64>(20)?,
|
||||
}))
|
||||
}).unwrap().filter_map(|r| r.ok()).collect()
|
||||
}
|
||||
|
||||
pub fn get_pair_results(&self, limit: u32) -> Vec<serde_json::Value> {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let mut stmt = conn.prepare(
|
||||
"SELECT id, node_id, created_at, en_text, fi_text,
|
||||
en_tokens, fi_tokens, en_chars_per_token, fi_chars_per_token,
|
||||
overhead_pct, duration_ms
|
||||
FROM pair_results ORDER BY id DESC LIMIT ?1"
|
||||
).unwrap();
|
||||
|
||||
stmt.query_map(params![limit], |row| {
|
||||
Ok(serde_json::json!({
|
||||
"id": row.get::<_, i64>(0)?,
|
||||
"node_id": row.get::<_, i64>(1)?,
|
||||
"created_at": row.get::<_, String>(2)?,
|
||||
"en_text": row.get::<_, Option<String>>(3)?,
|
||||
"fi_text": row.get::<_, Option<String>>(4)?,
|
||||
"en_tokens": row.get::<_, Option<i64>>(5)?,
|
||||
"fi_tokens": row.get::<_, Option<i64>>(6)?,
|
||||
"en_chars_per_token": row.get::<_, Option<f64>>(7)?,
|
||||
"fi_chars_per_token": row.get::<_, Option<f64>>(8)?,
|
||||
"overhead_pct": row.get::<_, Option<f64>>(9)?,
|
||||
"duration_ms": row.get::<_, Option<i64>>(10)?,
|
||||
}))
|
||||
}).unwrap().filter_map(|r| r.ok()).collect()
|
||||
}
|
||||
|
||||
pub fn get_stats(&self) -> serde_json::Value {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
|
||||
let total_sessions: i64 = conn.query_row("SELECT COUNT(*) FROM node_sessions", [], |r| r.get(0)).unwrap_or(0);
|
||||
let active_sessions: i64 = conn.query_row("SELECT COUNT(*) FROM node_sessions WHERE disconnected_at IS NULL", [], |r| r.get(0)).unwrap_or(0);
|
||||
let total_pairs: i64 = conn.query_row("SELECT COUNT(*) FROM pair_results", [], |r| r.get(0)).unwrap_or(0);
|
||||
let avg_overhead: f64 = conn.query_row("SELECT COALESCE(AVG(overhead_pct), 0) FROM pair_results", [], |r| r.get(0)).unwrap_or(0.0);
|
||||
let avg_en_cpt: f64 = conn.query_row("SELECT COALESCE(AVG(en_chars_per_token), 0) FROM pair_results", [], |r| r.get(0)).unwrap_or(0.0);
|
||||
let avg_fi_cpt: f64 = conn.query_row("SELECT COALESCE(AVG(fi_chars_per_token), 0) FROM pair_results", [], |r| r.get(0)).unwrap_or(0.0);
|
||||
|
||||
let webgpu_count: i64 = conn.query_row("SELECT COUNT(*) FROM node_sessions WHERE has_webgpu = 1", [], |r| r.get(0)).unwrap_or(0);
|
||||
let cpu_fallback_count: i64 = conn.query_row("SELECT COUNT(*) FROM node_sessions WHERE has_webgpu = 0 OR has_webgpu IS NULL", [], |r| r.get(0)).unwrap_or(0);
|
||||
|
||||
let unique_ips: i64 = conn.query_row("SELECT COUNT(DISTINCT ip) FROM node_sessions", [], |r| r.get(0)).unwrap_or(0);
|
||||
|
||||
serde_json::json!({
|
||||
"total_sessions": total_sessions,
|
||||
"active_sessions": active_sessions,
|
||||
"unique_ips": unique_ips,
|
||||
"total_pairs": total_pairs,
|
||||
"avg_overhead_pct": (avg_overhead * 10.0).round() / 10.0,
|
||||
"avg_en_chars_per_token": (avg_en_cpt * 100.0).round() / 100.0,
|
||||
"avg_fi_chars_per_token": (avg_fi_cpt * 100.0).round() / 100.0,
|
||||
"webgpu_sessions": webgpu_count,
|
||||
"cpu_fallback_sessions": cpu_fallback_count,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn insert_pair_result(
|
||||
&self,
|
||||
node_id: u64,
|
||||
en: &serde_json::Value,
|
||||
fi: &serde_json::Value,
|
||||
overhead: f64,
|
||||
duration_ms: u64,
|
||||
) {
|
||||
let conn = self.conn.lock().unwrap();
|
||||
let now = chrono::Utc::now().to_rfc3339();
|
||||
let _ = conn.execute(
|
||||
"INSERT INTO pair_results (
|
||||
node_id, created_at, en_text, fi_text,
|
||||
en_tokens, fi_tokens, en_chars_per_token, fi_chars_per_token,
|
||||
overhead_pct, duration_ms
|
||||
) VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10)",
|
||||
params![
|
||||
node_id as i64,
|
||||
now,
|
||||
en.get("text").and_then(|v| v.as_str()),
|
||||
fi.get("text").and_then(|v| v.as_str()),
|
||||
en.get("token_count").and_then(|v| v.as_u64()).map(|v| v as i64),
|
||||
fi.get("token_count").and_then(|v| v.as_u64()).map(|v| v as i64),
|
||||
en.get("chars_per_token").and_then(|v| v.as_f64()),
|
||||
fi.get("chars_per_token").and_then(|v| v.as_f64()),
|
||||
overhead,
|
||||
duration_ms as i64,
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user