Raw Jaeger trace dumps (with and without incoming traceparent) plus a
derived span inventory and README describing the pre-migration span shape.
Captured from master da3901c release build against a local Jaeger; see
docs/tracing-baseline/pre-migration/README.md for the procedure and the
single span-neutral deviation (aux listen port 8088->18088).
Artifact branch only; /docs/* is gitignored, files force-added deliberately.
Pre-migration span baseline (ticket #137, map #135)
Captured from master @ da3901c ("before" the tracing migration) so the
post-migration output can be diffed for parity. See map #135 for the
migration's destination.
What was run
- Revision:
da3901c(merge of "Prefs: owner defaults"), the last master commit beforefeature/tracing-migration. - Build:
cargo build --release— release is required; the pre-migration OTel init is behind#[cfg(not(debug_assertions))], so debug builds are a no-op (no global tracer provider, no spans). - Exporter: local Jaeger all-in-one (
jaegertracing/all-in-one:latest), OTLP/gRPC on host:14317(container:4317), UI:16686. Prod SigNoz (OTLP_OTLS_ENDPOINT=http://192.168.10.50:4317) was overridden deliberately. - DB: a private
.backupcopy of the liveauth.db(21,298image_exifrows, 4 NAS-backed libraries). The live deployment (image-rs.service) was left running and untouched; all output dirs (thumbs/gifs/video/reels) point at a temp dir. - LLM backend: the live local stack —
LLM_BACKEND=llamacpp,LLAMA_SWAP_URL=http://192.168.10.50:9292/v1, modelqwen3.8-27b-abliterated.
Deviation from pristine da3901c
The binary was built from da3901c with one span-neutral change: the
hardcoded secondary listener .bind("localhost:8088") was moved to
.bind("localhost:18088") because the live image-rs.service owns :8088.
The listen port cannot affect span production. Everything else is unmodified.
Requests exercised (the representative set)
All with Authorization: Bearer <JWT> (minted with the live SECRET_KEY,
sub=2).
| # | Request | Span trace(s) |
|---|---|---|
| 1 | GET /photos?path=Colleen&recursive=true&limit=20 |
cb07eeec… (3 spans) |
| 2 | GET /memories?path=.&recursive=true |
44b3b67d… (6 spans) |
| 3 | POST /insights/generate/agentic {file_path:"Photo06172345.jpg",backend:"local"} → 202 job_id=380 (async) |
948d2756… (HTTP span) and 9b520713… (background, 22 spans) |
| 4 | POST /insights/chat/stream {file_path:"Photo06172345.jpg",user_message:"What was I wearing in this photo?",backend:"local"} (SSE, 88 text deltas, done) |
fragmented — see below |
| 5 | GET /photos?path=Colleen&recursive=true&limit=5 with traceparent: 00-1111…1111-2222…2222-01 |
11111111… (3 spans) |
Span shape (the "before")
Service: image-server (resource service.name), version from
CARGO_PKG_VERSION. No per-request HTTP server span exists — the migration's
tracing-actix-web middleware (#139) adds one.
Span names
- Operation / "root" spans (created manually in handlers):
list_photos,list_memories,http.insights.generate_agentic,ai.insight.generate_agentic,ai.agentic.loop,ai.rag.search_daily_summaries,generate_video_thumbnail. - DB spans:
db.{query_type}.{operation}—query_type ∈ {query, insert, update},operation= snake_case DAO method (get_exif,store_insight, …). Tracer scope (otel.scope.name) isdb. - LLM spans:
llm.request, tracer scopeimage-server.
span.kind: internal for every operation/DB span; client for
llm.request.
Key attributes
- Every span:
otel.scope.name(image-serverordb),otel.status_code(OK/ unset),span.kind. - DB (
db.*):db.query_type,db.operation. - LLM (
llm.request):llm.model(qwen3.8-27b-abliterated),llm.provider(llamacpp),llm.operation(chatfor tool calls,chat_streamfor the SSE turn),llm.tokens.input,llm.tokens.output,http.status_code. list_photos: rich request shape —path,recursive,sort,library,media_type,pagination.limit/offset/enabled,file_count,returned_count,total_count,optimization.batch_tags,optimization.db_sort,tag_filter_mode, plus empty camera/gps/date fields.ai.insight.generate_agentic:backend,file_path,images_inline,max_iterations,model.ai.agentic.loop:iterations_used.generate_video_thumbnail:file-name,library,type.ai.rag.search_daily_summaries:query,date,limit,results_count,time_weighted.
Nesting — the headline finding. Parent/child threading is inconsistent:
- No HTTP root span. Handlers open an operation-named span as a root.
- Most DB spans are orphaned into their own single-span trace (see
span-inventory.json:trace_count == span_countfor most ops).db.query.get_prefs,db.query.count_for_library,db.insert.store_insight,db.update.*, etc. each form a separate trace. - Threading works in pockets.
list_photos(→ 2 DB children),list_memories(→get_prefs+ 4×get_memories_in_window), and the agentic loop (ai.insight.generate_agentic→ai.agentic.loop→llm.request+ DB spans) nest correctly. - Async work loses the trace.
POST /insights/generate/agenticreturns 202 withhttp.insights.generate_agenticin one trace; the background generation is a different root trace (ai.insight.generate_agentic). The streaming chat path is likewise fragmented — its DB calls andllm.requestland as separate single-span traces, with no handler span at all. - One 222-span trace (
b94440d1…) holds the startup thumbnail sweep — allgenerate_video_thumbnailspans share one trace.
Incoming propagation WORKS. With a caller-supplied W3C traceparent, the
list_photos span is created in the caller's trace-id and references the
remote span-id as CHILD_OF — i.e. the request joins the upstream trace
correctly. This is the behavior the migration must preserve.
Files
traces-no-traceparent.json— all 33 traces from requests 1–4 (Jaeger/api/tracesdump; requests made without an incoming traceparent).traces-with-traceparent.json— includes the propagation test (request 5).span-inventory.json— per-operation summary: span count, trace count, kind, scope, status, attribute keys. The direct input for a before/after shape diff.
Re-capturing
- Build
da3901crelease (or the branch under test for the "after"). docker run -d --name jaeger -p 16686:16686 -p 14317:4317 jaegertracing/all-in-one:latest.- Run the server against a copy of the DB with
OTLP_OTLS_ENDPOINT=http://localhost:14317and a tempBIND_URL; remember the hardcoded secondarylocalhost:8088bind collides with the live service (patch it, or stop the service). - Mint a JWT (
HS256,{sub, exp},SECRET_KEY) and replay the request set in the table above. curl 'http://localhost:16686/api/traces?service=image-server&limit=600'→ regeneratespan-inventory.jsonand diff operation names, kinds, attribute keys, and nesting.