Add tracing to EXIF DAO methods

This commit is contained in:
Cameron
2025-12-23 22:57:24 -05:00
parent 636701a69e
commit c035678162
7 changed files with 306 additions and 175 deletions

View File

@@ -3,6 +3,7 @@ use std::sync::{Arc, Mutex};
use chrono::Utc; use chrono::Utc;
use clap::Parser; use clap::Parser;
use opentelemetry;
use rayon::prelude::*; use rayon::prelude::*;
use walkdir::WalkDir; use walkdir::WalkDir;
@@ -63,6 +64,9 @@ fn main() -> anyhow::Result<()> {
let results: Vec<_> = image_files let results: Vec<_> = image_files
.par_iter() .par_iter()
.map(|path| { .map(|path| {
// Create context for this processing iteration
let context = opentelemetry::Context::new();
let relative_path = match path.strip_prefix(&base) { let relative_path = match path.strip_prefix(&base) {
Ok(p) => p.to_str().unwrap().to_string(), Ok(p) => p.to_str().unwrap().to_string(),
Err(_) => { Err(_) => {
@@ -76,7 +80,7 @@ fn main() -> anyhow::Result<()> {
// Check if EXIF data already exists // Check if EXIF data already exists
let existing = if let Ok(mut dao_lock) = dao.lock() { let existing = if let Ok(mut dao_lock) = dao.lock() {
dao_lock.get_exif(&relative_path).ok().flatten() dao_lock.get_exif(&context, &relative_path).ok().flatten()
} else { } else {
eprintln!("{} - Failed to acquire database lock", relative_path); eprintln!("{} - Failed to acquire database lock", relative_path);
return Err(anyhow::anyhow!("Lock error")); return Err(anyhow::anyhow!("Lock error"));
@@ -117,10 +121,12 @@ fn main() -> anyhow::Result<()> {
if let Ok(mut dao_lock) = dao.lock() { if let Ok(mut dao_lock) = dao.lock() {
let result = if existing.is_some() { let result = if existing.is_some() {
// Update existing record // Update existing record
dao_lock.update_exif(insert_exif).map(|_| "update") dao_lock
.update_exif(&context, insert_exif)
.map(|_| "update")
} else { } else {
// Insert new record // Insert new record
dao_lock.store_exif(insert_exif).map(|_| "insert") dao_lock.store_exif(&context, insert_exif).map(|_| "insert")
}; };
match result { match result {

View File

@@ -50,7 +50,7 @@ impl DatabaseUpdater {
// Update image_exif table // Update image_exif table
if let Ok(mut dao) = self.exif_dao.lock() { if let Ok(mut dao) = self.exif_dao.lock() {
match dao.update_file_path(old_path, new_path) { match dao.update_file_path(&context, old_path, new_path) {
Ok(_) => { Ok(_) => {
info!("Updated image_exif: {} -> {}", old_path, new_path); info!("Updated image_exif: {} -> {}", old_path, new_path);
success_count += 1; success_count += 1;
@@ -120,7 +120,7 @@ impl DatabaseUpdater {
// Get from image_exif // Get from image_exif
if let Ok(mut dao) = self.exif_dao.lock() { if let Ok(mut dao) = self.exif_dao.lock() {
match dao.get_all_file_paths() { match dao.get_all_file_paths(&context) {
Ok(paths) => { Ok(paths) => {
info!("Found {} paths in image_exif", paths.len()); info!("Found {} paths in image_exif", paths.len());
all_paths.extend(paths); all_paths.extend(paths);

View File

@@ -7,6 +7,7 @@ use std::sync::{Arc, Mutex};
use crate::database::models::{ use crate::database::models::{
Favorite, ImageExif, InsertFavorite, InsertImageExif, InsertUser, User, Favorite, ImageExif, InsertFavorite, InsertImageExif, InsertUser, User,
}; };
use crate::otel::trace_db_call;
pub mod models; pub mod models;
pub mod schema; pub mod schema;
@@ -221,18 +222,42 @@ impl FavoriteDao for SqliteFavoriteDao {
} }
pub trait ExifDao: Sync + Send { pub trait ExifDao: Sync + Send {
fn store_exif(&mut self, exif_data: InsertImageExif) -> Result<ImageExif, DbError>; fn store_exif(
fn get_exif(&mut self, file_path: &str) -> Result<Option<ImageExif>, DbError>; &mut self,
fn update_exif(&mut self, exif_data: InsertImageExif) -> Result<ImageExif, DbError>; context: &opentelemetry::Context,
fn delete_exif(&mut self, file_path: &str) -> Result<(), DbError>; exif_data: InsertImageExif,
fn get_all_with_date_taken(&mut self) -> Result<Vec<(String, i64)>, DbError>; ) -> Result<ImageExif, DbError>;
fn get_exif(
&mut self,
context: &opentelemetry::Context,
file_path: &str,
) -> Result<Option<ImageExif>, DbError>;
fn update_exif(
&mut self,
context: &opentelemetry::Context,
exif_data: InsertImageExif,
) -> Result<ImageExif, DbError>;
fn delete_exif(
&mut self,
context: &opentelemetry::Context,
file_path: &str,
) -> Result<(), DbError>;
fn get_all_with_date_taken(
&mut self,
context: &opentelemetry::Context,
) -> Result<Vec<(String, i64)>, DbError>;
/// Batch load EXIF data for multiple file paths (single query) /// Batch load EXIF data for multiple file paths (single query)
fn get_exif_batch(&mut self, file_paths: &[String]) -> Result<Vec<ImageExif>, DbError>; fn get_exif_batch(
&mut self,
context: &opentelemetry::Context,
file_paths: &[String],
) -> Result<Vec<ImageExif>, DbError>;
/// Query files by EXIF criteria with optional filters /// Query files by EXIF criteria with optional filters
fn query_by_exif( fn query_by_exif(
&mut self, &mut self,
context: &opentelemetry::Context,
camera_make: Option<&str>, camera_make: Option<&str>,
camera_model: Option<&str>, camera_model: Option<&str>,
lens_model: Option<&str>, lens_model: Option<&str>,
@@ -242,13 +267,24 @@ pub trait ExifDao: Sync + Send {
) -> Result<Vec<ImageExif>, DbError>; ) -> Result<Vec<ImageExif>, DbError>;
/// Get distinct camera makes with counts /// Get distinct camera makes with counts
fn get_camera_makes(&mut self) -> Result<Vec<(String, i64)>, DbError>; fn get_camera_makes(
&mut self,
context: &opentelemetry::Context,
) -> Result<Vec<(String, i64)>, DbError>;
/// Update file path in EXIF database /// Update file path in EXIF database
fn update_file_path(&mut self, old_path: &str, new_path: &str) -> Result<(), DbError>; fn update_file_path(
&mut self,
context: &opentelemetry::Context,
old_path: &str,
new_path: &str,
) -> Result<(), DbError>;
/// Get all file paths from EXIF database /// Get all file paths from EXIF database
fn get_all_file_paths(&mut self) -> Result<Vec<String>, DbError>; fn get_all_file_paths(
&mut self,
context: &opentelemetry::Context,
) -> Result<Vec<String>, DbError>;
} }
pub struct SqliteExifDao { pub struct SqliteExifDao {
@@ -270,113 +306,151 @@ impl SqliteExifDao {
} }
impl ExifDao for SqliteExifDao { impl ExifDao for SqliteExifDao {
fn store_exif(&mut self, exif_data: InsertImageExif) -> Result<ImageExif, DbError> { fn store_exif(
use schema::image_exif::dsl::*; &mut self,
context: &opentelemetry::Context,
exif_data: InsertImageExif,
) -> Result<ImageExif, DbError> {
trace_db_call(context, "insert", "store_exif", |_span| {
use schema::image_exif::dsl::*;
let mut connection = self.connection.lock().expect("Unable to get ExifDao"); let mut connection = self.connection.lock().expect("Unable to get ExifDao");
diesel::insert_into(image_exif) diesel::insert_into(image_exif)
.values(&exif_data) .values(&exif_data)
.execute(connection.deref_mut()) .execute(connection.deref_mut())
.map_err(|_| DbError::new(DbErrorKind::InsertError))?; .map_err(|_| anyhow::anyhow!("Insert error"))?;
image_exif image_exif
.filter(file_path.eq(&exif_data.file_path)) .filter(file_path.eq(&exif_data.file_path))
.first::<ImageExif>(connection.deref_mut()) .first::<ImageExif>(connection.deref_mut())
.map_err(|_| DbError::new(DbErrorKind::QueryError)) .map_err(|_| anyhow::anyhow!("Query error"))
})
.map_err(|_| DbError::new(DbErrorKind::InsertError))
} }
fn get_exif(&mut self, path: &str) -> Result<Option<ImageExif>, DbError> { fn get_exif(
use schema::image_exif::dsl::*; &mut self,
context: &opentelemetry::Context,
path: &str,
) -> Result<Option<ImageExif>, DbError> {
trace_db_call(context, "query", "get_exif", |_span| {
use schema::image_exif::dsl::*;
let mut connection = self.connection.lock().expect("Unable to get ExifDao"); let mut connection = self.connection.lock().expect("Unable to get ExifDao");
match image_exif match image_exif
.filter(file_path.eq(path)) .filter(file_path.eq(path))
.first::<ImageExif>(connection.deref_mut()) .first::<ImageExif>(connection.deref_mut())
{ {
Ok(exif) => Ok(Some(exif)), Ok(exif) => Ok(Some(exif)),
Err(diesel::result::Error::NotFound) => Ok(None), Err(diesel::result::Error::NotFound) => Ok(None),
Err(_) => Err(DbError::new(DbErrorKind::QueryError)), Err(_) => Err(anyhow::anyhow!("Query error")),
} }
})
.map_err(|_| DbError::new(DbErrorKind::QueryError))
} }
fn update_exif(&mut self, exif_data: InsertImageExif) -> Result<ImageExif, DbError> { fn update_exif(
use schema::image_exif::dsl::*; &mut self,
context: &opentelemetry::Context,
exif_data: InsertImageExif,
) -> Result<ImageExif, DbError> {
trace_db_call(context, "update", "update_exif", |_span| {
use schema::image_exif::dsl::*;
let mut connection = self.connection.lock().expect("Unable to get ExifDao"); let mut connection = self.connection.lock().expect("Unable to get ExifDao");
diesel::update(image_exif.filter(file_path.eq(&exif_data.file_path))) diesel::update(image_exif.filter(file_path.eq(&exif_data.file_path)))
.set(( .set((
camera_make.eq(&exif_data.camera_make), camera_make.eq(&exif_data.camera_make),
camera_model.eq(&exif_data.camera_model), camera_model.eq(&exif_data.camera_model),
lens_model.eq(&exif_data.lens_model), lens_model.eq(&exif_data.lens_model),
width.eq(&exif_data.width), width.eq(&exif_data.width),
height.eq(&exif_data.height), height.eq(&exif_data.height),
orientation.eq(&exif_data.orientation), orientation.eq(&exif_data.orientation),
gps_latitude.eq(&exif_data.gps_latitude), gps_latitude.eq(&exif_data.gps_latitude),
gps_longitude.eq(&exif_data.gps_longitude), gps_longitude.eq(&exif_data.gps_longitude),
gps_altitude.eq(&exif_data.gps_altitude), gps_altitude.eq(&exif_data.gps_altitude),
focal_length.eq(&exif_data.focal_length), focal_length.eq(&exif_data.focal_length),
aperture.eq(&exif_data.aperture), aperture.eq(&exif_data.aperture),
shutter_speed.eq(&exif_data.shutter_speed), shutter_speed.eq(&exif_data.shutter_speed),
iso.eq(&exif_data.iso), iso.eq(&exif_data.iso),
date_taken.eq(&exif_data.date_taken), date_taken.eq(&exif_data.date_taken),
last_modified.eq(&exif_data.last_modified), last_modified.eq(&exif_data.last_modified),
)) ))
.execute(connection.deref_mut()) .execute(connection.deref_mut())
.map_err(|_| DbError::new(DbErrorKind::InsertError))?; .map_err(|_| anyhow::anyhow!("Update error"))?;
image_exif image_exif
.filter(file_path.eq(&exif_data.file_path)) .filter(file_path.eq(&exif_data.file_path))
.first::<ImageExif>(connection.deref_mut()) .first::<ImageExif>(connection.deref_mut())
.map_err(|_| DbError::new(DbErrorKind::QueryError)) .map_err(|_| anyhow::anyhow!("Query error"))
})
.map_err(|_| DbError::new(DbErrorKind::UpdateError))
} }
fn delete_exif(&mut self, path: &str) -> Result<(), DbError> { fn delete_exif(&mut self, context: &opentelemetry::Context, path: &str) -> Result<(), DbError> {
use schema::image_exif::dsl::*; trace_db_call(context, "delete", "delete_exif", |_span| {
use schema::image_exif::dsl::*;
diesel::delete(image_exif.filter(file_path.eq(path))) diesel::delete(image_exif.filter(file_path.eq(path)))
.execute(self.connection.lock().unwrap().deref_mut()) .execute(self.connection.lock().unwrap().deref_mut())
.map(|_| ()) .map(|_| ())
.map_err(|_| DbError::new(DbErrorKind::QueryError)) .map_err(|_| anyhow::anyhow!("Delete error"))
})
.map_err(|_| DbError::new(DbErrorKind::QueryError))
} }
fn get_all_with_date_taken(&mut self) -> Result<Vec<(String, i64)>, DbError> { fn get_all_with_date_taken(
use schema::image_exif::dsl::*; &mut self,
context: &opentelemetry::Context,
) -> Result<Vec<(String, i64)>, DbError> {
trace_db_call(context, "query", "get_all_with_date_taken", |_span| {
use schema::image_exif::dsl::*;
let mut connection = self.connection.lock().expect("Unable to get ExifDao"); let mut connection = self.connection.lock().expect("Unable to get ExifDao");
image_exif image_exif
.select((file_path, date_taken)) .select((file_path, date_taken))
.filter(date_taken.is_not_null()) .filter(date_taken.is_not_null())
.load::<(String, Option<i64>)>(connection.deref_mut()) .load::<(String, Option<i64>)>(connection.deref_mut())
.map(|records| { .map(|records| {
records records
.into_iter() .into_iter()
.filter_map(|(path, dt)| dt.map(|ts| (path, ts))) .filter_map(|(path, dt)| dt.map(|ts| (path, ts)))
.collect() .collect()
}) })
.map_err(|_| DbError::new(DbErrorKind::QueryError)) .map_err(|_| anyhow::anyhow!("Query error"))
})
.map_err(|_| DbError::new(DbErrorKind::QueryError))
} }
fn get_exif_batch(&mut self, file_paths: &[String]) -> Result<Vec<ImageExif>, DbError> { fn get_exif_batch(
use schema::image_exif::dsl::*; &mut self,
context: &opentelemetry::Context,
file_paths: &[String],
) -> Result<Vec<ImageExif>, DbError> {
trace_db_call(context, "query", "get_exif_batch", |_span| {
use schema::image_exif::dsl::*;
if file_paths.is_empty() { if file_paths.is_empty() {
return Ok(Vec::new()); return Ok(Vec::new());
} }
let mut connection = self.connection.lock().expect("Unable to get ExifDao"); let mut connection = self.connection.lock().expect("Unable to get ExifDao");
image_exif image_exif
.filter(file_path.eq_any(file_paths)) .filter(file_path.eq_any(file_paths))
.load::<ImageExif>(connection.deref_mut()) .load::<ImageExif>(connection.deref_mut())
.map_err(|_| DbError::new(DbErrorKind::QueryError)) .map_err(|_| anyhow::anyhow!("Query error"))
})
.map_err(|_| DbError::new(DbErrorKind::QueryError))
} }
fn query_by_exif( fn query_by_exif(
&mut self, &mut self,
context: &opentelemetry::Context,
camera_make_filter: Option<&str>, camera_make_filter: Option<&str>,
camera_model_filter: Option<&str>, camera_model_filter: Option<&str>,
lens_model_filter: Option<&str>, lens_model_filter: Option<&str>,
@@ -384,88 +458,111 @@ impl ExifDao for SqliteExifDao {
date_from: Option<i64>, date_from: Option<i64>,
date_to: Option<i64>, date_to: Option<i64>,
) -> Result<Vec<ImageExif>, DbError> { ) -> Result<Vec<ImageExif>, DbError> {
use schema::image_exif::dsl::*; trace_db_call(context, "query", "query_by_exif", |_span| {
use schema::image_exif::dsl::*;
let mut connection = self.connection.lock().expect("Unable to get ExifDao"); let mut connection = self.connection.lock().expect("Unable to get ExifDao");
let mut query = image_exif.into_boxed(); let mut query = image_exif.into_boxed();
// Camera filters (case-insensitive partial match) // Camera filters (case-insensitive partial match)
if let Some(make) = camera_make_filter { if let Some(make) = camera_make_filter {
query = query.filter(camera_make.like(format!("%{}%", make))); query = query.filter(camera_make.like(format!("%{}%", make)));
} }
if let Some(model) = camera_model_filter { if let Some(model) = camera_model_filter {
query = query.filter(camera_model.like(format!("%{}%", model))); query = query.filter(camera_model.like(format!("%{}%", model)));
} }
if let Some(lens) = lens_model_filter { if let Some(lens) = lens_model_filter {
query = query.filter(lens_model.like(format!("%{}%", lens))); query = query.filter(lens_model.like(format!("%{}%", lens)));
} }
// GPS bounding box // GPS bounding box
if let Some((min_lat, max_lat, min_lon, max_lon)) = gps_bounds { if let Some((min_lat, max_lat, min_lon, max_lon)) = gps_bounds {
query = query query = query
.filter(gps_latitude.between(min_lat, max_lat)) .filter(gps_latitude.between(min_lat, max_lat))
.filter(gps_longitude.between(min_lon, max_lon)) .filter(gps_longitude.between(min_lon, max_lon))
.filter(gps_latitude.is_not_null()) .filter(gps_latitude.is_not_null())
.filter(gps_longitude.is_not_null()); .filter(gps_longitude.is_not_null());
} }
// Date range // Date range
if let Some(from) = date_from { if let Some(from) = date_from {
query = query.filter(date_taken.ge(from)); query = query.filter(date_taken.ge(from));
} }
if let Some(to) = date_to { if let Some(to) = date_to {
query = query.filter(date_taken.le(to)); query = query.filter(date_taken.le(to));
} }
if date_from.is_some() || date_to.is_some() { if date_from.is_some() || date_to.is_some() {
query = query.filter(date_taken.is_not_null()); query = query.filter(date_taken.is_not_null());
} }
query query
.load::<ImageExif>(connection.deref_mut()) .load::<ImageExif>(connection.deref_mut())
.map_err(|_| DbError::new(DbErrorKind::QueryError)) .map_err(|_| anyhow::anyhow!("Query error"))
})
.map_err(|_| DbError::new(DbErrorKind::QueryError))
} }
fn get_camera_makes(&mut self) -> Result<Vec<(String, i64)>, DbError> { fn get_camera_makes(
use diesel::dsl::count; &mut self,
use schema::image_exif::dsl::*; context: &opentelemetry::Context,
) -> Result<Vec<(String, i64)>, DbError> {
trace_db_call(context, "query", "get_camera_makes", |_span| {
use diesel::dsl::count;
use schema::image_exif::dsl::*;
let mut connection = self.connection.lock().expect("Unable to get ExifDao"); let mut connection = self.connection.lock().expect("Unable to get ExifDao");
image_exif image_exif
.filter(camera_make.is_not_null()) .filter(camera_make.is_not_null())
.group_by(camera_make) .group_by(camera_make)
.select((camera_make, count(id))) .select((camera_make, count(id)))
.order(count(id).desc()) .order(count(id).desc())
.load::<(Option<String>, i64)>(connection.deref_mut()) .load::<(Option<String>, i64)>(connection.deref_mut())
.map(|records| { .map(|records| {
records records
.into_iter() .into_iter()
.filter_map(|(make, cnt)| make.map(|m| (m, cnt))) .filter_map(|(make, cnt)| make.map(|m| (m, cnt)))
.collect() .collect()
}) })
.map_err(|_| DbError::new(DbErrorKind::QueryError)) .map_err(|_| anyhow::anyhow!("Query error"))
})
.map_err(|_| DbError::new(DbErrorKind::QueryError))
} }
fn update_file_path(&mut self, old_path: &str, new_path: &str) -> Result<(), DbError> { fn update_file_path(
use schema::image_exif::dsl::*; &mut self,
context: &opentelemetry::Context,
old_path: &str,
new_path: &str,
) -> Result<(), DbError> {
trace_db_call(context, "update", "update_file_path", |_span| {
use schema::image_exif::dsl::*;
let mut connection = self.connection.lock().expect("Unable to get ExifDao"); let mut connection = self.connection.lock().expect("Unable to get ExifDao");
diesel::update(image_exif.filter(file_path.eq(old_path))) diesel::update(image_exif.filter(file_path.eq(old_path)))
.set(file_path.eq(new_path)) .set(file_path.eq(new_path))
.execute(connection.deref_mut()) .execute(connection.deref_mut())
.map_err(|_| DbError::new(DbErrorKind::UpdateError))?; .map_err(|_| anyhow::anyhow!("Update error"))?;
Ok(()) Ok(())
})
.map_err(|_| DbError::new(DbErrorKind::UpdateError))
} }
fn get_all_file_paths(&mut self) -> Result<Vec<String>, DbError> { fn get_all_file_paths(
use schema::image_exif::dsl::*; &mut self,
context: &opentelemetry::Context,
) -> Result<Vec<String>, DbError> {
trace_db_call(context, "query", "get_all_file_paths", |_span| {
use schema::image_exif::dsl::*;
let mut connection = self.connection.lock().expect("Unable to get ExifDao"); let mut connection = self.connection.lock().expect("Unable to get ExifDao");
image_exif image_exif
.select(file_path) .select(file_path)
.load(connection.deref_mut()) .load(connection.deref_mut())
.map_err(|_| DbError::new(DbErrorKind::QueryError)) .map_err(|_| anyhow::anyhow!("Query error"))
})
.map_err(|_| DbError::new(DbErrorKind::QueryError))
} }
} }

View File

@@ -143,6 +143,7 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
let mut exif_dao_guard = exif_dao.lock().expect("Unable to get ExifDao"); let mut exif_dao_guard = exif_dao.lock().expect("Unable to get ExifDao");
let exif_results = exif_dao_guard let exif_results = exif_dao_guard
.query_by_exif( .query_by_exif(
&span_context,
req.camera_make.as_deref(), req.camera_make.as_deref(),
req.camera_model.as_deref(), req.camera_model.as_deref(),
req.lens_model.as_deref(), req.lens_model.as_deref(),
@@ -276,7 +277,7 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
// Batch fetch EXIF data // Batch fetch EXIF data
let mut exif_dao_guard = exif_dao.lock().expect("Unable to get ExifDao"); let mut exif_dao_guard = exif_dao.lock().expect("Unable to get ExifDao");
let exif_map: std::collections::HashMap<String, i64> = exif_dao_guard let exif_map: std::collections::HashMap<String, i64> = exif_dao_guard
.get_exif_batch(&file_paths) .get_exif_batch(&span_context, &file_paths)
.unwrap_or_default() .unwrap_or_default()
.into_iter() .into_iter()
.filter_map(|exif| exif.date_taken.map(|dt| (exif.file_path, dt))) .filter_map(|exif| exif.date_taken.map(|dt| (exif.file_path, dt)))
@@ -451,7 +452,7 @@ pub async fn list_photos<TagD: TagDao, FS: FileSystemAccess>(
// Batch fetch EXIF data // Batch fetch EXIF data
let mut exif_dao_guard = exif_dao.lock().expect("Unable to get ExifDao"); let mut exif_dao_guard = exif_dao.lock().expect("Unable to get ExifDao");
let exif_map: std::collections::HashMap<String, i64> = exif_dao_guard let exif_map: std::collections::HashMap<String, i64> = exif_dao_guard
.get_exif_batch(&file_paths) .get_exif_batch(&span_context, &file_paths)
.unwrap_or_default() .unwrap_or_default()
.into_iter() .into_iter()
.filter_map(|exif| exif.date_taken.map(|dt| (exif.file_path, dt))) .filter_map(|exif| exif.date_taken.map(|dt| (exif.file_path, dt)))
@@ -896,6 +897,7 @@ mod tests {
impl crate::database::ExifDao for MockExifDao { impl crate::database::ExifDao for MockExifDao {
fn store_exif( fn store_exif(
&mut self, &mut self,
_context: &opentelemetry::Context,
data: crate::database::models::InsertImageExif, data: crate::database::models::InsertImageExif,
) -> Result<crate::database::models::ImageExif, crate::database::DbError> { ) -> Result<crate::database::models::ImageExif, crate::database::DbError> {
// Return a dummy ImageExif for tests // Return a dummy ImageExif for tests
@@ -923,6 +925,7 @@ mod tests {
fn get_exif( fn get_exif(
&mut self, &mut self,
_context: &opentelemetry::Context,
_: &str, _: &str,
) -> Result<Option<crate::database::models::ImageExif>, crate::database::DbError> { ) -> Result<Option<crate::database::models::ImageExif>, crate::database::DbError> {
Ok(None) Ok(None)
@@ -930,6 +933,7 @@ mod tests {
fn update_exif( fn update_exif(
&mut self, &mut self,
_context: &opentelemetry::Context,
data: crate::database::models::InsertImageExif, data: crate::database::models::InsertImageExif,
) -> Result<crate::database::models::ImageExif, crate::database::DbError> { ) -> Result<crate::database::models::ImageExif, crate::database::DbError> {
// Return a dummy ImageExif for tests // Return a dummy ImageExif for tests
@@ -955,18 +959,24 @@ mod tests {
}) })
} }
fn delete_exif(&mut self, _: &str) -> Result<(), crate::database::DbError> { fn delete_exif(
&mut self,
_context: &opentelemetry::Context,
_: &str,
) -> Result<(), crate::database::DbError> {
Ok(()) Ok(())
} }
fn get_all_with_date_taken( fn get_all_with_date_taken(
&mut self, &mut self,
_context: &opentelemetry::Context,
) -> Result<Vec<(String, i64)>, crate::database::DbError> { ) -> Result<Vec<(String, i64)>, crate::database::DbError> {
Ok(Vec::new()) Ok(Vec::new())
} }
fn get_exif_batch( fn get_exif_batch(
&mut self, &mut self,
_context: &opentelemetry::Context,
_: &[String], _: &[String],
) -> Result<Vec<crate::database::models::ImageExif>, crate::database::DbError> { ) -> Result<Vec<crate::database::models::ImageExif>, crate::database::DbError> {
Ok(Vec::new()) Ok(Vec::new())
@@ -974,6 +984,7 @@ mod tests {
fn query_by_exif( fn query_by_exif(
&mut self, &mut self,
_context: &opentelemetry::Context,
_: Option<&str>, _: Option<&str>,
_: Option<&str>, _: Option<&str>,
_: Option<&str>, _: Option<&str>,
@@ -984,15 +995,26 @@ mod tests {
Ok(Vec::new()) Ok(Vec::new())
} }
fn get_camera_makes(&mut self) -> Result<Vec<(String, i64)>, crate::database::DbError> { fn get_camera_makes(
&mut self,
_context: &opentelemetry::Context,
) -> Result<Vec<(String, i64)>, crate::database::DbError> {
Ok(Vec::new()) Ok(Vec::new())
} }
fn update_file_path(&mut self, old_path: &str, new_path: &str) -> Result<(), DbError> { fn update_file_path(
&mut self,
_context: &opentelemetry::Context,
_old_path: &str,
_new_path: &str,
) -> Result<(), DbError> {
Ok(()) Ok(())
} }
fn get_all_file_paths(&mut self) -> Result<Vec<String>, DbError> { fn get_all_file_paths(
&mut self,
_context: &opentelemetry::Context,
) -> Result<Vec<String>, DbError> {
Ok(Vec::new()) Ok(Vec::new())
} }
} }

View File

@@ -158,6 +158,7 @@ async fn get_file_metadata(
let tracer = global_tracer(); let tracer = global_tracer();
let context = extract_context_from_request(&request); let context = extract_context_from_request(&request);
let mut span = tracer.start_with_context("get_file_metadata", &context); let mut span = tracer.start_with_context("get_file_metadata", &context);
let span_context = opentelemetry::Context::current();
let full_path = is_valid_full_path(&app_state.base_path, &path.path, false); let full_path = is_valid_full_path(&app_state.base_path, &path.path, false);
@@ -171,7 +172,7 @@ async fn get_file_metadata(
// Query EXIF data if available // Query EXIF data if available
if let Ok(mut dao) = exif_dao.lock() if let Ok(mut dao) = exif_dao.lock()
&& let Ok(Some(exif)) = dao.get_exif(&path.path) && let Ok(Some(exif)) = dao.get_exif(&span_context, &path.path)
{ {
response.exif = Some(exif.into()); response.exif = Some(exif.into());
} }
@@ -205,6 +206,7 @@ async fn upload_image(
let tracer = global_tracer(); let tracer = global_tracer();
let context = extract_context_from_request(&request); let context = extract_context_from_request(&request);
let mut span = tracer.start_with_context("upload_image", &context); let mut span = tracer.start_with_context("upload_image", &context);
let span_context = opentelemetry::Context::current();
let mut file_content: BytesMut = BytesMut::new(); let mut file_content: BytesMut = BytesMut::new();
let mut file_name: Option<String> = None; let mut file_name: Option<String> = None;
@@ -305,7 +307,7 @@ async fn upload_image(
}; };
if let Ok(mut dao) = exif_dao.lock() { if let Ok(mut dao) = exif_dao.lock() {
if let Err(e) = dao.store_exif(insert_exif) { if let Err(e) = dao.store_exif(&span_context, insert_exif) {
error!("Failed to store EXIF data for {}: {:?}", relative_path, e); error!("Failed to store EXIF data for {}: {:?}", relative_path, e);
} else { } else {
debug!("EXIF data stored for {}", relative_path); debug!("EXIF data stored for {}", relative_path);
@@ -877,6 +879,7 @@ fn process_new_files(
exif_dao: Arc<Mutex<Box<dyn ExifDao>>>, exif_dao: Arc<Mutex<Box<dyn ExifDao>>>,
modified_since: Option<SystemTime>, modified_since: Option<SystemTime>,
) { ) {
let context = opentelemetry::Context::new();
let thumbs = dotenv::var("THUMBNAILS").expect("THUMBNAILS not defined"); let thumbs = dotenv::var("THUMBNAILS").expect("THUMBNAILS not defined");
let thumbnail_directory = Path::new(&thumbs); let thumbnail_directory = Path::new(&thumbs);
@@ -922,7 +925,7 @@ fn process_new_files(
let existing_exif_paths: HashMap<String, bool> = { let existing_exif_paths: HashMap<String, bool> = {
let mut dao = exif_dao.lock().expect("Unable to lock ExifDao"); let mut dao = exif_dao.lock().expect("Unable to lock ExifDao");
match dao.get_exif_batch(&file_paths) { match dao.get_exif_batch(&context, &file_paths) {
Ok(exif_records) => exif_records Ok(exif_records) => exif_records
.into_iter() .into_iter()
.map(|record| (record.file_path, true)) .map(|record| (record.file_path, true))
@@ -995,7 +998,7 @@ fn process_new_files(
}; };
let mut dao = exif_dao.lock().expect("Unable to lock ExifDao"); let mut dao = exif_dao.lock().expect("Unable to lock ExifDao");
if let Err(e) = dao.store_exif(insert_exif) { if let Err(e) = dao.store_exif(&context, insert_exif) {
error!("Failed to store EXIF data for {}: {:?}", relative_path, e); error!("Failed to store EXIF data for {}: {:?}", relative_path, e);
} else { } else {
debug!("EXIF data stored for {}", relative_path); debug!("EXIF data stored for {}", relative_path);

View File

@@ -335,6 +335,7 @@ pub fn extract_date_from_filename(filename: &str) -> Option<DateTime<FixedOffset
/// Collect memories from EXIF database /// Collect memories from EXIF database
fn collect_exif_memories( fn collect_exif_memories(
exif_dao: &Data<Mutex<Box<dyn ExifDao>>>, exif_dao: &Data<Mutex<Box<dyn ExifDao>>>,
context: &opentelemetry::Context,
base_path: &str, base_path: &str,
now: NaiveDate, now: NaiveDate,
span_mode: MemoriesSpan, span_mode: MemoriesSpan,
@@ -344,7 +345,7 @@ fn collect_exif_memories(
) -> Vec<(MemoryItem, NaiveDate)> { ) -> Vec<(MemoryItem, NaiveDate)> {
// Query database for all files with date_taken // Query database for all files with date_taken
let exif_records = match exif_dao.lock() { let exif_records = match exif_dao.lock() {
Ok(mut dao) => match dao.get_all_with_date_taken() { Ok(mut dao) => match dao.get_all_with_date_taken(context) {
Ok(records) => records, Ok(records) => records,
Err(e) => { Err(e) => {
warn!("Failed to query EXIF database: {:?}", e); warn!("Failed to query EXIF database: {:?}", e);
@@ -471,8 +472,9 @@ pub async fn list_memories(
exif_dao: Data<Mutex<Box<dyn ExifDao>>>, exif_dao: Data<Mutex<Box<dyn ExifDao>>>,
) -> impl Responder { ) -> impl Responder {
let tracer = global_tracer(); let tracer = global_tracer();
let context = extract_context_from_request(&request); let parent_context = extract_context_from_request(&request);
let mut span = tracer.start_with_context("list_memories", &context); let mut span = tracer.start_with_context("list_memories", &parent_context);
let span_context = opentelemetry::Context::current();
let span_mode = q.span.unwrap_or(MemoriesSpan::Day); let span_mode = q.span.unwrap_or(MemoriesSpan::Day);
let years_back: u32 = 15; let years_back: u32 = 15;
@@ -506,6 +508,7 @@ pub async fn list_memories(
// Phase 1: Query EXIF database // Phase 1: Query EXIF database
let exif_memories = collect_exif_memories( let exif_memories = collect_exif_memories(
&exif_dao, &exif_dao,
&span_context,
&app_state.base_path, &app_state.base_path,
now, now,
span_mode, span_mode,

View File

@@ -113,7 +113,7 @@ async fn get_all_tags<D: TagDao>(
let camera_makes = exif_dao let camera_makes = exif_dao
.lock() .lock()
.expect("Unable to get ExifDao") .expect("Unable to get ExifDao")
.get_camera_makes() .get_camera_makes(&span_context)
.unwrap_or_else(|e| { .unwrap_or_else(|e| {
log::warn!("Failed to get camera makes: {:?}", e); log::warn!("Failed to get camera makes: {:?}", e);
Vec::new() Vec::new()
@@ -591,7 +591,7 @@ impl TagDao for SqliteTagDao {
&mut self, &mut self,
old_name: &str, old_name: &str,
new_name: &str, new_name: &str,
context: &opentelemetry::Context, _context: &opentelemetry::Context,
) -> anyhow::Result<()> { ) -> anyhow::Result<()> {
use crate::database::schema::tagged_photo::dsl::*; use crate::database::schema::tagged_photo::dsl::*;
@@ -603,7 +603,7 @@ impl TagDao for SqliteTagDao {
fn get_all_photo_names( fn get_all_photo_names(
&mut self, &mut self,
context: &opentelemetry::Context, _context: &opentelemetry::Context,
) -> anyhow::Result<Vec<String>> { ) -> anyhow::Result<Vec<String>> {
use crate::database::schema::tagged_photo::dsl::*; use crate::database::schema::tagged_photo::dsl::*;