Tracing: root spans for background jobs, retire raw OTel spans (#142)
Background work has no request parent, so each job now opens a named tracing root and carries it into its fan-out: - watcher: `watcher.tick` (scan=quick|full) entered per tick on the watcher's std thread, `watcher.library` per library, and `watcher.playlist_cleanup` per orphaned-HLS pass. face_watch / clip_watch carry the ambient span into every per-candidate tokio::spawn and give each call its own `face_watch.detect` / `clip_watch.encode` span. - thumbnails: `creating thumbnails` via #[instrument]; per-video spans on rayon workers name their parent explicitly. - video actors: `playlistgenerator.generate_playlist` and `preview_clip.generate` are tracing roots that instrument their futures. - reels: `reel.job` (created before the spawn, so on-demand jobs stay in the request trace), `reel.pregen_batch`, `reel.cache_sweep`. - RefreshThumbnailsMessage and the dead-code GIF job move to tracing. No raw OpenTelemetry span creation is left outside otel.rs; `global_tracer` is removed. tests/otel_background.rs pins the std-thread tick -> block_on -> spawn shape and the rayon explicit-parent shape. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b6deca2a52
commit
26115b3378
11 files changed
+330
-179
No files matched your search
+24
-15
@@ -25,6 +25,7 @@ use log::{debug, info, warn};
|
||||
use std::path::Path;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use tokio::sync::Semaphore;
|
||||
use tracing::Instrument;
|
||||
|
||||
/// One file the watcher would like to face-scan. Built by the caller from
|
||||
/// the EXIF batch (we need `content_hash` to key everything against).
|
||||
@@ -89,6 +90,9 @@ pub fn run_face_detection_pass(
|
||||
|
||||
let library_id = library.id;
|
||||
let library_root = library.root_path.clone();
|
||||
// The detect tasks run on this runtime's worker threads; carry the
|
||||
// watcher's span into each so their spans stay in the tick's trace.
|
||||
let parent = tracing::Span::current();
|
||||
rt.block_on(async move {
|
||||
let sem = Arc::new(Semaphore::new(concurrency));
|
||||
let mut handles = Vec::with_capacity(filtered.len());
|
||||
@@ -98,21 +102,25 @@ pub fn run_face_detection_pass(
|
||||
let face_dao = face_dao.clone();
|
||||
let tag_dao = tag_dao.clone();
|
||||
let library_root = library_root.clone();
|
||||
handles.push(tokio::spawn(async move {
|
||||
// acquire_owned would let us drop the permit explicitly
|
||||
// before await points; for a one-shot call into Apollo
|
||||
// the simpler bounded acquire is enough.
|
||||
let _permit = permit_sem.acquire().await.expect("face semaphore");
|
||||
process_one(
|
||||
library_id,
|
||||
&library_root,
|
||||
cand,
|
||||
&face_client,
|
||||
face_dao,
|
||||
tag_dao,
|
||||
)
|
||||
.await;
|
||||
}));
|
||||
let task_span = parent.clone();
|
||||
handles.push(tokio::spawn(
|
||||
async move {
|
||||
// acquire_owned would let us drop the permit explicitly
|
||||
// before await points; for a one-shot call into Apollo
|
||||
// the simpler bounded acquire is enough.
|
||||
let _permit = permit_sem.acquire().await.expect("face semaphore");
|
||||
process_one(
|
||||
library_id,
|
||||
&library_root,
|
||||
cand,
|
||||
&face_client,
|
||||
face_dao,
|
||||
tag_dao,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
.instrument(task_span),
|
||||
));
|
||||
}
|
||||
for h in handles {
|
||||
// join; per-task panics are logged inside process_one before
|
||||
@@ -122,6 +130,7 @@ pub fn run_face_detection_pass(
|
||||
});
|
||||
}
|
||||
|
||||
#[tracing::instrument(name = "face_watch.detect", skip_all, fields(rel_path = %cand.rel_path))]
|
||||
async fn process_one(
|
||||
library_id: i32,
|
||||
library_root: &str,
|
||||
|
||||
Reference in new issue
Block a user