Tracing: DAO layer + handler spans on tracing, drop all Context params (#141)

trace_db_call no longer takes an OpenTelemetry Context: it opens a
db.<query_type>.<operation> span (same name via otel.name, same
db.query_type / db.operation attrs) on the caller's ambient tracing span
and runs the DAO body inside it. The cx / ctx / context parameter is gone
from every DAO trait method, impl and call site, and from the helpers and
background drains that only threaded it.

The remaining request-handler spans (files, tags, faces, image, video,
favorites, duplicates, memories) move to #[instrument]; the
Context::current_with_span / with_remote_span_context plumbing is gone, as
is extract_context_from_request (the tracing-actix-web root span covers
it). otel::set_span_attributes replaces BoxedSpan::set_attributes.

Background-job raw spans (thumbnails, video actors) are left for #142.

tests/otel_db_spans.rs pins name, attributes, status and nesting (across
an instrumented spawn); otel_traceparent now uses an #[instrument]
handler. CLAUDE.md documents the tracing conventions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Cameron CordesandClaude Opus 5.5 committed 2026-09-29 17:02:40 -04:00
1 parent 1c508cdabd
commit b6deca2a52
55 files changed
+1942 -3852

No files matched your search

+108 -169
View File
@@ -43,7 +43,6 @@ use crate::tags;
///
/// Returns the number of rows successfully backfilled this pass.
pub fn backfill_unhashed_backlog(
context: &opentelemetry::Context,
library: &libraries::Library,
exif_dao: &Arc<Mutex<Box<dyn ExifDao>>>,
) -> usize {
@@ -61,8 +60,7 @@ pub fn backfill_unhashed_backlog(
// library's tick. Negligible cost given the cap.
let rows: Vec<(i32, String)> = {
let mut dao = exif_dao.lock().expect("Unable to lock ExifDao");
dao.get_rows_missing_hash(context, cap + 1)
.unwrap_or_default()
dao.get_rows_missing_hash(cap + 1).unwrap_or_default()
};
if rows.is_empty() {
return 0;
@@ -88,13 +86,9 @@ pub fn backfill_unhashed_backlog(
match content_hash::compute(&abs) {
Ok(id) => {
let mut dao = exif_dao.lock().expect("Unable to lock ExifDao");
if let Err(e) = dao.backfill_content_hash(
context,
library.id,
rel_path,
&id.content_hash,
id.size_bytes,
) {
if let Err(e) =
dao.backfill_content_hash(library.id, rel_path, &id.content_hash, id.size_bytes)
{
warn!(
"face_watch: backfill_content_hash failed for {}: {:?}",
rel_path, e
@@ -148,7 +142,6 @@ fn date_backfill_cap() -> i64 {
/// exiftool is the meaningful new attempt — it handles videos and
/// MakerNote-hosted dates kamadak can't reach.
pub fn backfill_missing_date_taken(
context: &opentelemetry::Context,
library: &libraries::Library,
exif_dao: &Arc<Mutex<Box<dyn ExifDao>>>,
) -> usize {
@@ -156,7 +149,7 @@ pub fn backfill_missing_date_taken(
let rows: Vec<(i32, String)> = {
let mut dao = exif_dao.lock().expect("Unable to lock ExifDao");
dao.get_rows_needing_date_backfill(context, library.id, cap + 1)
dao.get_rows_needing_date_backfill(library.id, cap + 1)
.unwrap_or_default()
};
if rows.is_empty() {
@@ -198,7 +191,6 @@ pub fn backfill_missing_date_taken(
continue;
};
match dao.backfill_date_taken(
context,
library.id,
rel_path,
rd.timestamp,
@@ -300,7 +292,6 @@ pub struct UtcBackfillOutcome {
/// regenerates them. Daily conversation summaries are SMS-derived and
/// don't depend on photo dates, so they're not touched.
pub fn backfill_utc_dates(
context: &opentelemetry::Context,
library: &libraries::Library,
exif_dao: &Arc<Mutex<Box<dyn ExifDao>>>,
reel_dao: &Arc<Mutex<Box<dyn PrecomputedReelDao>>>,
@@ -308,13 +299,10 @@ pub fn backfill_utc_dates(
cursor: &mut i32,
) -> UtcBackfillOutcome {
let cap = date_backfill_cap();
backfill_utc_dates_capped(
context, library, exif_dao, reel_dao, display_tz, cursor, cap,
)
backfill_utc_dates_capped(library, exif_dao, reel_dao, display_tz, cursor, cap)
}
fn backfill_utc_dates_capped(
context: &opentelemetry::Context,
library: &libraries::Library,
exif_dao: &Arc<Mutex<Box<dyn ExifDao>>>,
reel_dao: &Arc<Mutex<Box<dyn PrecomputedReelDao>>>,
@@ -350,7 +338,7 @@ fn backfill_utc_dates_capped(
'paging: loop {
let page = {
let mut dao = exif_dao.lock().expect("Unable to lock ExifDao");
dao.get_rows_needing_utc_backfill(context, library.id, *cursor, cap)
dao.get_rows_needing_utc_backfill(library.id, *cursor, cap)
.unwrap_or_default()
};
let page_len = page.len() as i64;
@@ -444,7 +432,7 @@ fn backfill_utc_dates_capped(
tz_source,
} in &conversions
{
match dao.backfill_date_utc(context, row.id, *utc, *offset, tz_source.as_str()) {
match dao.backfill_date_utc(row.id, *utc, *offset, tz_source.as_str()) {
Ok(true) => {
outcome.converted += 1;
let before = display_day(zone, row.date_taken);
@@ -468,8 +456,7 @@ fn backfill_utc_dates_capped(
}
if !affected_days.is_empty() {
outcome.reels_invalidated =
invalidate_day_reels(context, library, reel_dao, zone, &affected_days);
outcome.reels_invalidated = invalidate_day_reels(library, reel_dao, zone, &affected_days);
}
if outcome.converted > 0 || outcome.unresolved > 0 || more_remain {
@@ -510,7 +497,6 @@ fn reel_covered_day(reel: &PrecomputedReel, zone: chrono_tz::Tz) -> Option<Naive
/// With the ledger row gone the nightly pre-gen's freshness gate fails
/// and it renders a new one on its normal schedule. Returns rows deleted.
fn invalidate_day_reels(
context: &opentelemetry::Context,
library: &libraries::Library,
reel_dao: &Arc<Mutex<Box<dyn PrecomputedReelDao>>>,
zone: chrono_tz::Tz,
@@ -518,7 +504,7 @@ fn invalidate_day_reels(
) -> usize {
let keys = vec!["all".to_string(), library.id.to_string()];
let mut dao = reel_dao.lock().expect("Unable to lock PrecomputedReelDao");
let reels = match dao.list_for_span(context, "day", &keys) {
let reels = match dao.list_for_span("day", &keys) {
Ok(r) => r,
Err(e) => {
warn!("utc_backfill: listing day reels failed: {:?}", e);
@@ -535,7 +521,7 @@ fn invalidate_day_reels(
return 0;
}
let ids: Vec<i32> = stale.iter().map(|r| r.id).collect();
match dao.delete_ids(context, &ids) {
match dao.delete_ids(&ids) {
Ok(n) => {
// Unlink too, so the on-demand cache can't serve the stale
// render under the same key.
@@ -563,14 +549,13 @@ fn invalidate_day_reels(
/// doesn't call this for them), so a flapping share doesn't make the
/// gauge dip.
pub fn refresh_utc_backfill_progress(
context: &opentelemetry::Context,
library: &libraries::Library,
exif_dao: &Arc<Mutex<Box<dyn ExifDao>>>,
remaining: &mut HashMap<i32, i64>,
) {
let count = {
let mut dao = exif_dao.lock().expect("Unable to lock ExifDao");
dao.count_rows_needing_utc_backfill(context, library.id)
dao.count_rows_needing_utc_backfill(library.id)
};
if let Ok(n) = count {
remaining.insert(library.id, n);
@@ -594,7 +579,6 @@ pub fn refresh_utc_backfill_progress(
/// retry every tick at this point; future Branch may add a status
/// column like face_detections has.
pub fn process_clip_backlog(
context: &opentelemetry::Context,
library: &libraries::Library,
clip_client: &crate::ai::clip_client::ClipClient,
exif_dao: &Arc<Mutex<Box<dyn ExifDao>>>,
@@ -611,7 +595,7 @@ pub fn process_clip_backlog(
let rows: Vec<(String, String)> = {
let mut dao = exif_dao.lock().expect("exif dao");
match dao.list_clip_unencoded_candidates(context, library.id, cap) {
match dao.list_clip_unencoded_candidates(library.id, cap) {
Ok(r) => r,
Err(e) => {
warn!(
@@ -653,7 +637,6 @@ pub fn process_clip_backlog(
}
pub fn process_face_backlog(
context: &opentelemetry::Context,
library: &libraries::Library,
face_client: &crate::ai::face_client::FaceClient,
face_dao: &Arc<Mutex<Box<dyn faces::FaceDao>>>,
@@ -668,7 +651,7 @@ pub fn process_face_backlog(
let rows: Vec<(String, String)> = {
let mut dao = face_dao.lock().expect("face dao");
match dao.list_unscanned_candidates(context, library.id, cap) {
match dao.list_unscanned_candidates(library.id, cap) {
Ok(r) => r,
Err(e) => {
warn!(
@@ -719,7 +702,6 @@ pub fn process_face_backlog(
/// table (vanished mid-scan, permission denied, etc.) burned the budget
/// every tick and the rest of the backlog never advanced.
pub fn backfill_missing_content_hashes(
context: &opentelemetry::Context,
files: &[(PathBuf, String)],
library: &libraries::Library,
exif_dao: &Arc<Mutex<Box<dyn ExifDao>>>,
@@ -735,7 +717,7 @@ pub fn backfill_missing_content_hashes(
let exif_records = {
let mut dao = exif_dao.lock().expect("Unable to lock ExifDao");
dao.get_exif_batch(context, Some(library.id), &image_paths)
dao.get_exif_batch(Some(library.id), &image_paths)
.unwrap_or_default()
};
// Cheap lookup back from rel_path → absolute file_path so
@@ -775,7 +757,6 @@ pub fn backfill_missing_content_hashes(
Ok(id) => {
let mut dao = exif_dao.lock().expect("Unable to lock ExifDao");
if let Err(e) = dao.backfill_content_hash(
context,
library.id,
&record.file_path,
&id.content_hash,
@@ -820,7 +801,6 @@ pub fn backfill_missing_content_hashes(
/// just inserted alongside any pre-existing rows the watcher walked
/// over — covers both new uploads and the initial backlog scan.
pub fn build_face_candidates(
context: &opentelemetry::Context,
library: &libraries::Library,
files: &[(PathBuf, String)],
exif_dao: &Arc<Mutex<Box<dyn ExifDao>>>,
@@ -839,7 +819,7 @@ pub fn build_face_candidates(
let exif_records = {
let mut dao = exif_dao.lock().expect("Unable to lock ExifDao");
dao.get_exif_batch(context, Some(library.id), &image_paths)
dao.get_exif_batch(Some(library.id), &image_paths)
.unwrap_or_default()
};
// rel_path → content_hash (only rows with a hash; without one we have
@@ -857,7 +837,7 @@ pub fn build_face_candidates(
let Some(hash) = hash_by_path.get(&rel_path) else {
continue;
};
match dao.already_scanned(context, hash) {
match dao.already_scanned(hash) {
Ok(true) => continue,
Ok(false) => candidates.push(face_watch::FaceCandidate {
rel_path,
@@ -887,10 +867,6 @@ mod tests {
use crate::faces::{FaceDao, SqliteFaceDao};
use crate::libraries::Library;
fn ctx() -> opentelemetry::Context {
opentelemetry::Context::new()
}
/// Everything `setup` hands back to a test: tempdir, library, shared
/// connection, and the two DAOs. Aliased to keep clippy's
/// type-complexity lint satisfied.
@@ -952,37 +928,34 @@ mod tests {
content_hash: Option<&str>,
) {
let mut dao = exif_dao.lock().unwrap();
dao.store_exif(
&ctx(),
InsertImageExif {
library_id: lib_id,
file_path: rel.to_string(),
camera_make: None,
camera_model: None,
lens_model: None,
width: None,
height: None,
orientation: None,
gps_latitude: None,
gps_longitude: None,
gps_altitude: None,
focal_length: None,
aperture: None,
shutter_speed: None,
iso: None,
date_taken: None,
created_time: 0,
last_modified: 0,
content_hash: content_hash.map(|s| s.to_string()),
size_bytes: None,
phash_64: None,
dhash_64: None,
date_taken_source: None,
date_utc: None,
camera_offset_minutes: None,
date_taken_tz_source: None,
},
)
dao.store_exif(InsertImageExif {
library_id: lib_id,
file_path: rel.to_string(),
camera_make: None,
camera_model: None,
lens_model: None,
width: None,
height: None,
orientation: None,
gps_latitude: None,
gps_longitude: None,
gps_altitude: None,
focal_length: None,
aperture: None,
shutter_speed: None,
iso: None,
date_taken: None,
created_time: 0,
last_modified: 0,
content_hash: content_hash.map(|s| s.to_string()),
size_bytes: None,
phash_64: None,
dhash_64: None,
date_taken_source: None,
date_utc: None,
camera_offset_minutes: None,
date_taken_tz_source: None,
})
.expect("insert");
}
@@ -1002,12 +975,12 @@ mod tests {
insert_exif(&exif_dao, 1, "a.jpg", None);
insert_exif(&exif_dao, 1, "b.jpg", None);
let backfilled = backfill_unhashed_backlog(&ctx(), &library, &exif_dao);
let backfilled = backfill_unhashed_backlog(&library, &exif_dao);
assert_eq!(backfilled, 2);
let mut dao = exif_dao.lock().unwrap();
let rows = dao
.get_exif_batch(&ctx(), Some(1), &["a.jpg".to_string(), "b.jpg".to_string()])
.get_exif_batch(Some(1), &["a.jpg".to_string(), "b.jpg".to_string()])
.unwrap();
assert_eq!(rows.len(), 2);
for r in rows {
@@ -1029,12 +1002,12 @@ mod tests {
// Row in the other library — must be skipped (different lib_id).
insert_exif(&exif_dao, 2, "other.jpg", None);
let backfilled = backfill_unhashed_backlog(&ctx(), &library, &exif_dao);
let backfilled = backfill_unhashed_backlog(&library, &exif_dao);
assert_eq!(backfilled, 1, "only the existing in-library file hashes");
let mut dao = exif_dao.lock().unwrap();
let other = dao
.get_exif_batch(&ctx(), Some(2), &["other.jpg".to_string()])
.get_exif_batch(Some(2), &["other.jpg".to_string()])
.unwrap();
assert_eq!(other.len(), 1);
assert!(
@@ -1042,7 +1015,7 @@ mod tests {
"other-library row must remain unhashed"
);
let ghost = dao
.get_exif_batch(&ctx(), Some(1), &["ghost.jpg".to_string()])
.get_exif_batch(Some(1), &["ghost.jpg".to_string()])
.unwrap();
assert_eq!(ghost.len(), 1);
assert!(
@@ -1068,7 +1041,7 @@ mod tests {
insert_exif(&exif_dao, 1, &rel, None);
}
let backfilled = backfill_unhashed_backlog(&ctx(), &library, &exif_dao);
let backfilled = backfill_unhashed_backlog(&library, &exif_dao);
assert_eq!(backfilled, 2, "cap=2 must bound the per-tick successes");
unsafe {
std::env::remove_var("FACE_HASH_BACKFILL_MAX_PER_TICK");
@@ -1091,12 +1064,11 @@ mod tests {
(tmp.path().join("already.jpg"), "already.jpg".to_string()),
(tmp.path().join("clip.mp4"), "clip.mp4".to_string()),
];
backfill_missing_content_hashes(&ctx(), &files, &library, &exif_dao);
backfill_missing_content_hashes(&files, &library, &exif_dao);
let mut dao = exif_dao.lock().unwrap();
let rows = dao
.get_exif_batch(
&ctx(),
Some(1),
&[
"fresh.jpg".to_string(),
@@ -1138,7 +1110,7 @@ mod tests {
// Mark `scanned.jpg`'s hash as already detected.
{
let mut dao = face_dao.lock().unwrap();
dao.mark_status(&ctx(), 1, "hash-scanned", "scanned.jpg", "no_faces", "test")
dao.mark_status(1, "hash-scanned", "scanned.jpg", "no_faces", "test")
.expect("mark scanned");
}
@@ -1148,7 +1120,7 @@ mod tests {
(tmp.path().join("unhashed.jpg"), "unhashed.jpg".to_string()),
(tmp.path().join("clip.mp4"), "clip.mp4".to_string()),
];
let candidates = build_face_candidates(&ctx(), &library, &files, &exif_dao, &face_dao);
let candidates = build_face_candidates(&library, &files, &exif_dao, &face_dao);
assert_eq!(
candidates.len(),
@@ -1194,37 +1166,34 @@ mod tests {
source: Option<&str>,
) -> i32 {
let mut dao = exif_dao.lock().unwrap();
dao.store_exif(
&ctx(),
InsertImageExif {
library_id: lib_id,
file_path: rel.to_string(),
camera_make: None,
camera_model: None,
lens_model: None,
width: None,
height: None,
orientation: None,
gps_latitude: None,
gps_longitude: None,
gps_altitude: None,
focal_length: None,
aperture: None,
shutter_speed: None,
iso: None,
date_taken,
created_time: 0,
last_modified: 0,
content_hash: None,
size_bytes: None,
phash_64: None,
dhash_64: None,
date_taken_source: source.map(str::to_string),
date_utc: None,
camera_offset_minutes: None,
date_taken_tz_source: None,
},
)
dao.store_exif(InsertImageExif {
library_id: lib_id,
file_path: rel.to_string(),
camera_make: None,
camera_model: None,
lens_model: None,
width: None,
height: None,
orientation: None,
gps_latitude: None,
gps_longitude: None,
gps_altitude: None,
focal_length: None,
aperture: None,
shutter_speed: None,
iso: None,
date_taken,
created_time: 0,
last_modified: 0,
content_hash: None,
size_bytes: None,
phash_64: None,
dhash_64: None,
date_taken_source: source.map(str::to_string),
date_utc: None,
camera_offset_minutes: None,
date_taken_tz_source: None,
})
.expect("insert")
.id
}
@@ -1241,7 +1210,7 @@ mod tests {
exif_dao
.lock()
.unwrap()
.count_rows_needing_utc_backfill(&ctx(), lib_id)
.count_rows_needing_utc_backfill(lib_id)
.unwrap()
}
@@ -1385,8 +1354,7 @@ mod tests {
);
assert_eq!(remaining(&exif_dao, 1), 5);
let out =
backfill_utc_dates_capped(&ctx(), &library, &exif_dao, &reel_dao, NY, &mut 0, 500);
let out = backfill_utc_dates_capped(&library, &exif_dao, &reel_dao, NY, &mut 0, 500);
assert_eq!(out.converted, 5, "{out:?}");
assert_eq!(out.unresolved, 0, "{out:?}");
@@ -1446,12 +1414,11 @@ mod tests {
.collect::<Vec<_>>()
};
let before = snapshot(&conn);
let again =
backfill_utc_dates_capped(&ctx(), &library, &exif_dao, &reel_dao, NY, &mut 0, 500);
let again = backfill_utc_dates_capped(&library, &exif_dao, &reel_dao, NY, &mut 0, 500);
assert_eq!(again, UtcBackfillOutcome::default());
assert_eq!(snapshot(&conn), before);
let mut progress = HashMap::new();
refresh_utc_backfill_progress(&ctx(), &library, &exif_dao, &mut progress);
refresh_utc_backfill_progress(&library, &exif_dao, &mut progress);
assert_eq!(progress.get(&1), Some(&0));
}
@@ -1467,8 +1434,7 @@ mod tests {
let undated = insert_legacy(&exif_dao, 1, "undated.jpg", None, None);
let other = insert_legacy(&exif_dao, 2, "1500000000.jpg", Some(ts), Some("filename"));
let out =
backfill_utc_dates_capped(&ctx(), &library, &exif_dao, &reel_dao, NY, &mut 0, 500);
let out = backfill_utc_dates_capped(&library, &exif_dao, &reel_dao, NY, &mut 0, 500);
assert_eq!(out.converted, 1);
assert_eq!(row(&conn, converted).date_utc, Some(ts));
assert_eq!(row(&conn, undated).date_utc, None);
@@ -1508,16 +1474,8 @@ mod tests {
// Cursor carried across ticks, as the watcher does.
let mut cursor = 0;
for _ in 0..4 {
let out = backfill_utc_dates_capped(
&ctx(),
&library,
&exif_dao,
&reel_dao,
NY,
&mut cursor,
2,
);
refresh_utc_backfill_progress(&ctx(), &library, &exif_dao, &mut progress);
let out = backfill_utc_dates_capped(&library, &exif_dao, &reel_dao, NY, &mut cursor, 2);
refresh_utc_backfill_progress(&library, &exif_dao, &mut progress);
seen.push((out.converted, progress[&1]));
}
// Cap holds (2 per tick) and the 3 missing rows never spend it.
@@ -1532,7 +1490,7 @@ mod tests {
.execute(&mut *c)
.unwrap();
}
refresh_utc_backfill_progress(&ctx(), &library, &exif_dao, &mut progress);
refresh_utc_backfill_progress(&library, &exif_dao, &mut progress);
assert_eq!(progress[&1], 0);
}
@@ -1566,15 +1524,7 @@ mod tests {
let mut cursor = 0;
let mut converted = 0;
for _ in 0..4 {
let out = backfill_utc_dates_capped(
&ctx(),
&library,
&exif_dao,
&reel_dao,
NY,
&mut cursor,
2,
);
let out = backfill_utc_dates_capped(&library, &exif_dao, &reel_dao, NY, &mut cursor, 2);
converted += out.converted;
}
assert_eq!(converted, 2, "the rows behind the stuck ones still drain");
@@ -1614,16 +1564,14 @@ mod tests {
let health_map = libraries::new_health_map(std::slice::from_ref(&unmounted));
assert!(!libraries::refresh_health(&health_map, &unmounted, true).is_online());
// …and the drain itself converts nothing even if called.
let out =
backfill_utc_dates_capped(&ctx(), &unmounted, &exif_dao, &reel_dao, NY, &mut 0, 500);
let out = backfill_utc_dates_capped(&unmounted, &exif_dao, &reel_dao, NY, &mut 0, 500);
assert_eq!(out, UtcBackfillOutcome::default());
assert_eq!(row(&conn, filed).date_utc, None);
assert_eq!(row(&conn, manual).date_utc, None);
// Share returns → picked up next tick.
assert!(libraries::refresh_health(&health_map, &library, true).is_online());
let out =
backfill_utc_dates_capped(&ctx(), &library, &exif_dao, &reel_dao, NY, &mut 0, 500);
let out = backfill_utc_dates_capped(&library, &exif_dao, &reel_dao, NY, &mut 0, 500);
assert_eq!(out.converted, 2);
assert_eq!(remaining(&exif_dao, 1), 0);
}
@@ -1643,21 +1591,18 @@ mod tests {
let id = reel_dao
.lock()
.unwrap()
.record_reel(
&ctx(),
&InsertablePrecomputedReel {
span: span.to_string(),
library_key: library_key.to_string(),
cache_key: format!("{span}-{library_key}-{month}-{day}"),
output_path: mp4.to_string_lossy().to_string(),
title: "t".to_string(),
media_count: 1,
render_version: 1,
tz_offset_minutes: -240,
voice: None,
generated_at: utc(2025, month, day, 7, 0, 0),
},
)
.record_reel(&InsertablePrecomputedReel {
span: span.to_string(),
library_key: library_key.to_string(),
cache_key: format!("{span}-{library_key}-{month}-{day}"),
output_path: mp4.to_string_lossy().to_string(),
title: "t".to_string(),
media_count: 1,
render_version: 1,
tz_offset_minutes: -240,
voice: None,
generated_at: utc(2025, month, day, 7, 0, 0),
})
.unwrap();
(id, mp4)
}
@@ -1704,8 +1649,7 @@ mod tests {
let (other_day, mp4_0301) = seed_reel(&reel_dao, reels_dir.path(), "day", "1", 3, 1);
let (week, _) = seed_reel(&reel_dao, reels_dir.path(), "week", "1", 6, 12);
let out =
backfill_utc_dates_capped(&ctx(), &library, &exif_dao, &reel_dao, NY, &mut 0, 500);
let out = backfill_utc_dates_capped(&library, &exif_dao, &reel_dao, NY, &mut 0, 500);
assert_eq!(out.converted, 2);
assert_eq!(out.day_changed, 1, "{out:?}");
assert_eq!(out.reels_invalidated, 2, "{out:?}");
@@ -1722,11 +1666,7 @@ mod tests {
// With the ledger row gone the nightly pre-gen's freshness gate
// (`latest_for` must match the new cache key) can't skip → it
// regenerates on its normal schedule; no new scheduler involved.
let latest = reel_dao
.lock()
.unwrap()
.latest_for(&ctx(), "day", "all")
.unwrap();
let latest = reel_dao.lock().unwrap().latest_for("day", "all").unwrap();
assert!(latest.is_none());
}
@@ -1749,8 +1689,7 @@ mod tests {
seed_reel(&reel_dao, reels_dir.path(), "day", "1", 6, 11);
seed_reel(&reel_dao, reels_dir.path(), "day", "all", 6, 12);
let out =
backfill_utc_dates_capped(&ctx(), &library, &exif_dao, &reel_dao, NY, &mut 0, 500);
let out = backfill_utc_dates_capped(&library, &exif_dao, &reel_dao, NY, &mut 0, 500);
assert_eq!(out.converted, 1);
assert_eq!(out.day_changed, 0);
assert_eq!(out.reels_invalidated, 0);