@@ -83,24 +83,42 @@ impl Storage {
8383 max_points : Option < u32 > ,
8484 ) -> rusqlite:: Result < Vec < SensorReading > > {
8585 let conn = self . conn . lock ( ) . await ;
86+
87+ // 1. Performance PRAGMAs (Crucial for Raspberry Pi)
88+ conn. execute_batch (
89+ "
90+ PRAGMA journal_mode = WAL;
91+ PRAGMA temp_store = MEMORY;
92+ PRAGMA synchronous = NORMAL;
93+ " ,
94+ ) ?;
95+
8696 let cutoff = format ! ( "-{hours} hours" ) ;
8797
8898 match ( sensor_id, max_points) {
8999 ( Some ( sid) , Some ( max) ) => {
100+ // Get total count first to determine sampling step
101+ let total: u32 = conn. query_row (
102+ "SELECT COUNT(*) FROM sensor_readings WHERE sensor_id = ?1 AND recorded_at > strftime('%Y-%m-%dT%H:%M:%SZ', 'now', ?2)" ,
103+ rusqlite:: params![ sid, cutoff] ,
104+
105+ |r| r. get ( 0 ) ,
106+ ) ?;
107+ let step = ( total / max) . max ( 1 ) ;
108+
90109 let mut stmt = conn. prepare_cached (
91110 "SELECT sensor_id, sensor_name, temperature, room_type, recorded_at
92111 FROM (
93- SELECT sensor_id, sensor_name, temperature, room_type, recorded_at,
94- ROW_NUMBER() OVER (ORDER BY recorded_at) AS rn,
95- COUNT(*) OVER () AS total
112+ SELECT *, ROW_NUMBER() OVER (ORDER BY recorded_at) AS rn
96113 FROM sensor_readings
97114 WHERE sensor_id = ?1
98115 AND recorded_at > strftime('%Y-%m-%dT%H:%M:%SZ', 'now', ?2)
99116 )
100- WHERE ( rn - 1) % MAX(1, (total + ?3 - 1) / ?3) = 0
117+ WHERE rn % ?3 = 0
101118 ORDER BY recorded_at ASC" ,
102119 ) ?;
103- let rows = stmt. query_map ( rusqlite:: params![ sid, cutoff, max] , row_to_reading) ?
120+ let rows = stmt
121+ . query_map ( rusqlite:: params![ sid, cutoff, step] , row_to_reading) ?
104122 . collect :: < rusqlite:: Result < Vec < _ > > > ( ) ;
105123 rows
106124 }
@@ -111,24 +129,28 @@ impl Storage {
111129 WHERE sensor_id = ?1 AND recorded_at > strftime('%Y-%m-%dT%H:%M:%SZ', 'now', ?2)
112130 ORDER BY recorded_at ASC" ,
113131 ) ?;
114- let rows = stmt. query_map ( rusqlite:: params![ sid, cutoff] , row_to_reading) ?
132+ let rows = stmt
133+ . query_map ( rusqlite:: params![ sid, cutoff] , row_to_reading) ?
115134 . collect :: < rusqlite:: Result < Vec < _ > > > ( ) ;
116135 rows
117136 }
118137 ( None , Some ( max) ) => {
138+ // For 'All Sensors', we use a sub-query to find the average density
139+ // or just apply a global row-number filter per sensor.
119140 let mut stmt = conn. prepare_cached (
120141 "SELECT sensor_id, sensor_name, temperature, room_type, recorded_at
121142 FROM (
122- SELECT sensor_id, sensor_name, temperature, room_type, recorded_at,
123- ROW_NUMBER() OVER (PARTITION BY sensor_id ORDER BY recorded_at) AS rn,
124- COUNT(*) OVER (PARTITION BY sensor_id) AS total
143+ SELECT *, ROW_NUMBER() OVER (PARTITION BY sensor_id ORDER BY recorded_at) AS rn,
144+ COUNT(*) OVER (PARTITION BY sensor_id) as sensor_total
125145 FROM sensor_readings
126146 WHERE recorded_at > strftime('%Y-%m-%dT%H:%M:%SZ', 'now', ?1)
127147 )
128- WHERE (rn - 1) % MAX(1, (total + ?2 - 1) / ?2) = 0
148+ -- Logic: Only return a row if it hits the step threshold for its specific sensor
149+ WHERE rn % MAX(1, sensor_total / ?2) = 0
129150 ORDER BY recorded_at ASC" ,
130151 ) ?;
131- let rows = stmt. query_map ( rusqlite:: params![ cutoff, max] , row_to_reading) ?
152+ let rows = stmt
153+ . query_map ( rusqlite:: params![ cutoff, max] , row_to_reading) ?
132154 . collect :: < rusqlite:: Result < Vec < _ > > > ( ) ;
133155 rows
134156 }
@@ -139,7 +161,8 @@ impl Storage {
139161 WHERE recorded_at > strftime('%Y-%m-%dT%H:%M:%SZ', 'now', ?1)
140162 ORDER BY recorded_at ASC" ,
141163 ) ?;
142- let rows = stmt. query_map ( rusqlite:: params![ cutoff] , row_to_reading) ?
164+ let rows = stmt
165+ . query_map ( rusqlite:: params![ cutoff] , row_to_reading) ?
143166 . collect :: < rusqlite:: Result < Vec < _ > > > ( ) ;
144167 rows
145168 }
@@ -148,11 +171,9 @@ impl Storage {
148171
149172 pub async fn get_settings ( & self ) -> rusqlite:: Result < String > {
150173 let conn = self . conn . lock ( ) . await ;
151- conn. query_row (
152- "SELECT data FROM user_settings WHERE id = 1" ,
153- [ ] ,
154- |row| row. get ( 0 ) ,
155- )
174+ conn. query_row ( "SELECT data FROM user_settings WHERE id = 1" , [ ] , |row| {
175+ row. get ( 0 )
176+ } )
156177 }
157178
158179 pub async fn save_settings ( & self , data : & str ) -> rusqlite:: Result < ( ) > {
@@ -226,7 +247,11 @@ pub fn start_recording_loop(state: Arc<AppState>) {
226247
227248 tick_count += 1 ;
228249 if tick_count. is_multiple_of ( 288 ) {
229- match state. storage . prune ( state. settings . history_retention_days ) . await {
250+ match state
251+ . storage
252+ . prune ( state. settings . history_retention_days )
253+ . await
254+ {
230255 Ok ( n) if n > 0 => tracing:: info!( "Pruned {n} old sensor readings" ) ,
231256 Err ( e) => tracing:: error!( "Failed to prune old readings: {e}" ) ,
232257 _ => { }
0 commit comments