1use std::os::raw::c_int;
5
6use reifydb_value::{byte_size::ByteSize, count::Count};
7use rusqlite::{
8 Connection,
9 ffi::{
10 SQLITE_DBSTATUS_CACHE_HIT, SQLITE_DBSTATUS_CACHE_MISS, SQLITE_DBSTATUS_CACHE_USED, SQLITE_OK,
11 sqlite3_db_status, sqlite3_memory_used,
12 },
13};
14
15pub fn global_memory_used() -> ByteSize {
16 let used = unsafe { sqlite3_memory_used() };
18 ByteSize::from_bytes(used.max(0) as u64)
19}
20
21#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
22pub struct ConnectionCacheSweep {
23 pub used: ByteSize,
24 pub hits: Count,
25 pub misses: Count,
26}
27
28pub fn sweep_connection_cache(conn: &Connection) -> ConnectionCacheSweep {
29 ConnectionCacheSweep {
30 used: ByteSize::from_bytes(db_status(conn, SQLITE_DBSTATUS_CACHE_USED, false)),
31 hits: Count::new(db_status(conn, SQLITE_DBSTATUS_CACHE_HIT, true)),
32 misses: Count::new(db_status(conn, SQLITE_DBSTATUS_CACHE_MISS, true)),
33 }
34}
35
36fn db_status(conn: &Connection, op: c_int, reset: bool) -> u64 {
37 let mut current: c_int = 0;
38 let mut highwater: c_int = 0;
39 let rc = unsafe { sqlite3_db_status(conn.handle(), op, &mut current, &mut highwater, reset as c_int) };
41 if rc == SQLITE_OK {
42 current.max(0) as u64
43 } else {
44 0
45 }
46}
47
48#[cfg(test)]
49mod tests {
50 use rusqlite::Connection;
51
52 use super::sweep_connection_cache;
53
54 #[test]
55 fn sweep_resets_hit_and_miss_counters_but_not_used() {
56 let conn = Connection::open_in_memory().expect("open in-memory db");
59 conn.execute_batch(
60 "CREATE TABLE t (id INTEGER PRIMARY KEY, v TEXT); \
61 INSERT INTO t VALUES (1, 'a'), (2, 'b');",
62 )
63 .expect("seed table");
64 let count: i64 = conn.query_row("SELECT COUNT(*) FROM t", [], |row| row.get(0)).expect("count");
65 assert_eq!(count, 2);
66
67 let first = sweep_connection_cache(&conn);
68 assert!(
69 first.hits.as_u64() + first.misses.as_u64() > 0,
70 "reading a table must touch the page cache, got {first:?}"
71 );
72 assert!(first.used.as_bytes() > 0, "pages held by the connection must report as used");
73
74 let second = sweep_connection_cache(&conn);
75 assert_eq!(second.hits.as_u64(), 0, "hits must have been taken by the first sweep");
76 assert_eq!(second.misses.as_u64(), 0, "misses must have been taken by the first sweep");
77 assert!(second.used.as_bytes() > 0, "used is instantaneous and must not be reset by sweeping");
78 }
79}