very fast at protocol indexer with flexible filtering, xrpc queries, cursor-backed event stream, and more, built on fjall
rust fjall at-protocol atproto indexer

[api] return keyspace disk sizes in stats

ptr.pet 94a3ac85 f2e43218

verified
+43 -3
+26 -3
src/api/stats.rs
··· 6 6 #[derive(Serialize)] 7 7 pub struct StatsResponse { 8 8 pub counts: BTreeMap<&'static str, u64>, 9 + pub size: BTreeMap<&'static str, u64>, 9 10 } 10 11 11 12 pub async fn get_stats(State(state): State<Arc<AppState>>) -> Result<Json<StatsResponse>> { 12 - let db = &state.db; 13 + let db = state.db.clone(); 13 14 14 15 let mut counts: BTreeMap<&'static str, u64> = futures::future::join_all( 15 16 [ ··· 23 24 "error_generic", 24 25 ] 25 26 .into_iter() 26 - .map(|name| async move { (name, db.get_count(name).await) }), 27 + .map(|name| { 28 + let db = db.clone(); 29 + async move { (name, db.get_count(name).await) } 30 + }), 27 31 ) 28 32 .await 29 33 .into_iter() 30 34 .collect(); 31 35 // this should be accurate since we dont remove events 36 + // todo: ...unless in ephemeral mode 32 37 counts.insert("events", db.events.approximate_len() as u64); 33 38 34 - Ok(Json(StatsResponse { counts })) 39 + let size = tokio::task::spawn_blocking(move || { 40 + let mut size = BTreeMap::new(); 41 + size.insert("repos", db.repos.disk_space()); 42 + size.insert("records", db.records.disk_space()); 43 + size.insert("blocks", db.blocks.disk_space()); 44 + size.insert("cursors", db.cursors.disk_space()); 45 + size.insert("pending", db.pending.disk_space()); 46 + size.insert("resync", db.resync.disk_space()); 47 + size.insert("resync_buffer", db.resync_buffer.disk_space()); 48 + size.insert("events", db.events.disk_space()); 49 + size.insert("counts", db.counts.disk_space()); 50 + size.insert("filter", db.filter.disk_space()); 51 + size.insert("crawler", db.crawler.disk_space()); 52 + size 53 + }) 54 + .await 55 + .map_err(|_| axum::http::StatusCode::INTERNAL_SERVER_ERROR)?; 56 + 57 + Ok(Json(StatsResponse { counts, size })) 35 58 }
+4
src/db/mod.rs
··· 28 28 KeyspaceCreateOptions::default() 29 29 } 30 30 31 + #[derive(Clone)] 31 32 pub struct Db { 32 33 pub inner: Arc<Database>, 33 34 pub repos: Keyspace, ··· 230 231 .max_memtable_size(mb(16)) 231 232 .data_block_size_policy(BlockSizePolicy::all(kb(1))), 232 233 )?; 234 + 235 + // when adding new keyspaces, make sure to add them to the /stats endpoint 236 + // and also update any relevant /debug/* endpoints 233 237 234 238 let mut last_id = 0; 235 239 if let Some(guard) = events.iter().next_back() {
+13
tests/debug_endpoints.nu
··· 76 76 print "PASSED: /debug/iter on events returns JSON objects" 77 77 } 78 78 79 + # 4. Test size in /stats 80 + print "testing size in /stats" 81 + let stats = http get $"($url)/stats" 82 + if ($stats.size | is-empty) { 83 + print "FAILED: /stats returned empty size" 84 + exit 1 85 + } 86 + if not ("repos" in ($stats.size | columns)) { 87 + print "FAILED: /stats missing 'repos' in size" 88 + exit 1 89 + } 90 + print "PASSED: /stats returns keyspace sizes" 91 + 79 92 } else { 80 93 print "backfill failed" 81 94 exit 1