Skip to main content

max / audiofiles

4.7 KB · 102 lines History Blame Raw
1 //! Storage accounting: the counts and byte totals the sync panel prices caps from.
2 //!
3 //! Extracted from the former `db.rs`; these are inherent methods on
4 //! [`Database`], so the parent needs no re-export.
5
6 use super::{Database, DbError};
7
8 impl Database {
9 /// Aggregate storage stats: (sample_count, total_file_bytes).
10 ///
11 /// Excludes tombstoned rows (`deleted_at IS NOT NULL`) so the figure matches
12 /// the library the user actually sees, the M019 read-path filter applies here
13 /// like every other sample read site.
14 pub fn storage_stats(&self) -> Result<(u64, u64), DbError> {
15 let (count, total): (u64, u64) = self.conn.query_row(
16 "SELECT COUNT(*), COALESCE(SUM(file_size), 0) FROM live_samples",
17 [],
18 // SQLite integers are i64. COUNT/SUM should be non-negative, but a
19 // single corrupt negative file_size must surface as an error, not
20 // wrap silently to ~1.8e19 (workspace denies unwrap for this class).
21 |row| {
22 let count = row.get::<_, i64>(0)?;
23 let total = row.get::<_, i64>(1)?;
24 Ok((
25 u64::try_from(count)
26 .map_err(|_| rusqlite::Error::IntegralValueOutOfRange(0, count))?,
27 u64::try_from(total)
28 .map_err(|_| rusqlite::Error::IntegralValueOutOfRange(1, total))?,
29 ))
30 },
31 )?;
32 Ok((count, total))
33 }
34
35 /// Per-VFS storage stats: count and total bytes of *unique* samples
36 /// referenced by `vfs_id`. A sample referenced from multiple nodes in the
37 /// same VFS counts once. Used by the sync panel's per-VFS toggle rows so
38 /// the user can see how much would upload before enabling blob sync.
39 pub fn vfs_storage_stats(&self, vfs_id: i64) -> Result<(u64, u64), DbError> {
40 // Soft-delete keeps vfs placements, so a tombstoned sample would still be
41 // counted/summed here and inflate the "would upload" estimate; read through
42 // live_samples to exclude it.
43 let (count, total): (u64, u64) = self.conn.query_row(
44 "SELECT COUNT(*), COALESCE(SUM(file_size), 0) FROM live_samples \
45 WHERE hash IN (\
46 SELECT DISTINCT sample_hash FROM vfs_nodes \
47 WHERE vfs_id = ? AND sample_hash IS NOT NULL\
48 )",
49 [vfs_id],
50 // Non-negative in practice; a corrupt negative surfaces as an error
51 // rather than wrapping silently to a nonsense u64.
52 |row| {
53 let count = row.get::<_, i64>(0)?;
54 let total = row.get::<_, i64>(1)?;
55 Ok((
56 u64::try_from(count)
57 .map_err(|_| rusqlite::Error::IntegralValueOutOfRange(0, count))?,
58 u64::try_from(total)
59 .map_err(|_| rusqlite::Error::IntegralValueOutOfRange(1, total))?,
60 ))
61 },
62 )?;
63 Ok((count, total))
64 }
65
66 /// Count and total bytes of the samples blob sync would actually upload:
67 /// the *union* of every VFS with `sync_files` set.
68 ///
69 /// A union rather than a sum over [`vfs_storage_stats`](Self::vfs_storage_stats),
70 /// because a sample placed in two synced VFSes uploads once. Blobs are
71 /// content-addressed and the server dedups on `(app, user, hash)`, so
72 /// adding the per-VFS figures would overstate the need and buy the user a
73 /// cap they do not require. Reads through `live_samples` for the same
74 /// reason the per-VFS query does: a tombstoned sample is not going to
75 /// upload.
76 ///
77 /// Zero synced VFSes gives `(0, 0)`, which is the honest answer — nothing
78 /// is set to sync, so nothing would upload.
79 pub fn synced_storage_stats(&self) -> Result<(u64, u64), DbError> {
80 let (count, total): (u64, u64) = self.conn.query_row(
81 "SELECT COUNT(*), COALESCE(SUM(file_size), 0) FROM live_samples \
82 WHERE hash IN (\
83 SELECT DISTINCT sample_hash FROM vfs_nodes \
84 WHERE sample_hash IS NOT NULL \
85 AND vfs_id IN (SELECT id FROM vfs WHERE sync_files != 0)\
86 )",
87 [],
88 |row| {
89 let count = row.get::<_, i64>(0)?;
90 let total = row.get::<_, i64>(1)?;
91 Ok((
92 u64::try_from(count)
93 .map_err(|_| rusqlite::Error::IntegralValueOutOfRange(0, count))?,
94 u64::try_from(total)
95 .map_err(|_| rusqlite::Error::IntegralValueOutOfRange(1, total))?,
96 ))
97 },
98 )?;
99 Ok((count, total))
100 }
101 }
102