Skip to content

Commit 7032941

Browse files
committed
Avoid duplicate broker instrumentation
1 parent f08a54a commit 7032941

4 files changed

Lines changed: 99 additions & 5 deletions

File tree

‎taskiq/instrumentation.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ def instrument_broker(
8787
if not hasattr(broker, "_is_instrumented_by_opentelemetry"):
8888
broker._is_instrumented_by_opentelemetry = False # type: ignore[attr-defined] # noqa: SLF001
8989

90-
if not getattr(broker, "is_instrumented_by_opentelemetry", False):
90+
if not getattr(broker, "_is_instrumented_by_opentelemetry", False):
9191
broker.middlewares.insert(
9292
0,
9393
OpenTelemetryMiddleware(

‎tests/opentelemetry/test_auto_instrumentation.py‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88

99

1010
class TestTaskiqAutoInstrumentation(TestBase):
11+
def tearDown(self) -> None:
12+
TaskiqInstrumentor().uninstrument()
13+
super().tearDown()
14+
1115
def test_auto_instrument(self) -> None:
1216
TaskiqInstrumentor().instrument()
1317

‎tests/opentelemetry/test_duplicate.py‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ class TestUtils(unittest.TestCase):
99
@staticmethod
1010
def test_duplicate_instrumentation() -> None:
1111
first = TaskiqInstrumentor()
12-
first.instrument_broker(broker)
13-
second = TaskiqInstrumentor()
14-
second.instrument_broker(broker)
15-
TaskiqInstrumentor().uninstrument()
12+
try:
13+
first.instrument_broker(broker)
14+
second = TaskiqInstrumentor()
15+
second.instrument_broker(broker)
16+
assert len(broker.middlewares) == 1
17+
finally:
18+
first.uninstrument_broker(broker)
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
from collections.abc import Generator
2+
3+
import pytest
4+
from opentelemetry import context
5+
from opentelemetry.sdk.trace import TracerProvider
6+
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
7+
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
8+
from opentelemetry.trace import SpanKind
9+
10+
from taskiq import InMemoryBroker
11+
from taskiq.instrumentation import TaskiqInstrumentor
12+
from taskiq.middlewares.opentelemetry_middleware import OpenTelemetryMiddleware
13+
14+
15+
@pytest.fixture
16+
def local_broker() -> Generator[InMemoryBroker, None, None]:
17+
broker = InMemoryBroker(await_inplace=True)
18+
TaskiqInstrumentor().uninstrument_broker(broker)
19+
try:
20+
yield broker
21+
finally:
22+
TaskiqInstrumentor().uninstrument_broker(broker)
23+
broker.executor.shutdown()
24+
25+
26+
def test_repeated_instrumentation_keeps_one_middleware(
27+
local_broker: InMemoryBroker,
28+
) -> None:
29+
instrumentor = TaskiqInstrumentor()
30+
instrumentor.instrument_broker(local_broker)
31+
first = local_broker.middlewares[0]
32+
instrumentor.instrument_broker(local_broker)
33+
assert local_broker.middlewares == [first]
34+
35+
36+
def test_uninstrumented_broker_can_be_instrumented_again(
37+
local_broker: InMemoryBroker,
38+
) -> None:
39+
instrumentor = TaskiqInstrumentor()
40+
instrumentor.instrument_broker(local_broker)
41+
instrumentor.uninstrument_broker(local_broker)
42+
assert local_broker.middlewares == []
43+
instrumentor.instrument_broker(local_broker)
44+
assert len(local_broker.middlewares) == 1
45+
assert isinstance(local_broker.middlewares[0], OpenTelemetryMiddleware)
46+
47+
48+
def test_different_brokers_are_instrumented_independently(
49+
local_broker: InMemoryBroker,
50+
) -> None:
51+
other = InMemoryBroker()
52+
instrumentor = TaskiqInstrumentor()
53+
try:
54+
instrumentor.instrument_broker(local_broker)
55+
instrumentor.instrument_broker(other)
56+
assert len(local_broker.middlewares) == len(other.middlewares) == 1
57+
assert local_broker.middlewares[0] is not other.middlewares[0]
58+
finally:
59+
instrumentor.uninstrument_broker(other)
60+
other.executor.shutdown()
61+
62+
63+
async def test_repeated_setup_does_not_duplicate_task_spans(
64+
local_broker: InMemoryBroker,
65+
) -> None:
66+
exporter = InMemorySpanExporter()
67+
provider = TracerProvider()
68+
provider.add_span_processor(SimpleSpanProcessor(exporter))
69+
instrumentor = TaskiqInstrumentor()
70+
token = context.attach(context.Context())
71+
try:
72+
instrumentor.instrument_broker(local_broker, tracer_provider=provider)
73+
instrumentor.instrument_broker(local_broker, tracer_provider=provider)
74+
75+
@local_broker.task
76+
async def task() -> int:
77+
return 42
78+
79+
result = await task.kiq()
80+
assert (await result.get_result()).return_value == 42
81+
spans = exporter.get_finished_spans()
82+
assert len([span for span in spans if span.kind == SpanKind.PRODUCER]) == 1
83+
assert len([span for span in spans if span.kind == SpanKind.CONSUMER]) == 1
84+
finally:
85+
instrumentor.uninstrument_broker(local_broker)
86+
context.detach(token)
87+
provider.shutdown()

0 commit comments

Comments
 (0)