|
| 1 | +//! Module to collect and display Disk Space Usage of a Profile. |
| 2 | +use crate::{context::Context, message::MsgId}; |
| 3 | +use anyhow::Result; |
| 4 | +use humansize::{BINARY, format_size}; |
| 5 | + |
| 6 | +/// Storage Usage Report |
| 7 | +/// Useful for debugging space usage problems in the deltachat database. |
| 8 | +#[derive(Debug)] |
| 9 | +pub struct StorageUsage { |
| 10 | + /// Total database size, subtract this from the backup size to estimate size of all blobs |
| 11 | + pub db_size: usize, |
| 12 | + /// size and row count of the 10 biggest tables |
| 13 | + pub largest_tables: Vec<(String, usize, Option<usize>)>, |
| 14 | + /// count and total size of status updates |
| 15 | + /// for the 10 webxdc apps with the most size usage in status updates |
| 16 | + pub largest_webxdc_data: Vec<(MsgId, usize, usize)>, |
| 17 | +} |
| 18 | + |
| 19 | +impl std::fmt::Display for StorageUsage { |
| 20 | + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 21 | + writeln!(f, "Storage Usage:")?; |
| 22 | + let human_db_size = format_size(self.db_size, BINARY); |
| 23 | + writeln!(f, "[Database Size]: {human_db_size}")?; |
| 24 | + writeln!(f, "[Largest Tables]:")?; |
| 25 | + for (name, size, row_count) in &self.largest_tables { |
| 26 | + let human_table_size = format_size(*size, BINARY); |
| 27 | + writeln!( |
| 28 | + f, |
| 29 | + " {name:<20} {human_table_size:>10}, {row_count:>6} rows", |
| 30 | + name = format!("{name}:"), |
| 31 | + row_count = row_count.map(|c| c.to_string()).unwrap_or("?".to_owned()) |
| 32 | + )?; |
| 33 | + } |
| 34 | + writeln!(f, "[Webxdc With Biggest Status Update Space Usage]:")?; |
| 35 | + for (msg_id, size, update_count) in &self.largest_webxdc_data { |
| 36 | + let human_size = format_size(*size, BINARY); |
| 37 | + writeln!( |
| 38 | + f, |
| 39 | + " {msg_id:<8} {human_size:>10} across {update_count:>5} updates", |
| 40 | + msg_id = format!("{msg_id}:") |
| 41 | + )?; |
| 42 | + } |
| 43 | + Ok(()) |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +/// Get storage usage information for the Context's database |
| 48 | +pub async fn get_storage_usage(ctx: &Context) -> Result<StorageUsage> { |
| 49 | + let page_size: usize = ctx |
| 50 | + .sql |
| 51 | + .query_get_value("PRAGMA page_size", ()) |
| 52 | + .await? |
| 53 | + .unwrap_or_default(); |
| 54 | + let page_count: usize = ctx |
| 55 | + .sql |
| 56 | + .query_get_value("PRAGMA page_count", ()) |
| 57 | + .await? |
| 58 | + .unwrap_or_default(); |
| 59 | + |
| 60 | + let mut largest_tables = ctx |
| 61 | + .sql |
| 62 | + .query_map_vec( |
| 63 | + "SELECT name, |
| 64 | + SUM(pgsize) AS size |
| 65 | + FROM dbstat |
| 66 | + WHERE name IN (SELECT name FROM sqlite_master WHERE type='table') |
| 67 | + GROUP BY name ORDER BY size DESC LIMIT 10", |
| 68 | + (), |
| 69 | + |row| { |
| 70 | + let name: String = row.get(0)?; |
| 71 | + let size: usize = row.get(1)?; |
| 72 | + Ok((name, size, None)) |
| 73 | + }, |
| 74 | + ) |
| 75 | + .await?; |
| 76 | + |
| 77 | + for row in &mut largest_tables { |
| 78 | + let name = &row.0; |
| 79 | + let row_count: Result<Option<usize>> = ctx |
| 80 | + .sql |
| 81 | + // SECURITY: the table name comes from the db, not from the user |
| 82 | + .query_get_value(&format!("SELECT COUNT(*) FROM {name}"), ()) |
| 83 | + .await; |
| 84 | + row.2 = row_count.unwrap_or_default(); |
| 85 | + } |
| 86 | + |
| 87 | + let largest_webxdc_data = ctx |
| 88 | + .sql |
| 89 | + .query_map_vec( |
| 90 | + "SELECT msg_id, SUM(length(update_item)) as size, COUNT(*) as update_count |
| 91 | + FROM msgs_status_updates |
| 92 | + GROUP BY msg_id ORDER BY size DESC LIMIT 10", |
| 93 | + (), |
| 94 | + |row| { |
| 95 | + let msg_id: MsgId = row.get(0)?; |
| 96 | + let size: usize = row.get(1)?; |
| 97 | + let count: usize = row.get(2)?; |
| 98 | + |
| 99 | + Ok((msg_id, size, count)) |
| 100 | + }, |
| 101 | + ) |
| 102 | + .await?; |
| 103 | + |
| 104 | + Ok(StorageUsage { |
| 105 | + db_size: page_size * page_count, |
| 106 | + largest_tables, |
| 107 | + largest_webxdc_data, |
| 108 | + }) |
| 109 | +} |
0 commit comments