1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
//! How long the last full place recompute and face regroup took, so `videre
//! watch` can choose per batch between redoing the whole partition and placing
//! only the new files.
//!
//! The cost of a full recompute depends on the library, not on the batch: one
//! new photo costs the same eight-minute place recompute on a 70k-file library
//! as a hundred do. So the switch is a time budget, measured, not a file count.
use anyhow::Result;
use rusqlite::Connection;
use std::time::Duration;
/// Milliseconds the last full `location_cluster::recompute_all` took.
pub const LOCATIONS: &str = "locations_recompute_ms";
/// Milliseconds the last full face regroup (`run_clustering`) took.
pub const FACES: &str = "faces_regroup_ms";
/// A full recompute that finished within this is cheap enough to run for every
/// watched batch.
pub const RECOMPUTE_BUDGET: Duration = Duration::from_secs(5);
/// Record how long a full recompute took.
pub fn record(conn: &Connection, key: &str, took: Duration) -> Result<()> {
crate::library_state::set(conn, key, took.as_millis().min(i64::MAX as u128) as i64)
}
/// True only when a full run was recorded and took under the budget.
///
/// Unknown is not cheap: a library that never recomputed under this version
/// could be the 70k-file one, and watch's startup reconcile runs the full
/// recompute when the data changed, which records a time soon enough.
pub fn is_cheap(conn: &Connection, key: &str) -> Result<bool> {
Ok(crate::library_state::get(conn, key)?
.is_some_and(|ms| ms >= 0 && (ms as u128) < RECOMPUTE_BUDGET.as_millis()))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unknown_is_not_cheap() {
let conn = Connection::open_in_memory().unwrap();
assert!(!is_cheap(&conn, LOCATIONS).unwrap());
}
#[test]
fn under_the_budget_is_cheap_and_over_is_not() {
let conn = Connection::open_in_memory().unwrap();
record(&conn, LOCATIONS, Duration::from_millis(4_000)).unwrap();
assert!(is_cheap(&conn, LOCATIONS).unwrap());
record(&conn, LOCATIONS, Duration::from_millis(6_000)).unwrap();
assert!(
!is_cheap(&conn, LOCATIONS).unwrap(),
"a later record overwrites"
);
}
#[test]
fn the_two_keys_are_independent() {
let conn = Connection::open_in_memory().unwrap();
record(&conn, FACES, Duration::from_millis(100)).unwrap();
assert!(is_cheap(&conn, FACES).unwrap());
assert!(!is_cheap(&conn, LOCATIONS).unwrap());
}
}