Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Async client disconnect #2372

Open
wants to merge 3 commits into
base: master
Choose a base branch
from

Conversation

hundeboll
Copy link

I'm scratching my own itch here.

I'm implementing log-file streaming over HTTP using the usual Client - NGINX - uWSGI - Flask stack. When clients requests the stream, they receive the full log, and then new lines added to logs are streamed as they come. Those new lines might occur with several minutes in between. If the client disconnects in such an interval, the handler is blocked waiting for new log messages, and doesn't detect the closed connection. This effectively exhausts the "worker" pool if many such disconnects happen. Writing "dummy" data to the socket regularly isn't an option, as the dummy data would appear in the streamed log contents.

To fix this, the async core can detect the disconnected clients by keeping the protocol sockets in the event queue, and enable EPOLLRDHUP events. (I haven't checked similar flags for other event queue implementations.) The python plugin then checks a flag to detect closed connections in the response handler.

Martin Hundeboll added 3 commits November 19, 2021 10:48
Enabling the EPOLLRDHUP event when watching sockets makes epoll signal
closed connections when they happen.
After processing protocol data, the client socket is marked as "idle" in
the event queue, which enables connection-closed events in the event
loop.

Keeping "idle" connections in the event loop requires special casing
when enabling read and/or write events too, as the event queue complains
if a filedescriptor is added twice to the event loop. Avoid such errors
by modifying request sockets (wsgi_req->fd) instead of adding/deleting
them when handling response reads and writes.

Once a request connection is closed, the async event loop marks the
request as closed (wsgi_req->async_closed = 1) and continues request
processing to allow the request handler to clean up gracefully.
Check the wsgi_req->async_closed flag when processing response
iterations, and raise an OSError is the flag is set.

This solves the case, where a long running (but idle) client connection
is closed by the client. Since no data is written to the socket, the
close event is not detected, and the backend handler keeps running.

One case of an idle long running connection is log file streaming: The
client reads the log contents, and keeps hanging around for new log
messages. If no new messages occur, nothing is written to the
connection. When the client disconnects, the request handler should be
stopped.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant