lightweight com.atproto.sync.listReposByCollection
45
fork

Configure Feed

Select the types of activity you want to include in your feed.

database metrics

phil 35678889 251254be

+88 -4
+15
src/main.rs
··· 277 277 if let Err(e) = storage::meta::save(&db) { 278 278 warn!(error = %e, "failed to periodically save meta stats"); 279 279 } 280 + // Emit fjall storage gauges. 281 + let ss = db.storage_stats(); 282 + metrics::gauge!("lightrail_db_disk_bytes", "keyspace" => "total") 283 + .set(ss.disk_bytes as f64); 284 + metrics::gauge!("lightrail_db_disk_bytes", "keyspace" => "default") 285 + .set(ss.default_ks_disk_bytes as f64); 286 + metrics::gauge!("lightrail_db_disk_bytes", "keyspace" => "index") 287 + .set(ss.index_ks_disk_bytes as f64); 288 + metrics::gauge!("lightrail_db_journal_count").set(ss.journal_count as f64); 289 + metrics::gauge!("lightrail_db_active_compactions") 290 + .set(ss.active_compactions as f64); 291 + metrics::gauge!("lightrail_db_compactions_completed") 292 + .set(ss.compactions_completed as f64); 293 + metrics::gauge!("lightrail_db_time_compacting_seconds") 294 + .set(ss.time_compacting.as_secs_f64()); 280 295 } 281 296 info!("meta stats done."); 282 297 Ok(())
+30 -4
src/server/admin/page.js
··· 28 28 return `${minutes}m`; 29 29 } 30 30 31 + /** Format a byte count as a human-readable string like "1.2 GB". */ 32 + function formatBytes(bytes) { 33 + if (bytes >= 1e9) return (bytes / 1e9).toFixed(2) + " GB"; 34 + if (bytes >= 1e6) return (bytes / 1e6).toFixed(1) + " MB"; 35 + if (bytes >= 1e3) return (bytes / 1e3).toFixed(1) + " KB"; 36 + return bytes + " B"; 37 + } 38 + 31 39 /** Escape a string for safe insertion into innerHTML. */ 32 40 function escapeHtml(str) { 33 41 const el = document.createElement("span"); ··· 61 69 return ` <span class="delta">${text}</span>`; 62 70 } 63 71 64 - /** Format a gauge delta as an arrow badge, e.g. "↑5" or "↓22". Returns "" if unchanged. */ 65 - function gauge(cur, prev) { 72 + /** Format a gauge delta as an arrow badge, e.g. "↑5" or "↓22". Returns "" if unchanged. 73 + * Optional `fmt` overrides the number formatter (e.g. formatBytes for byte deltas). */ 74 + function gauge(cur, prev, fmt) { 66 75 if (prev == null) return ""; 67 76 const delta = cur - prev; 68 77 if (delta === 0) return ""; 69 78 const arrow = delta > 0 ? "\u2191" : "\u2193"; 70 - return ` <span class="delta">${arrow}${formatNumber(Math.abs(delta))}</span>`; 79 + const text = fmt ? fmt(Math.abs(delta)) : formatNumber(Math.abs(delta)); 80 + return ` <span class="delta">${arrow}${text}</span>`; 71 81 } 72 82 73 83 // ── HTML fragment builders ─────────────────────────────────────────────── ··· 186 196 return card("Accounts", html); 187 197 } 188 198 199 + function renderStorageCard(status) { 200 + const p = prevSnapshot; 201 + const html = 202 + statRow("Total disk", formatBytes(status.db_disk_bytes), 203 + gauge(status.db_disk_bytes, p?.db_disk_bytes, formatBytes)) 204 + + statRow("Default keyspace", formatBytes(status.db_default_ks_disk_bytes), 205 + gauge(status.db_default_ks_disk_bytes, p?.db_default_ks_disk_bytes, formatBytes)) 206 + + statRow("Index keyspace", formatBytes(status.db_index_ks_disk_bytes), 207 + gauge(status.db_index_ks_disk_bytes, p?.db_index_ks_disk_bytes, formatBytes)) 208 + + statRow("Journal files", formatNumber(status.db_journal_count), 209 + gauge(status.db_journal_count, p?.db_journal_count)); 210 + 211 + return card("Storage", html); 212 + } 213 + 189 214 function renderDispatcherCard(dispatcher) { 190 215 const pd = prevSnapshot?.dispatcher; 191 216 let html = statRow("Workers", formatNumber(dispatcher.worker_count), ··· 290 315 // Build all cards 291 316 let html = renderIndexingCard(status) 292 317 + renderCollectionsCard(status) 293 - + renderAccountsCard(status); 318 + + renderAccountsCard(status) 319 + + renderStorageCard(status); 294 320 295 321 if (status.dispatcher) { 296 322 html += renderDispatcherCard(status.dispatcher);
+10
src/server/admin/status.rs
··· 40 40 distinct_accounts_commit_lenient: usize, 41 41 distinct_accounts_desynced: usize, 42 42 distinct_pds_hosts: usize, 43 + // ── Storage (fjall) ────────────────────────────────────────────────── 44 + db_disk_bytes: u64, 45 + db_default_ks_disk_bytes: u64, 46 + db_index_ks_disk_bytes: u64, 47 + db_journal_count: usize, 43 48 // ── Backfill progress (live from storage) ───────────────────────────── 44 49 upstream_backfill_complete: bool, 45 50 upstream_backfill_completed_at: Option<String>, ··· 104 109 let dispatcher = dispatcher_ext.map(|Extension(state)| state.lock().unwrap().clone()); 105 110 let throttled_hosts = client_ext.map(|Extension(client)| client.currently_limiting()); 106 111 112 + let ss = db.storage_stats(); 107 113 let s = &db.stats; 108 114 Ok(Json(AdminStatus { 109 115 first_startup_ms: s.first_startup_ms.load(Ordering::Relaxed), ··· 128 134 .estimate(), 129 135 distinct_accounts_desynced: s.sketch_accounts_desynced.lock().unwrap().estimate(), 130 136 distinct_pds_hosts: s.sketch_pds_hosts.lock().unwrap().estimate(), 137 + db_disk_bytes: ss.disk_bytes, 138 + db_default_ks_disk_bytes: ss.default_ks_disk_bytes, 139 + db_index_ks_disk_bytes: ss.index_ks_disk_bytes, 140 + db_journal_count: ss.journal_count, 131 141 upstream_backfill_complete: backfill 132 142 .as_ref() 133 143 .and_then(|b| b.completed_at.as_ref())
+33
src/storage/mod.rs
··· 68 68 pub(crate) stats: StatsRef, 69 69 } 70 70 71 + /// Point-in-time snapshot of fjall storage stats. 72 + pub struct StorageStats { 73 + /// Total disk space used by the database (journals + all keyspaces). 74 + pub disk_bytes: u64, 75 + /// Disk space used by the default keyspace (repo state, queues, cursors). 76 + pub default_ks_disk_bytes: u64, 77 + /// Disk space used by the index keyspace (rbc + cbr). 78 + pub index_ks_disk_bytes: u64, 79 + /// Number of journal files on disk. 80 + pub journal_count: usize, 81 + /// Number of compactions currently running. 82 + pub active_compactions: usize, 83 + /// Total compactions completed since the database was opened. 84 + pub compactions_completed: usize, 85 + /// Total time spent compacting since the database was opened. 86 + pub time_compacting: std::time::Duration, 87 + } 88 + 89 + impl Db { 90 + /// Collect a snapshot of fjall storage stats. 91 + pub fn storage_stats(&self) -> StorageStats { 92 + StorageStats { 93 + disk_bytes: self.database.disk_space().unwrap_or(0), 94 + default_ks_disk_bytes: self.ks.disk_space(), 95 + index_ks_disk_bytes: self.index_ks.disk_space(), 96 + journal_count: self.database.journal_count(), 97 + active_compactions: self.database.active_compactions(), 98 + compactions_completed: self.database.compactions_completed(), 99 + time_compacting: self.database.time_compacting(), 100 + } 101 + } 102 + } 103 + 71 104 /// Cheaply-cloneable reference to the shared database. 72 105 pub type DbRef = Arc<Db>; 73 106