Repository: getsentry/sentry-python

Issue:
gunicorn with gevent worker - is this supported?
We see 'mixed' events in our Sentry installation. We recently activated the gevent worker for gunicorn, to get more concurrent requests (our service is highly IO dependent).

In the docs on sentry.io I couldn't find any documentation if gevent workers are supported or not.

I have seen some older tickets that state that it is not supported at all, but not sure if this is still the case? If it is not supported, how could we make it supported? I guess we would need to put tracking information into a Greenlet local? Would that be the right way to do it?

Thanks for any information.


Discussion:
@amir-hadi do you have the gevent monkeypatches enabled (i.e. gevent monkeypatching the stdllib)? Those are necessary for sure.
To my knowledge the Gevent worker of Gunicorn is patching everything for us, [see this line in the Gunicorn source code](https://github.com/benoitc/gunicorn/blob/dc7b5d5c4876b49f86ea2460698a335d0f5ef7c9/gunicorn/workers/ggevent.py#L160). The call of `self.patch()` calls `gevent.monkey.patch_all()`.
Understood, in this case everything should be supported. At this point it becomes a bug. Which Python version is that?
We are using Python 3.7.3 in our docker containers, but I am not sure if this is a bug on the UI or not. The code path we are seeing is impossible to reach for the API URL path visible in Sentry. Also, it seems like that impossible (for our system) combinations of Browser information is being done (okhttp client for native apps and Chrome for one of our management UIs).

Let me know how I can help you further to track this issue down.
All of this is likely an artifact of leaking scopes. I will take a look
soon.

On Fri, Jun 21, 2019, 09:47 Amir Hadi <notifications@github.com> wrote:

> We are using Python 3.7.3 in our docker containers, but I am not sure if
> this is a bug on the UI or not. The code path we are seeing is impossible
> to reach for the API URL path visible in Sentry. Also, it seems like that
> impossible (for our system) combinations of Browser information is being
> done (okhttp client for native apps and Chrome for one of our management
> UIs).
>
> Let me know how I can help you further to track this issue down.
>
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub
> <https://github.com/getsentry/sentry-python/issues/392?email_source=notifications&email_token=AAGMPRJYYCNOSZJJMNBEXJLP3SBPZA5CNFSM4HZKQAL2YY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGODYHXABY#issuecomment-504328199>,
> or mute the thread
> <https://github.com/notifications/unsubscribe-auth/AAGMPRMRUE26HVMWKL2JR5TP3SBPZANCNFSM4HZKQALQ>
> .
>


Resolving patch:
diff --git a/sentry_sdk/utils.py b/sentry_sdk/utils.py
--- a/sentry_sdk/utils.py
+++ b/sentry_sdk/utils.py
@@ -734,15 +734,50 @@ def realign_remark(remark):
     )
 
 
-HAS_REAL_CONTEXTVARS = True
+def _is_threading_local_monkey_patched():
+    # type: () -> bool
+    try:
+        from gevent.monkey import is_object_patched  # type: ignore
+
+        if is_object_patched("_threading", "local"):
+            return True
+    except ImportError:
+        pass
+
+    try:
+        from eventlet.patcher import is_monkey_patched  # type: ignore
+
+        if is_monkey_patched("thread"):
+            return True
+    except ImportError:
+        pass
+
+    return False
+
 
-try:
-    from contextvars import ContextVar  # type: ignore
+IS_THREADING_LOCAL_MONKEY_PATCHED = _is_threading_local_monkey_patched()
+del _is_threading_local_monkey_patched
 
-    if not PY2 and sys.version_info < (3, 7):
-        import aiocontextvars  # type: ignore  # noqa
-except ImportError:
-    HAS_REAL_CONTEXTVARS = False
+
+def _get_contextvars():
+    # () -> (bool, Type)
+    """
+    Try to import contextvars and use it if it's deemed safe. We should not use
+    contextvars if gevent or eventlet have patched thread locals, as
+    contextvars are unaffected by that patch.
+
+    https://github.com/gevent/gevent/issues/1407
+    """
+    if not IS_THREADING_LOCAL_MONKEY_PATCHED:
+        try:
+            from contextvars import ContextVar  # type: ignore
+
+            if not PY2 and sys.version_info < (3, 7):
+                import aiocontextvars  # type: ignore  # noqa
+
+            return True, ContextVar
+        except ImportError:
+            pass
 
     from threading import local
 
@@ -759,6 +794,12 @@ def get(self, default):
         def set(self, value):
             setattr(self._local, "value", value)
 
+    return False, ContextVar
+
+
+HAS_REAL_CONTEXTVARS, ContextVar = _get_contextvars()
+del _get_contextvars
+
 
 def transaction_from_function(func):
     # type: (Callable[..., Any]) -> Optional[str]


Test patch: