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:
1 parent
1c508cdabd
commit
b6deca2a52
55 files changed
+1942
-3852
No files matched your search
+24
-56
@@ -4,6 +4,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Mutex;
|
||||
use tracing_opentelemetry::OpenTelemetrySpanExt;
|
||||
|
||||
use actix_files::NamedFile;
|
||||
use actix_web::{
|
||||
@@ -12,7 +13,7 @@ use actix_web::{
|
||||
};
|
||||
use log::{debug, error, info, warn};
|
||||
use opentelemetry::KeyValue;
|
||||
use opentelemetry::trace::{Span, Status, Tracer};
|
||||
use opentelemetry::trace::Status;
|
||||
use serde::Serialize;
|
||||
|
||||
use crate::content_hash;
|
||||
@@ -23,7 +24,6 @@ use crate::data::{
|
||||
use crate::database::{ExifDao, PreviewDao};
|
||||
use crate::files::is_valid_full_path;
|
||||
use crate::libraries;
|
||||
use crate::otel::{extract_context_from_request, global_tracer};
|
||||
use crate::state::AppState;
|
||||
use crate::video::actors::{
|
||||
GeneratePreviewClipMessage, QueueVideosMessage, VideoToQueue, probe_video_stream_meta,
|
||||
@@ -56,16 +56,14 @@ struct GenerateVideoResponse {
|
||||
}
|
||||
|
||||
#[post("/video/generate")]
|
||||
#[tracing::instrument(name = "generate_video", skip_all)]
|
||||
pub async fn generate_video(
|
||||
_claims: Claims,
|
||||
request: HttpRequest,
|
||||
app_state: Data<AppState>,
|
||||
exif_dao: Data<std::sync::Mutex<Box<dyn ExifDao>>>,
|
||||
body: web::Json<ThumbnailRequest>,
|
||||
) -> impl Responder {
|
||||
let tracer = global_tracer();
|
||||
let context = extract_context_from_request(&request);
|
||||
let mut span = tracer.start_with_context("generate_video", &context);
|
||||
let span = tracing::Span::current();
|
||||
|
||||
let preferred_library =
|
||||
libraries::resolve_library_param_state(&app_state, body.library.as_deref())
|
||||
@@ -106,11 +104,7 @@ pub async fn generate_video(
|
||||
// already-ingested videos.
|
||||
let hash_from_db: Option<String> = {
|
||||
let mut dao = exif_dao.lock().expect("Unable to lock ExifDao");
|
||||
match dao.get_exif_batch(
|
||||
&context,
|
||||
Some(resolved_library_id),
|
||||
std::slice::from_ref(&rel_path),
|
||||
) {
|
||||
match dao.get_exif_batch(Some(resolved_library_id), std::slice::from_ref(&rel_path)) {
|
||||
Ok(rows) => rows.into_iter().next().and_then(|r| r.content_hash),
|
||||
Err(e) => {
|
||||
warn!(
|
||||
@@ -209,15 +203,14 @@ pub async fn generate_video(
|
||||
/// shape is 400'd to defend against operators stashing other content in
|
||||
/// the hash dir.
|
||||
#[get("/video/hls/{hash}/{file}")]
|
||||
#[tracing::instrument(name = "stream_hls_file", skip_all)]
|
||||
pub async fn stream_hls_file(
|
||||
request: HttpRequest,
|
||||
_: Claims,
|
||||
path: web::Path<(String, String)>,
|
||||
app_state: Data<AppState>,
|
||||
) -> impl Responder {
|
||||
let tracer = global_tracer();
|
||||
let context = extract_context_from_request(&request);
|
||||
let mut span = tracer.start_with_context("stream_hls_file", &context);
|
||||
let span = tracing::Span::current();
|
||||
|
||||
let (hash, file) = path.into_inner();
|
||||
if !is_valid_hash(&hash) {
|
||||
@@ -319,6 +312,7 @@ fn is_allowed_hls_filename(name: &str) -> bool {
|
||||
}
|
||||
|
||||
#[get("/video/preview")]
|
||||
#[tracing::instrument(name = "get_video_preview", skip_all)]
|
||||
pub async fn get_video_preview(
|
||||
_claims: Claims,
|
||||
request: HttpRequest,
|
||||
@@ -326,9 +320,7 @@ pub async fn get_video_preview(
|
||||
app_state: Data<AppState>,
|
||||
preview_dao: Data<Mutex<Box<dyn PreviewDao>>>,
|
||||
) -> impl Responder {
|
||||
let tracer = global_tracer();
|
||||
let context = extract_context_from_request(&request);
|
||||
let mut span = tracer.start_with_context("get_video_preview", &context);
|
||||
let span = tracing::Span::current();
|
||||
|
||||
// Validate path
|
||||
let full_path = match is_valid_full_path(&app_state.base_path, &req.path, true) {
|
||||
@@ -351,7 +343,7 @@ pub async fn get_video_preview(
|
||||
// Check preview status in DB
|
||||
let preview = {
|
||||
let mut dao = preview_dao.lock().expect("Unable to lock PreviewDao");
|
||||
dao.get_preview(&context, &relative_path)
|
||||
dao.get_preview(&relative_path)
|
||||
};
|
||||
|
||||
match preview {
|
||||
@@ -369,14 +361,7 @@ pub async fn get_video_preview(
|
||||
Err(_) => {
|
||||
// File missing on disk but DB says complete - reset and regenerate
|
||||
let mut dao = preview_dao.lock().expect("Unable to lock PreviewDao");
|
||||
let _ = dao.update_status(
|
||||
&context,
|
||||
&relative_path,
|
||||
"pending",
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let _ = dao.update_status(&relative_path, "pending", None, None, None);
|
||||
app_state
|
||||
.preview_clip_generator
|
||||
.do_send(GeneratePreviewClipMessage {
|
||||
@@ -424,7 +409,7 @@ pub async fn get_video_preview(
|
||||
// No record exists - insert as pending and trigger generation
|
||||
{
|
||||
let mut dao = preview_dao.lock().expect("Unable to lock PreviewDao");
|
||||
let _ = dao.insert_preview(&context, &relative_path, "pending");
|
||||
let _ = dao.insert_preview(&relative_path, "pending");
|
||||
}
|
||||
app_state
|
||||
.preview_clip_generator
|
||||
@@ -445,16 +430,14 @@ pub async fn get_video_preview(
|
||||
}
|
||||
|
||||
#[post("/video/preview/status")]
|
||||
#[tracing::instrument(name = "get_preview_status", skip_all)]
|
||||
pub async fn get_preview_status(
|
||||
_claims: Claims,
|
||||
request: HttpRequest,
|
||||
body: web::Json<PreviewStatusRequest>,
|
||||
app_state: Data<AppState>,
|
||||
preview_dao: Data<Mutex<Box<dyn PreviewDao>>>,
|
||||
) -> impl Responder {
|
||||
let tracer = global_tracer();
|
||||
let context = extract_context_from_request(&request);
|
||||
let mut span = tracer.start_with_context("get_preview_status", &context);
|
||||
let span = tracing::Span::current();
|
||||
|
||||
// Limit to 200 paths per request
|
||||
if body.paths.len() > 200 {
|
||||
@@ -465,7 +448,7 @@ pub async fn get_preview_status(
|
||||
|
||||
let previews = {
|
||||
let mut dao = preview_dao.lock().expect("Unable to lock PreviewDao");
|
||||
dao.get_previews_batch(&context, &body.paths)
|
||||
dao.get_previews_batch(&body.paths)
|
||||
};
|
||||
|
||||
match previews {
|
||||
@@ -507,7 +490,7 @@ pub async fn get_preview_status(
|
||||
// No record exists — insert as pending and trigger generation
|
||||
{
|
||||
let mut dao = preview_dao.lock().expect("Unable to lock PreviewDao");
|
||||
let _ = dao.insert_preview(&context, path, "pending");
|
||||
let _ = dao.insert_preview(path, "pending");
|
||||
}
|
||||
|
||||
// Build full path for ffmpeg (actor needs the absolute path for input)
|
||||
@@ -687,8 +670,7 @@ mod tests {
|
||||
|
||||
// Verify the DAO now has a pending record
|
||||
let mut dao_lock = preview_dao.lock().unwrap();
|
||||
let ctx = opentelemetry::Context::new();
|
||||
let clip = dao_lock.get_preview(&ctx, "photos/new_video.mp4").unwrap();
|
||||
let clip = dao_lock.get_preview("photos/new_video.mp4").unwrap();
|
||||
assert!(clip.is_some());
|
||||
assert_eq!(clip.unwrap().status, "pending");
|
||||
}
|
||||
@@ -696,18 +678,9 @@ mod tests {
|
||||
#[actix_rt::test]
|
||||
async fn test_get_preview_status_returns_complete_with_url() {
|
||||
let mut dao = TestPreviewDao::new();
|
||||
let ctx = opentelemetry::Context::new();
|
||||
dao.insert_preview(&ctx, "photos/done.mp4", "pending")
|
||||
dao.insert_preview("photos/done.mp4", "pending").unwrap();
|
||||
dao.update_status("photos/done.mp4", "complete", Some(9.5), Some(500000), None)
|
||||
.unwrap();
|
||||
dao.update_status(
|
||||
&ctx,
|
||||
"photos/done.mp4",
|
||||
"complete",
|
||||
Some(9.5),
|
||||
Some(500000),
|
||||
None,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let preview_dao = make_preview_dao(dao);
|
||||
let app_state = Data::new(AppState::test_state());
|
||||
@@ -771,10 +744,9 @@ mod tests {
|
||||
#[actix_rt::test]
|
||||
async fn test_get_preview_status_mixed_statuses() {
|
||||
let mut dao = TestPreviewDao::new();
|
||||
let ctx = opentelemetry::Context::new();
|
||||
dao.insert_preview(&ctx, "a.mp4", "pending").unwrap();
|
||||
dao.insert_preview(&ctx, "b.mp4", "pending").unwrap();
|
||||
dao.update_status(&ctx, "b.mp4", "complete", Some(10.0), Some(100000), None)
|
||||
dao.insert_preview("a.mp4", "pending").unwrap();
|
||||
dao.insert_preview("b.mp4", "pending").unwrap();
|
||||
dao.update_status("b.mp4", "complete", Some(10.0), Some(100000), None)
|
||||
.unwrap();
|
||||
|
||||
let preview_dao = make_preview_dao(dao);
|
||||
@@ -824,15 +796,11 @@ mod tests {
|
||||
#[actix_rt::test]
|
||||
async fn test_get_preview_status_requeues_pending_and_failed() {
|
||||
let mut dao = TestPreviewDao::new();
|
||||
let ctx = opentelemetry::Context::new();
|
||||
|
||||
// Simulate stale records left from a previous server run
|
||||
dao.insert_preview(&ctx, "stale/pending.mp4", "pending")
|
||||
.unwrap();
|
||||
dao.insert_preview(&ctx, "stale/failed.mp4", "pending")
|
||||
.unwrap();
|
||||
dao.insert_preview("stale/pending.mp4", "pending").unwrap();
|
||||
dao.insert_preview("stale/failed.mp4", "pending").unwrap();
|
||||
dao.update_status(
|
||||
&ctx,
|
||||
"stale/failed.mp4",
|
||||
"failed",
|
||||
None,
|
||||
|
||||
Reference in new issue
Block a user