Skip to content

fix(traces): always open vcon_processing root span, link if upstream context present#181

Merged
pavanputhra merged 2 commits into
mainfrom
fix/conserver-always-open-vcon-processing-span
May 21, 2026
Merged

fix(traces): always open vcon_processing root span, link if upstream context present#181
pavanputhra merged 2 commits into
mainfrom
fix/conserver-always-open-vcon-processing-span

Conversation

@pavanputhra
Copy link
Copy Markdown
Contributor

Summary

  • Refactor _create_span_from_context so it always returns a span context manager — never None.
  • A span Link to the producer's trace is attached only when self.context carries a valid trace_id/span_id pair. When no upstream context is propagated, the span is created with no links so child link.* / storage.* spans still nest under a single root.
  • run() setup collapses from a 25-line if/else dance to two lines; the POC trace-id verification log goes away.

Why

Before this change, if the producer didn't store_context_* to Redis before enqueueing — e.g. an adapter that ingests directly without OTel — _create_span_from_context short-circuited and every link.* / storage.* span became its own root trace. The trace-per-vCon model is now preserved regardless of producer instrumentation, and the upstream link still lights up automatically once producers do propagate context.

Tests

27 directly relevant tests pass locally (common/tests/lib/test_context_utils.py, test_inflight_counter.py, test_parallel_processing.py, test_otel_fork_safe_init.py). The unrelated link-metrics failures in the local venv are pre-existing ModuleNotFoundError for optional deps (openai/groq/ffmpeg/transformers).

Test plan

  • Roll the resulting image and confirm vcon_processing.<chain> spans appear in SignOz with link.* / storage.* spans nested under them, one trace per vCon.
  • When a producer later begins propagating context, confirm the span link to the upstream trace shows up in SignOz's trace view.

🤖 Generated with Claude Code

pavanputhra and others added 2 commits May 21, 2026 01:29
… global

Follow-up to #178. The fork-safe init landed, but
SignOz queries still showed only one worker's value (peak inflight 128
across 4 worker processes instead of the expected ~512). Root cause:
the user-provided OTel resource (``host.name``, ``service.instance.id``)
never reached the metrics backend.

OpenTelemetry Python's ``metrics.set_meter_provider()`` is single-call.
When ``opentelemetry-instrumentation`` (auto-instrumentation) is active
in the process, it registers a default MeterProvider early in startup —
before this module's lazy ``_init_otel_metrics()`` runs. Our subsequent
``set_meter_provider(our_provider)`` call is silently ignored, and
``metrics.get_meter(__name__)`` returns the global proxy bound to the
auto-instrumentation's provider — whose resource we don't control.

The visible CH symptom: ``resource_attrs`` on every conserver.*
metric only contains ``service.name`` + ``telemetry.sdk.*`` +
``telemetry.auto.version``; ``host.name`` and ``service.instance.id``
that this module sets are dropped. All four worker processes collapse
onto a single fingerprint, last-write-wins.

Fix: bind ``meter`` to OUR provider directly via
``provider.get_meter(__name__)``, never touching the global. Our
provider has the correct resource and its own export pipeline to
the OTel collector. Auto-instrumentation metrics continue to flow
through their own pipeline independently — both arrive at the
collector.

Tests updated to assert ``set_meter_provider`` is NOT called and the
``meter`` global is bound to the provider's own meter, not the global
proxy.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…context present

Previously the vcon_processing.<chain> span was only created when
self.context carried an upstream trace context (extracted from Redis
under context:{ingress}:{vcon_uuid}). When the producer didn't store
a context — e.g. an adapter that ingests directly to the ingress list
without OTel instrumentation — _create_span_from_context short-
circuited to None and every link.* / storage.* span emitted during
the chain run became its own root trace, defeating the
trace-per-vcon model.

Refactor _create_span_from_context to always return a span context
manager. A span link to the producer's trace is attached only when
trace_id and span_id parse to non-zero values; otherwise the span
opens without links. Trace structure is preserved either way, and
the link semantics still light up automatically once producers (e.g.
the BDS adapter) begin propagating context via store_context_*.

Net: -27 lines (139 changed, 56+/83-). Run-method setup collapses
from a 25-line if/else dance to two lines; the POC trace-id
verification log goes away.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pavanputhra pavanputhra merged commit 4f41488 into main May 21, 2026
@pavanputhra pavanputhra deleted the fix/conserver-always-open-vcon-processing-span branch May 21, 2026 06:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant