question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Have a global signalling mechanism for request/response lifecycle

See original GitHub issue

Core Django, Flask and some other web frameworks have a way to register a callback whenever a request is handled or a response is sent out. This registry exists globally and independently of application state. It’s invaluable to monitoring tools like Newrelic and Sentry, as their SDKs/Agents can just subscribe to signals at any point in time without requiring the user to register a middleware or really do any other kind of configuration (example). For frameworks/libraries where such signals don’t exist, there is still the possibility of monkeypatching (example). I am affiliated with Sentry, as I work on the Sentry SDK for Python.

Channels offers no possibility to safely hook into the request/response lifecycle of the ASGI application it exports. Like most frameworks it has no global signals, but the routing mechanism is left up to the user in a way that makes it very hard to wrap the outermost ASGI application even through monkeypatching. Django also allows the user to wrap the Django-exported WSGI application in arbitrarily many layers of WSGI middleware that we cannot instrument, but Channels makes it a common pattern for the user to use ASGI middleware while it’s uncommon enough in Django to ignore the issue there.

What I would like to see is the following:

  1. Modify channels.routing.get_default_application() to wrap the discovered ASGI application in a middleware ChannelsApp that does nothing but forward to the inner application. This already allows us to monkeypatch to achieve our goal.
  2. (optional) Add new global signals that are emitted by this middleware. We are fine with using internal APIs to instrument channels, but official APIs are always better.

Even just the first change enables Sentry and similar tools to hook into channels before Django settings have been loaded. This point is crucial because:

  • Sentry’s SDK is initialized as part of loading settings in the first place
  • NewRelic’s Agent attempts to register signals and monkeypatch shortly after process startup

The status-quo solution to this problem is to do one of the following:

  • Patch AsgiHandler exclusively if you only care about HTTP

  • Patch ProtocolTypeRouter under the assumption that most people use this type to wrap everything else

  • Use some sort of trampoline to look into Django settings to discover ASGI_APPLICATION, and then patch that object. This opens two more choices:

    • Patch Django to know when settings are loaded
    • Do it on the first request where you can be sure that settings are loaded

    This is a bit involved because ASGI_APPLICATION can be any kind of object that might be callable without providing you a settable __call__. For example it might be a primitive function, in which case patching func_code/__code__ might do the trick.

Would love to know what you think about this. I am happy to prepare the patch for this.

Django’s upcoming ASGI support seems to do much better in this regard, as there only ASGIHandler needs to be patched.

Issue Analytics

  • State:closed
  • Created 4 years ago
  • Comments:12 (6 by maintainers)

github_iconTop GitHub Comments

8reactions
carltongibsoncommented, Oct 2, 2019

OK, I understand.

In that case, no, this isn’t something I’d want to add. Given the choice between explicitly wrapping the ASGI application or opaquely monkey patching in, I would always opt for the former. I think we do no-one any real favours otherwise.

Explicit beats implicit, and ASGI’s one obvious way to do things is wrapping applications in this manner.

Sorry.

6reactions
untitakercommented, Oct 2, 2019

The third choice is to have global signals like Flask. I mentioned this possibility four times now. Honestly it’s frustrating to engage with someone who doesn’t bother to read anything that I write (and doesn’t ask for clarification either in case my comments were too vague).

Be that as it may, Django 3 will have the signals we need, so I guess time will solve this.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Implementing a simple WebRTC signaling mechanism with ...
A code walkthrough of implementing a realtime WebRTC signaling mechanism with FSharp, ... To establish a connection between two peers they need to...
Read more >
Webhook service | Dialogflow ES - Google Cloud
Dialogflow supports the following mechanisms for authentication: ... You also have the option to use a Cloud Function not created by the inline...
Read more >
Request lifecycle - FAQ - A progressive Node.js framework
First, Nest runs globally bound middleware (such as middleware bound with ... However, at a route parameter level, if you have multiple pipes...
Read more >
What is MQTT and How Does it Work? - TechTarget
An MQTT broker acts as a go-between for the clients who are sending messages and the subscribers who are receiving those messages. In...
Read more >
Introduction to HTTP/2 - web.dev
To achieve the 50% PLT improvement, SPDY aimed to make more efficient use of the underlying TCP connection by introducing a new binary...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found