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

+14 -47
View File
@@ -269,7 +269,7 @@ pub async fn get_prefs(_claims: Claims, app_state: web::Data<AppState>) -> HttpR
}));
}
};
match dao.get_prefs(&opentelemetry::Context::new()) {
match dao.get_prefs() {
Ok(row) => HttpResponse::Ok().json(row_to_response(row.as_ref())),
Err(e) => HttpResponse::InternalServerError().json(json!({
"error": format!("failed to read prefs: {e}")
@@ -316,8 +316,7 @@ pub async fn put_prefs(
}));
}
};
let ctx = opentelemetry::Context::new();
let existing = match dao.get_prefs(&ctx) {
let existing = match dao.get_prefs() {
Ok(row) => row,
Err(e) => {
return HttpResponse::InternalServerError().json(json!({
@@ -362,7 +361,7 @@ pub async fn put_prefs(
reel_max_segments: merged.reel_max_segments,
updated_at: now,
};
match dao.upsert_prefs(&ctx, &upsert) {
match dao.upsert_prefs(&upsert) {
Ok(()) => HttpResponse::Ok().json(resp),
Err(e) => HttpResponse::InternalServerError().json(json!({
"error": format!("failed to write prefs: {e}")
@@ -413,8 +412,7 @@ mod tests {
}
fn seed(dao: &mut SqliteUserAiPrefsDao, row: UpsertUserAiPrefs) {
dao.upsert_prefs(&opentelemetry::Context::new(), &row)
.expect("seed prefs");
dao.upsert_prefs(&row).expect("seed prefs");
}
fn seeded_row(
@@ -594,10 +592,7 @@ mod tests {
assert_eq!(resp.status(), 200);
let mut dao = state.user_ai_prefs_dao.lock().unwrap();
let row = dao
.get_prefs(&opentelemetry::Context::new())
.unwrap()
.unwrap();
let row = dao.get_prefs().unwrap().unwrap();
assert_eq!(row.voice.as_deref(), Some("dad"));
assert_eq!(row.library.as_deref(), Some("1"));
assert_eq!(row.display_tz.as_deref(), Some("America/New_York"));
@@ -634,10 +629,7 @@ mod tests {
assert_eq!(resp.status(), 200);
let mut dao = state.user_ai_prefs_dao.lock().unwrap();
let row = dao
.get_prefs(&opentelemetry::Context::new())
.unwrap()
.unwrap();
let row = dao.get_prefs().unwrap().unwrap();
assert!(row.voice.is_none());
assert_eq!(row.library.as_deref(), Some("1"));
assert_eq!(row.display_tz.as_deref(), Some("America/New_York"));
@@ -667,10 +659,7 @@ mod tests {
assert_eq!(resp.status(), 200);
let mut dao = state.user_ai_prefs_dao.lock().unwrap();
let row = dao
.get_prefs(&opentelemetry::Context::new())
.unwrap()
.unwrap();
let row = dao.get_prefs().unwrap().unwrap();
assert_eq!(row.library.as_deref(), Some("1"));
assert!(row.voice.is_none());
assert!(row.display_tz.is_none());
@@ -705,10 +694,7 @@ mod tests {
// Nothing written — not even the valid field in the same body.
let mut dao = state.user_ai_prefs_dao.lock().unwrap();
let row = dao
.get_prefs(&opentelemetry::Context::new())
.unwrap()
.unwrap();
let row = dao.get_prefs().unwrap().unwrap();
assert_eq!(row.voice.as_deref(), Some("grandma"));
assert_eq!(row.display_tz.as_deref(), Some("America/New_York"));
assert_eq!(row.updated_at, 1_700_000_000);
@@ -738,10 +724,7 @@ mod tests {
assert_eq!(resp.status(), 200);
let mut dao = state.user_ai_prefs_dao.lock().unwrap();
let row = dao
.get_prefs(&opentelemetry::Context::new())
.unwrap()
.unwrap();
let row = dao.get_prefs().unwrap().unwrap();
assert_eq!(row.display_tz.as_deref(), Some("Europe/Berlin"));
}
@@ -771,11 +754,7 @@ mod tests {
assert_eq!(body["updated_at"], serde_json::Value::Null);
let mut dao = state.user_ai_prefs_dao.lock().unwrap();
assert!(
dao.get_prefs(&opentelemetry::Context::new())
.unwrap()
.is_none()
);
assert!(dao.get_prefs().unwrap().is_none());
}
// --- GET /timezones -----------------------------------------------------
@@ -918,10 +897,7 @@ mod tests {
assert_eq!(resp.status(), 200);
let mut dao = state.user_ai_prefs_dao.lock().unwrap();
let row = dao
.get_prefs(&opentelemetry::Context::new())
.unwrap()
.unwrap();
let row = dao.get_prefs().unwrap().unwrap();
assert_eq!(row.default_model.as_deref(), Some("llama3.2:3b"));
assert_eq!(row.default_backend.as_deref(), Some("hybrid"));
assert_eq!(row.default_persona_id.as_deref(), Some("cabin"));
@@ -963,10 +939,7 @@ mod tests {
assert_eq!(resp.status(), 200);
let mut dao = state.user_ai_prefs_dao.lock().unwrap();
let row = dao
.get_prefs(&opentelemetry::Context::new())
.unwrap()
.unwrap();
let row = dao.get_prefs().unwrap().unwrap();
assert!(row.default_model.is_none());
assert!(row.default_backend.is_none());
assert!(row.default_persona_id.is_none());
@@ -1004,10 +977,7 @@ mod tests {
assert_eq!(resp.status(), 400);
let mut dao = state.user_ai_prefs_dao.lock().unwrap();
let row = dao
.get_prefs(&opentelemetry::Context::new())
.unwrap()
.unwrap();
let row = dao.get_prefs().unwrap().unwrap();
// Nothing written — not even the valid field in the same body.
assert_eq!(row.voice.as_deref(), Some("grandma"));
assert!(row.default_backend.is_none());
@@ -1056,10 +1026,7 @@ mod tests {
assert_eq!(resp.status(), 400);
let mut dao = state.user_ai_prefs_dao.lock().unwrap();
let row = dao
.get_prefs(&opentelemetry::Context::new())
.unwrap()
.unwrap();
let row = dao.get_prefs().unwrap().unwrap();
assert!(row.reel_span.is_none());
assert_eq!(row.updated_at, 1_700_000_000);
}