We sadly started getting popped wrong scope assertion errors in our Django + channels application after upgrading to sentry-sdk from raven. Here's our stack:
django==2.1.2
channels==2.1.5
daphne==2.2.2
sentry-sdk==0.4.1
on Python 3.7.0.
Channels is great for us because we're building a websocket heavy application that does lots of work outside an HTTP request response cycle. We would like to use Sentry's scopes to decorate any exceptions that happen during that websocket processing with context from our application as Sentry already does for HTTP requests. We use the (confusingly named) channels self.scope to store stuff about which user and which session the websocket is for, and then we want to push that onto a Sentry scope every time we go to process a websocket frame that has arrived. We implemented a handy dandy asynccontextmanager that does exactly this.
This doesn't quite work right now however. I believe that because there are multiple coroutines executing at the same time, the ScopeManager contextmanager __exit__s get called at weird and unexpected times, especially if the coroutine execution is interleaved. I am not exactly sure, but I think that it would need to use a ContextVar the same way the HubManager does to be async friendly.
Here's a reproduction that triggers the same issue without all the django business:
import asyncio
import time
from sentry_sdk import push_scope
from contextlib import contextmanager, asynccontextmanager
@asynccontextmanager
async def set_context(value):
with push_scope() as sentry_scope:
sentry_scope.set_tag("value", value)
yield
async def say_after(delay, what):
async with set_context(what):
await asyncio.sleep(delay)
print(what)
await asyncio.sleep(delay)
print(f"{what} is done now")
async def main():
print('started at', time.strftime('%X'))
a = asyncio.create_task(say_after(0.25, 'hello'))
b = asyncio.create_task(say_after(0.3, 'world'))
c = asyncio.create_task(say_after(0.4, 'good day'))
await a
await b
await c
print('finished at', time.strftime('%X'))
asyncio.run(main())
Because the context exits happen in a different order than the enters, we get a AssertionError: popped wrong scope.
Happy to provide more information if this isn't clear! Multithreading strikes again!
We sadly started getting
popped wrong scopeassertion errors in our Django + channels application after upgrading tosentry-sdkfromraven. Here's our stack:on Python
3.7.0.Channels is great for us because we're building a websocket heavy application that does lots of work outside an HTTP request response cycle. We would like to use Sentry's scopes to decorate any exceptions that happen during that websocket processing with context from our application as Sentry already does for HTTP requests. We use the (confusingly named) channels
self.scopeto store stuff about which user and which session the websocket is for, and then we want to push that onto a Sentry scope every time we go to process a websocket frame that has arrived. We implemented a handy dandyasynccontextmanagerthat does exactly this.This doesn't quite work right now however. I believe that because there are multiple coroutines executing at the same time, the
ScopeManagercontextmanager__exit__s get called at weird and unexpected times, especially if the coroutine execution is interleaved. I am not exactly sure, but I think that it would need to use aContextVarthe same way theHubManagerdoes to be async friendly.Here's a reproduction that triggers the same issue without all the django business:
Because the context exits happen in a different order than the enters, we get a
AssertionError: popped wrong scope.Happy to provide more information if this isn't clear! Multithreading strikes again!