Skip to content

Commit c9ee662

Browse files
committed
Isolate async gRPC server scope per request
1 parent 301a879 commit c9ee662

File tree

2 files changed

+49
-21
lines changed

2 files changed

+49
-21
lines changed

sentry_sdk/integrations/grpc/aio/server.py

Lines changed: 22 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -47,27 +47,28 @@ async def wrapped(request: "Any", context: "ServicerContext") -> "Any":
4747
if not name:
4848
return await handler(request, context)
4949

50-
# What if the headers are empty?
51-
transaction = sentry_sdk.continue_trace(
52-
dict(context.invocation_metadata()),
53-
op=OP.GRPC_SERVER,
54-
name=name,
55-
source=TransactionSource.CUSTOM,
56-
origin=SPAN_ORIGIN,
57-
)
58-
59-
with sentry_sdk.start_transaction(transaction=transaction):
60-
try:
61-
return await handler.unary_unary(request, context)
62-
except AbortError:
63-
raise
64-
except Exception as exc:
65-
event, hint = event_from_exception(
66-
exc,
67-
mechanism={"type": "grpc", "handled": False},
68-
)
69-
sentry_sdk.capture_event(event, hint=hint)
70-
raise
50+
with sentry_sdk.isolation_scope():
51+
# What if the headers are empty?
52+
transaction = sentry_sdk.continue_trace(
53+
dict(context.invocation_metadata()),
54+
op=OP.GRPC_SERVER,
55+
name=name,
56+
source=TransactionSource.CUSTOM,
57+
origin=SPAN_ORIGIN,
58+
)
59+
60+
with sentry_sdk.start_transaction(transaction=transaction):
61+
try:
62+
return await handler.unary_unary(request, context)
63+
except AbortError:
64+
raise
65+
except Exception as exc:
66+
event, hint = event_from_exception(
67+
exc,
68+
mechanism={"type": "grpc", "handled": False},
69+
)
70+
sentry_sdk.capture_event(event, hint=hint)
71+
raise
7172

7273
elif not handler.request_streaming and handler.response_streaming:
7374
handler_factory = grpc.unary_stream_rpc_method_handler

tests/integrations/grpc/test_grpc_aio.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,26 @@ async def test_grpc_server_abort(grpc_server_and_channel, capture_events):
176176
assert len(events) == 1
177177

178178

179+
@pytest.mark.asyncio
180+
async def test_grpc_server_uses_isolation_scope_per_request(
181+
grpc_server_and_channel, capture_events
182+
):
183+
_, channel = grpc_server_and_channel
184+
events = capture_events()
185+
186+
stub = gRPCTestServiceStub(channel)
187+
await stub.TestServe(gRPCTestMessage(text="scope:first"))
188+
await stub.TestServe(gRPCTestMessage(text="scope:second"))
189+
190+
first_event, second_event = events
191+
192+
assert first_event["extra"]["grpc_scope_marker"] == "scope:first"
193+
assert "grpc_previous_scope_marker" not in first_event.get("extra", {})
194+
195+
assert second_event["extra"]["grpc_scope_marker"] == "scope:second"
196+
assert "grpc_previous_scope_marker" not in second_event.get("extra", {})
197+
198+
179199
@pytest.mark.asyncio
180200
async def test_grpc_client_starts_span(
181201
grpc_server_and_channel, capture_events_forksafe
@@ -304,6 +324,13 @@ def __init__(self):
304324

305325
@classmethod
306326
async def TestServe(cls, request, context): # noqa: N802
327+
if request.text.startswith("scope:"):
328+
scope = sentry_sdk.get_isolation_scope()
329+
previous_marker = scope._extras.get("grpc_scope_marker")
330+
if previous_marker is not None:
331+
scope.set_extra("grpc_previous_scope_marker", previous_marker)
332+
scope.set_extra("grpc_scope_marker", request.text)
333+
307334
with start_span(
308335
op="test",
309336
name="test",

0 commit comments

Comments
 (0)