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

Prevent gthread connection reset on max-requests restart #3340

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions gunicorn/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,10 @@ def worker_class(self):
worker_class.setup()
return worker_class

@property
def worker_connections_enqueue_async(self):
return self.settings['worker_connections_enqueue_async'].get()

@property
def address(self):
s = self.settings['bind'].get()
Expand Down Expand Up @@ -737,6 +741,30 @@ class WorkerConnections(Setting):
"""


class WorkerConnectionsEnqueueAsync(Setting):
name = "worker_connections_enqueue_async"
section = "Worker Processes"
cli = ["--worker-connections-enqueue-async"]
validator = validate_bool
action = 'store_true'
default = False
desc = """\
Enqueue accepted connections to worker asynchronously.

It helps to use Gunicorn without reverse proxy.
When clients connect to Gunicorn, the server will start read data only when clients sent data.
If clients keep connections without sending data, server only accept the connections and no block the worker;

.. note::
If you're enable ``max-requests`` and it option, the worker can restart with accepted connections.

When clients send data via connections after restart the worker,
clients may have error ``Connection reset by peer``

This setting only affects the ``gthread`` worker types.
"""


class MaxRequests(Setting):
name = "max_requests"
section = "Worker Processes"
Expand Down
18 changes: 13 additions & 5 deletions gunicorn/workers/gthread.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,18 @@ def accept(self, server, listener):
conn = TConn(self.cfg, sock, client, server)

self.nr_conns += 1
# wait until socket is readable
with self._lock:
self.poller.register(conn.sock, selectors.EVENT_READ,
partial(self.on_client_socket_readable, conn))

if not self.cfg.worker_connections_enqueue_async:
# submit the connection to a worker
self.enqueue_req(conn)
else:
# wait until socket is readable
with self._lock:
self.poller.register(
conn.sock,
selectors.EVENT_READ,
partial(self.on_client_socket_readable, conn),
)
except OSError as e:
if e.errno not in (errno.EAGAIN, errno.ECONNABORTED,
errno.EWOULDBLOCK):
Expand All @@ -137,7 +145,7 @@ def on_client_socket_readable(self, conn, client):
# unregister the client from the poller
self.poller.unregister(client)

if conn.initialized:
if not self.cfg.worker_connections_enqueue_async or conn.initialized:
# remove the connection from keepalive
try:
self._keep.remove(conn)
Expand Down