Don't require the parent span to have a tracer in traced_from_parent_span() - #788
Merged
Conversation
Both rebalance callbacks trace their work from the span
`_start_span_from_rebalancing()` returns, which is a no-op span whenever
no tracer is configured. The no-op stand-in Faust falls back to when the
`faust[opentracing]` extra is missing gave its spans no tracer, while
`traced_from_parent_span()` starts its child span from `parent.tracer`,
so every rebalance raised:
AttributeError: 'NoneType' object has no attribute 'start_span'
`on_rebalance_start()` has already run by then, and `on_rebalance_end()`
is only reached through recovery -- downstream of the callback that
raised -- so the app was left with `rebalancing` stuck true.
The stand-in's spans now carry the tracer that made them, as the real
library's do, and `Tracer.start_span()` returns that tracer's own no-op
span rather than a fresh tracerless one.
The same drift hid a second divergence: the stand-in defined
`Span.operation_name`, which `AIOKafkaConsumerThread` reads to tell a
real span from a no-op one, catching the `AttributeError` the real
library raises. Defining it sent no-op spans down the real-span path,
where they failed on `_real_finish`. Dropping it restores the sentinel.
Two smaller parity fixes go with those: `Span.__exit__` now calls
`finish()`, which is what the driver's lazy spans rebind and what the
real library calls on exit, and `start_child_span()` goes through the
parent's tracer instead of returning an unrelated span.
Finally, `traced_from_parent_span()` no longer assumes a parent span has
a tracer. A span is whatever the configured tracer hands back, and
tracing is instrumentation: it now runs the wrapped function untraced
rather than breaking its caller.
`Tracer.start_active_span()` is dropped. Faust never calls it, and the
real one returns a `Scope` wrapping the span rather than the span
itself, so the stand-in's version would have mis-served any caller.
Tests run the stand-in side by side with the real library, so a
divergence fails as a parametrization rather than resting on a claim
about what the real library does, and cover both rebalance callbacks
with the stand-in substituted in. Eleven of them fail before this change.
Fixes #786
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Sejq9tYzzeqWeZAgA3EB6s
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #788 +/- ##
=======================================
Coverage 96.20% 96.20%
=======================================
Files 110 110
Lines 11789 11789
Branches 1281 1281
=======================================
Hits 11342 11342
Misses 350 350
Partials 97 97 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
#787 landed the same root-cause fix for #786 while this branch was open, so defer to it: `faust/utils/_opentracing.py` is taken from master verbatim, and its parity tests (`test_opentracing_shim.py`) stand as the guard for the stand-in itself. What master does not have, and this branch keeps: `traced_from_parent_span()` still assumes the parent span it is given has a tracer. Master's fix means the stand-in always supplies one, so the reported crash is gone either way -- but a span is whatever the configured tracer hands back, and tracing is instrumentation. It now runs the wrapped function untraced rather than failing its caller. The tests are cut down to what `test_opentracing_shim.py` does not already cover, and moved to `test_tracing.py` to match: the tracerless parent (by argument and through the context variable), error propagation, and both rebalance callbacks with the stand-in substituted for the real library -- the wiring the parity tests note they do not reach. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Sejq9tYzzeqWeZAgA3EB6s
opentracing is not installedtraced_from_parent_span()
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note: Before submitting this pull request, please review our contributing
guidelines.
Description
Rebased onto master now that #787 has landed. The scope of this PR has changed substantially — it opened as an alternative fix for #786, and is now just the one piece that #787 did not cover.
#787 landed the same root-cause fix while this was open, so this defers to it:
faust/utils/_opentracing.pyis taken from master verbatim, and its parity tests (test_opentracing_shim.py) stand as the guard for the stand-in itself. Nothing here re-litigates that.What's left
traced_from_parent_span()still assumes the parent span it is handed has a tracer:Master's fix means the stand-in always supplies one, so the crash reported in #786 is gone either way. But a span is whatever the configured tracer hands back, and
parent.traceris not guaranteed —opentracing.Spandocumentstraceras a property backed by whatever was passed to the constructor, and nothing stops it beingNone. Tracing is instrumentation; it shouldn't be what breaks a rebalance. So this runs the wrapped function untraced instead:getattrcovers both branches the old code had — "no parent at all" and "parent with no tracer" — so theif parent is not Nonetest folds into it and the body de-indents. That accounts for most of the diff; the behavioural change is the two added lines.Tests
Cut down to what
test_opentracing_shim.pydoesn't already cover, and moved totests/unit/utils/test_tracing.pyto match what they now exercise:faust.utils.tracing.opentracing.test_opentracing_shim.pynotes that it exercises the stand-in's objects directly and so "do[es] not prove theexcept ImportErrorfallback wiring itself" — these cover that wiring, and would have caught Kafka rebalancing fails whenopentracingis not installed #786 end to end.Three of the eight fail without the guard; the rebalance tests pass on master already and are here as regression cover.
Verification
tests/unit+tests/functional: 2503 passed, 12 skipped.isort/blackclean;flake8output byte-identical to master (4 pre-existing hits, none in touched files).Happy to close this instead if you'd rather leave
traced_from_parent_span()strict — #786 itself is fixed on master either way.