Ability to handle request dispatch in custom process #1367

Open
jesper opened this Issue Oct 13, 2016 · 4 comments

Projects

None yet

3 participants

@jesper
jesper commented Oct 13, 2016

I'm writing a middleware for my flask app that under certain conditions will immediately returns a 202, while then continuing to actually dispatch the request (wsgi_app(environ, start_response)) in a custom forked process.

However, after forking, gunicorn exits with "Parent changed, shutting down".

Any idea how one may get around this?

Thanks for all the effort in this amazing project. Absolutely a huge fan!

@benoitc
Owner
benoitc commented Oct 16, 2016

Can you share your code to help to reproduce?

Imo simply forking isn't enough you should also instruct gunicorn that the socket have been given to another process using the same tricks we do for websockets.

@jesper
jesper commented Oct 17, 2016 edited

Thanks. FYI while creating the sample code I noticed it only happens for me with gevent.

Output:

$ gunicorn -k gevent app:app
[2016-10-17 17:57:13 +0000] [5477] [INFO] Starting gunicorn 19.4.5
[2016-10-17 17:57:13 +0000] [5477] [INFO] Listening at: http://127.0.0.1:8000 (5477)
[2016-10-17 17:57:13 +0000] [5477] [INFO] Using worker: gevent
[2016-10-17 17:57:13 +0000] [5482] [INFO] Booting worker with pid: 5482
[2016-10-17 17:57:15 +0000] [5493] [INFO] Parent changed, shutting down: <Worker 5493>
Process Process-1:
Traceback (most recent call last):
  File "/usr/lib/python2.7/multiprocessing/process.py", line 258, in _bootstrap
    self.run()
  File "/usr/lib/python2.7/multiprocessing/process.py", line 114, in run
    self._target(*self._args, **self._kwargs)
  File "/home/jesper/app.py", line 15, in worker
    sleep(2)
  File "/home/jesper/src/venv/local/lib/python2.7/site-packages/gevent/hub.py", line 173, in sleep
    hub.wait(loop.timer(seconds, ref=ref))
  File "/home/jesper/src/venv/local/lib/python2.7/site-packages/gevent/hub.py", line 606, in wait
    result = waiter.get()
  File "/home/jesper/src/venv/local/lib/python2.7/site-packages/gevent/hub.py", line 854, in get
    return self.hub.switch()
  File "/home/jesper/src/venv/local/lib/python2.7/site-packages/gevent/hub.py", line 585, in switch
    return greenlet.switch(self)
GreenletExit

Code:

from flask import Flask
from multiprocessing import Process
from time import sleep

app = Flask(__name__)


class ForkMiddleware(object):

    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        def worker():
            sleep(2)
            # Do some elaborate stuff here...
            self.app(environ, start_response)

        p = Process(target=worker)
        p.start()

        start_response("202", [("content-type", "application/json")])
        return '{"status": "queued"}'

app.wsgi_app = ForkMiddleware(app.wsgi_app)


@app.route('/')
def index():
    print("Inside route execution")
    return 'Spoon!'
@tilgovi
Collaborator
tilgovi commented Dec 21, 2016

It may not be possible with gevent. I don't know if Process uses pipes, but I guess that it does in order to return values, and this breaks under gevent.

More information here: http://stackoverflow.com/questions/8678307/gevent-monkeypatching-breaking-multiprocessing

@tilgovi
Collaborator
tilgovi commented Dec 21, 2016

It might work more easily if you use threading instead, since I think gevent works well with that (you'll get a greenlet), although that might not be what you want if the work you're doing in the background is CPU-bound.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment