Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Propagate http abort/close from nginx to uwsgi / Django

Tags:

I have a Django application web application, and I was wondering if it was possible to have nginx propagate the abort/close to uwsgi/Django.

Basically I know that nginx is aware of the premature abort/close because it defaults to uwsgi_ignore_client_abort to "off", and you get nginx 499 errors in your nginx logs when requests are aborted/closed before the response is sent. Once uwsgi finishes processing the request it throws an "IO Error" when it goes to return the response to nginx.

Turning uwsgi_ignore_client_abort to "on" just makes nginx unaware of the abort/close, and removes the uwsgi "IO Errors" because uwsgi can still write back to nginx.

My use case is that I have an application where people page through some ajax results very quickly, and so if the quickly page through I abort the pending ajax request for the page that they skipped, this keeps the client clean and efficient. But this does nothing for the server side (uwsgi/Django) because they still have to process every single request even if nothing will be waiting for the response.

Now obviously there may be certain pages, where I don't want the request to be prematurely aborted for any reason. But I use celery for long running requests that may fall into that category.

So is this possible? uwsgi's hariakari setting makes me think that it is at some level.... just can't figure out how to do it.

like image 259
byoungb Avatar asked Sep 29 '16 22:09

byoungb


People also ask

Can I use uWSGI without nginx?

Can I then ditch NGINX? uWSGI could be used as a standalone web server in production, but that is not it's intentional use. It may sound odd, but uWSGI was always supposed to be a go-between a full-featured web server like NGINX and your Python files.

Which is better uWSGI or Gunicorn?

Gunicorn and uWSGI are primarily classified as "Web Servers" and "Web Server Interface" tools respectively. Gunicorn and uWSGI are both open source tools. Gunicorn with 5.96K GitHub stars and 1.12K forks on GitHub appears to be more popular than uWSGI with 2.5K GitHub stars and 541 GitHub forks.

What is uWSGI in nginx?

Nginx implements a uwsgi proxying mechanism, which is a fast binary protocol that uWSGI can use to talk with other servers. The uwsgi protocol is actually uWSGI's default protocol, so simply by omitting a protocol specification, it will fall back to uwsgi .


2 Answers

My use case is that I have an application where people page through some ajax results very quickly, and so if the quickly page through I abort the pending ajax request for the page that they skipped, this keeps the client clean and efficient.

Aborting an AJAX request on the client side is done through XMLHttpRequest.abort(). If the request has not yet been sent out when abort() is called, then the request won't go out. But if the request has been sent, the server won't know that the request has been aborted. The connection won't be closed, there won't be any message sent to the server, nothing. If you want the server to know that a request is no longer needed, you basically need to come up with a way to identify requests so that when you make the initial request you get an identifier for it. Then, through another AJAX request you could tell the server that an earlier request should be cancelled. (If you search questions about abort() like this one and search for "server" you'll find explanations saying the same.)

Note that uwsgi_ignore_client_abort is something that deals with connection closures at the TCP level. That's a different thing from aborting an AJAX request. There is generally no action you can take in JavaScript that will entail closing a TCP connection. The browser optimizes the creation and destruction of connections to suit its needs. Just now, I did this:

  1. I used lsof to check whether any process had a connection to example.com. There were none. (lsof is a *nix utility that allows listing open files. Network connections are "files" in *nix.)

  2. I opened a page to example.com in Chrome. lsof showed the connection and the process that opened it.

  3. Then I closed the page.

  4. I polled with lsof to see if the connection I identified earlier was still opened. It stayed open for about one minute after I closed the page even though there was no real need to keep the connection open.

And there's no amount of fiddling with uswgi settings that will make it be aware of aborts performed through XMLHttpRequest.abort()

The use-case scenario you gave was one where users were paging fast through some results. I can see two possibilities for the description given in the question:

  1. The user waits for a refresh before paging further. For instance, Alice is looking through an list of user names sorted alphabetically for user "Zeno" and each time a new page is shown, she sees the name is not there and pages down. In this case, there's nothing to abort because the user's action is dependent on the request having been handled first. (The user has to see the new page before making a decision.)

  2. The user just pages down without waiting for a refresh. Alice again is looking for "Zeno" but she figures it's going to be on the last page so click, click, click she goes. In this case, you can debounce the requests made to the server. Then the next page button is pressed, increment the number of the page that should be shown to the user but don't send the request right away. Instead, you wait for a small delay after the user ceases clicking the button to then send the request with final page number and so you make one request instead of a dozen. Here is an example of a debounce performed for a DataTables search.

like image 196
Louis Avatar answered Sep 17 '22 13:09

Louis


Now obviously there may be certain pages, where I don't want the request to be prematurely aborted for any reason.

This is precisely the problem behind taking this one way or the other.

  • Obviously, you may not want to continue spending system resources processing a connection that has since been aborted, e.g., an expensive search operation.

  • But then maybe the connection was important enough that it still has to be processed even if the client has disconnected.

    • E.g., the very same expensive search operation, but one that's actually not client-specific, and will be cached by nginx for all subsequent clients, too.

    • Or maybe an operation that modifies the state of your application — you clearly wouldn't want to have your application to have an inconsistent state!

As mentioned, the problem is with uWSGI, not with NGINX. However, you cannot have uWSGI automatically decide what was your intention, without you revealing such intention yourself to uWSGI.

And how exactly will you reveal your intention in your code? A whole bunch of programming languages don't really support multithreaded and/or asynchronous programming models, which makes it entirely non-trivial to cancel operations.

As such, there is no magic solution here. Even the concurrency-friendly programming languages like Golang are having issues around the WithCancel context — you may have to pass it around in every function call that could possibly block, making the code very ugly.

Are you already doing the above context passing in Django? If not, then the solution is ugly but very simple — any time you can clearly abort the request, check whether the client is still connected with uwsgi.is_connected(uwsgi.connection_fd()):

  • http://lists.unbit.it/pipermail/uwsgi/2013-February/005362.html
like image 32
cnst Avatar answered Sep 18 '22 13:09

cnst