Skip to content

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