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

+47 -69
View File
@@ -1,18 +1,18 @@
use actix_web::web::Data;
use actix_web::{HttpRequest, HttpResponse, Responder, get, web};
use actix_web::{HttpResponse, Responder, get, web};
use chrono::{DateTime, Datelike, NaiveDate, NaiveDateTime, TimeZone, Utc};
use log::{debug, trace, warn};
use opentelemetry::KeyValue;
use opentelemetry::trace::{Span, Status, TraceContextExt, Tracer};
use opentelemetry::trace::Status;
use serde::{Deserialize, Serialize};
use std::path::Path;
use std::path::PathBuf;
use std::str::FromStr;
use std::sync::Mutex;
use tracing_opentelemetry::OpenTelemetrySpanExt;
use crate::data::Claims;
use crate::database::ExifDao;
use crate::otel::{extract_context_from_request, global_tracer};
use crate::state::AppState;
// Helper that encapsulates path-exclusion semantics
@@ -487,18 +487,14 @@ fn plausible_filename_date(dt: DateTime<Utc>) -> Option<DateTime<Utc>> {
pub const DEFAULT_YEARS_BACK: i32 = 20;
#[get("/memories")]
#[tracing::instrument(name = "list_memories", skip_all)]
pub async fn list_memories(
_claims: Claims,
request: HttpRequest,
q: web::Query<MemoriesRequest>,
app_state: Data<AppState>,
exif_dao: Data<Mutex<Box<dyn ExifDao>>>,
) -> impl Responder {
let tracer = global_tracer();
let parent_context = extract_context_from_request(&request);
let mut span = tracer.start_with_context("list_memories", &parent_context);
let span_context =
opentelemetry::Context::new().with_remote_span_context(span.span_context().clone());
let span = tracing::Span::current();
let span_mode = q.span.unwrap_or(MemoriesSpan::Day);
@@ -513,7 +509,7 @@ pub async fn list_memories(
.user_ai_prefs_dao
.lock()
.ok()
.and_then(|mut dao| dao.get_prefs(&span_context).ok().flatten());
.and_then(|mut dao| dao.get_prefs().ok().flatten());
crate::camera_tz::display_zone_for(prefs.as_ref().and_then(|p| p.display_tz.as_deref()))
};
let display_zone =
@@ -522,7 +518,6 @@ pub async fn list_memories(
let items = match gather_memory_items(
&app_state,
&exif_dao,
&span_context,
span_mode,
display_zone,
q.library.as_deref(),
@@ -565,7 +560,6 @@ pub async fn list_memories(
pub fn gather_memory_items(
app_state: &AppState,
exif_dao: &Mutex<Box<dyn ExifDao>>,
span_context: &opentelemetry::Context,
span_mode: MemoriesSpan,
display_zone: chrono_tz::Tz,
library_param: Option<&str>,
@@ -605,23 +599,18 @@ pub fn gather_memory_items(
let path_excluder = PathExcluder::new(base, &effective);
let rows = match exif_dao.lock() {
Ok(mut dao) => match dao.get_memories_in_window(
span_context,
lib.id,
conv_lo,
conv_hi,
unconv_lo,
unconv_hi,
) {
Ok(rows) => rows,
Err(e) => {
warn!(
"Failed to query memories for library '{}': {:?}",
lib.name, e
);
continue;
Ok(mut dao) => {
match dao.get_memories_in_window(lib.id, conv_lo, conv_hi, unconv_lo, unconv_hi) {
Ok(rows) => rows,
Err(e) => {
warn!(
"Failed to query memories for library '{}': {:?}",
lib.name, e
);
continue;
}
}
},
}
Err(e) => {
warn!("Failed to lock EXIF DAO: {:?}", e);
continue;
@@ -1339,37 +1328,34 @@ mod tests {
use crate::database::test::in_memory_db_connection;
fn seed_row(dao: &mut SqliteExifDao, rel: &str, date_taken: i64, source: &str) {
dao.store_exif(
&opentelemetry::Context::new(),
InsertImageExif {
library_id: 1,
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: Some(date_taken),
created_time: 0,
last_modified: 0,
content_hash: None,
size_bytes: None,
phash_64: None,
dhash_64: None,
date_taken_source: Some(source.to_string()),
date_utc: None,
camera_offset_minutes: None,
date_taken_tz_source: None,
},
)
dao.store_exif(InsertImageExif {
library_id: 1,
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: Some(date_taken),
created_time: 0,
last_modified: 0,
content_hash: None,
size_bytes: None,
phash_64: None,
dhash_64: None,
date_taken_source: Some(source.to_string()),
date_utc: None,
camera_offset_minutes: None,
date_taken_tz_source: None,
})
.expect("insert row");
}
@@ -1400,7 +1386,6 @@ mod tests {
let conv_instant = at_12(year - 3).and_utc().timestamp();
seed_row(&mut dao, "conv.jpg", conv_instant, "exif");
dao.backfill_date_taken(
&opentelemetry::Context::new(),
1,
"conv.jpg",
conv_instant,
@@ -1427,15 +1412,8 @@ mod tests {
seed_row(&mut dao, "unconv-vid.mp4", vid_legacy, "exiftool");
let exif_mutex: Mutex<Box<dyn ExifDao>> = Mutex::new(Box::new(dao));
let items = gather_memory_items(
&state,
&exif_mutex,
&opentelemetry::Context::new(),
MemoriesSpan::Month,
zone,
None,
)
.expect("gather");
let items = gather_memory_items(&state, &exif_mutex, MemoriesSpan::Month, zone, None)
.expect("gather");
let by_path = |p: &str| items.iter().find(|it| it.path == p);
// All three rows group into the same owner month (AC-2/AC-3).