[Web-SIG] A 'shutdown' function in WSGI
Hello I need to be able to call a function when the web application shuts down (SIGTERM/SIGINT) -- the use case is to stop a background thread. I am currently using signals because it seems to be the most clean way to do this. atexit is much trickier since you don't know when it's going to get called and you might try to call objects that were garbage collected unless you hack something to keep references alive. But signals are also tricky beasts since you may compete with other code that are listening to them. For instance mod_wsgi don't like apps that have signal handlers. Anyways, the bottom line is that the cleanest way to do this -- as per Chris McDonough idea, would be to introduce in the WSGI protocol a shutdown function the servers would be obligated to call before exiting. I am not sure yet about its arguments, maybe a signum + frame or simply an exit code... But how do you like the idea ? That would solve for me the problem of having to deal differently here depending on if I am called with mod_wsgi or gunicorn or xxx Cheers Tarek -- Tarek Ziadé | http://ziade.org ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] A 'shutdown' function in WSGI
CherryPy provides a bus that allows you to add events to the web server process. It is specified pretty clearly and CherryPy recently made it available as a standalone package, Magicbus (https://bitbucket.org/cherrypy/magicbus/overview). Specifically it allows you to send events on different signals the main server process might get. You can also use it for a general bus within the app server, but at its most basic level, the goal was to make the stop/start/restart events easy to hook into. I've found it to be really helpful for managing processes and wrote a simple supervisor-ish app called Dad using it (http://bitbucket.org/elarson/dad). HTH Eric -- Eric Larson On Monday, February 20, 2012 at 10:03 AM, Tarek Ziadé wrote: Hello I need to be able to call a function when the web application shuts down (SIGTERM/SIGINT) -- the use case is to stop a background thread. I am currently using signals because it seems to be the most clean way to do this. atexit is much trickier since you don't know when it's going to get called and you might try to call objects that were garbage collected unless you hack something to keep references alive. But signals are also tricky beasts since you may compete with other code that are listening to them. For instance mod_wsgi don't like apps that have signal handlers. Anyways, the bottom line is that the cleanest way to do this -- as per Chris McDonough idea, would be to introduce in the WSGI protocol a shutdown function the servers would be obligated to call before exiting. I am not sure yet about its arguments, maybe a signum + frame or simply an exit code... But how do you like the idea ? That would solve for me the problem of having to deal differently here depending on if I am called with mod_wsgi or gunicorn or xxx Cheers Tarek -- Tarek Ziadé | http://ziade.org ___ Web-SIG mailing list Web-SIG@python.org (mailto:Web-SIG@python.org) Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/eric%40ionrock.org ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] A 'shutdown' function in WSGI
On Mon, Feb 20, 2012 at 8:09 PM, Eric Larson e...@ionrock.org wrote: CherryPy provides a bus that allows you to add events to the web server process. It is specified pretty clearly and CherryPy recently made it available as a standalone package, Magicbus ( https://bitbucket.org/cherrypy/magicbus/overview). Specifically it allows you to send events on different signals the main server process might get. You can also use it for a general bus within the app server, but at its most basic level, the goal was to make the stop/start/restart events easy to hook into. I've found it to be really helpful for managing processes and wrote a simple supervisor-ish app called Dad using it ( http://bitbucket.org/elarson/dad). Thanks for the pointer -- that looks pretty neat I would be more interested though, in defining an extension to the WSGI standard A rough example of what I am talking about: If I take the wsgiref doc, here's an example of a minimal wsgi application (myapp.py): from wsgiref.simple_server import make_server def hello_world_app(environ, start_response): status = '200 OK' # HTTP Status headers = [('Content-type', 'text/plain')] start_response(status, headers) return [Hello World] def main(): return make_server('', 8000, hello_world_app) That module can be run by any web server out there that understands WSGI. For instance, with gunicorn I can do: $ gunicorn myapp:main What I am talking about is a second entry point for the shutdown - example: from wsgiref.simple_server import make_server def hello_world_app(environ, start_response): status = '200 OK' # HTTP Status headers = [('Content-type', 'text/plain')] start_response(status, headers) return [Hello World] def main(): return make_server('', 8000, hello_world_app) def shutdown(): # or maybe something else as an argument I don't know do_some_cleanup() And point the shutdown callable to a web server: $ gunicorn myapp:main myapp:shutdown If this is defined in the WSGI standard it means any wsgi web server could call shutdown() , not only gunicorn Cheers Tarek HTH Eric -- Eric Larson On Monday, February 20, 2012 at 10:03 AM, Tarek Ziadé wrote: Hello I need to be able to call a function when the web application shuts down (SIGTERM/SIGINT) -- the use case is to stop a background thread. I am currently using signals because it seems to be the most clean way to do this. atexit is much trickier since you don't know when it's going to get called and you might try to call objects that were garbage collected unless you hack something to keep references alive. But signals are also tricky beasts since you may compete with other code that are listening to them. For instance mod_wsgi don't like apps that have signal handlers. Anyways, the bottom line is that the cleanest way to do this -- as per Chris McDonough idea, would be to introduce in the WSGI protocol a shutdown function the servers would be obligated to call before exiting. I am not sure yet about its arguments, maybe a signum + frame or simply an exit code... But how do you like the idea ? That would solve for me the problem of having to deal differently here depending on if I am called with mod_wsgi or gunicorn or xxx Cheers Tarek -- Tarek Ziadé | http://ziade.org ___ Web-SIG mailing list Web-SIG@python.org (mailto:Web-SIG@python.org) Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/eric%40ionrock.org -- Tarek Ziadé | http://ziade.org ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] A 'shutdown' function in WSGI
oops my examples were broken, should be: def hello_world_app(environ, start_response): status = '200 OK' # HTTP Status headers = [('Content-type', 'text/plain')] start_response(status, headers) return [Hello World] def shutdown(): # or maybe something else as an argument I don't know do_some_cleanup() and: $ gunicorn myapp:hello_world_app myapp:shutdown Cheers Tarek ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] A 'shutdown' function in WSGI
The standard way to do this would be to define an optional server extension API supplied in the environ; for example, a 'x-wsgiorg.register_shutdown' function. The wsgi.org wiki used to be the place to propose these sorts of things for standardization, but it appears to no longer be a wiki, so the mailing list is probably a good place to discuss such a proposal. On Mon, Feb 20, 2012 at 2:30 PM, Tarek Ziadé ziade.ta...@gmail.com wrote: oops my examples were broken, should be: def hello_world_app(environ, start_response): status = '200 OK' # HTTP Status headers = [('Content-type', 'text/plain')] start_response(status, headers) return [Hello World] def shutdown(): # or maybe something else as an argument I don't know do_some_cleanup() and: $ gunicorn myapp:hello_world_app myapp:shutdown Cheers Tarek ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/pje%40telecommunity.com ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] A 'shutdown' function in WSGI
On Mon, 2012-02-20 at 17:39 -0500, PJ Eby wrote: The standard way to do this would be to define an optional server extension API supplied in the environ; for example, a 'x-wsgiorg.register_shutdown' function. Unlikely, AFACIT, as shutdown may happen when no request is active. Even if this somehow happened to not be the case, asking the application to put it in the environ is not useful, as the environ can't really be relied on to retain values up the call stack. - C The wsgi.org wiki used to be the place to propose these sorts of things for standardization, but it appears to no longer be a wiki, so the mailing list is probably a good place to discuss such a proposal. On Mon, Feb 20, 2012 at 2:30 PM, Tarek Ziadé ziade.ta...@gmail.com wrote: oops my examples were broken, should be: def hello_world_app(environ, start_response): status = '200 OK' # HTTP Status headers = [('Content-type', 'text/plain')] start_response(status, headers) return [Hello World] def shutdown(): # or maybe something else as an argument I don't know do_some_cleanup() and: $ gunicorn myapp:hello_world_app myapp:shutdown Cheers Tarek ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/pje% 40telecommunity.com ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/chrism%40plope.com ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] A 'shutdown' function in WSGI
Le 21/02/2012 01:18, Chris McDonough a écrit : On Mon, 2012-02-20 at 17:39 -0500, PJ Eby wrote: The standard way to do this would be to define an optional server extension API supplied in the environ; for example, a 'x-wsgiorg.register_shutdown' function. Unlikely, AFACIT, as shutdown may happen when no request is active. Even if this somehow happened to not be the case, asking the application to put it in the environ is not useful, as the environ can't really be relied on to retain values up the call stack. Hi, I like environ['x-wsgiorg.register_shutdown']. It would work without changes to WSGI itself. I think that the idea is not to put your shutdown function in the environment and hope it stays there up the stack, but to register it by calling register_shutdown: @environ.get('x-wsgiorg.register_shutdown', lambda f: f) def do_cleanup(): pass Also, a shutdown function would be used to clean up something that was set up in a request. So if the server shuts down without having ever served a request, there probably is nothing to clean up. Regards, -- Simon Sapin ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] A 'shutdown' function in WSGI
On 21 February 2012 12:03, Simon Sapin simon.sa...@exyr.org wrote: Le 21/02/2012 01:18, Chris McDonough a écrit : On Mon, 2012-02-20 at 17:39 -0500, PJ Eby wrote: The standard way to do this would be to define an optional server extension API supplied in the environ; for example, a 'x-wsgiorg.register_shutdown' function. Unlikely, AFACIT, as shutdown may happen when no request is active. Even if this somehow happened to not be the case, asking the application to put it in the environ is not useful, as the environ can't really be relied on to retain values up the call stack. Hi, I like environ['x-wsgiorg.register_shutdown']. It would work without changes to WSGI itself. I think that the idea is not to put your shutdown function in the environment and hope it stays there up the stack, but to register it by calling register_shutdown: @environ.get('x-wsgiorg.register_shutdown', lambda f: f) def do_cleanup(): pass Also, a shutdown function would be used to clean up something that was set up in a request. So if the server shuts down without having ever served a request, there probably is nothing to clean up. Using environ is not going to work it is supplied on a per request basis. You would typically want an application scope cleanup handler to only be registered once. In this scheme you are relying on it being registered from within a request scope. To ensure that it is only registered once, the caller would need to use a flag protected by a thread mutex to know whether should call a second time, which is cumbersome. If you don't do that you could end up registering a separate callback for every single request that occurs and memory usage alone would blow out just from recording them all. Alternatively, you would have to require the underlying WSGI server/adapter to weed out duplicates, but even if you do that, you still waste the time of the per request scope registering it all the time. Even if you have a registration mechanism, especially with a WSGI adapter riding on top of something else, how is the WSGI adapter going to get notified to call them. All you have therefore done is shift the problem of how it is triggered somewhere else. Overall the best chance of being able to do anything is relying on atexit. You are though at the mercy of the WSGI hosting mechanism shutting down the process and so the interpreter, in an orderly manner such that atexit callbacks get called. In Apache/mod_wsgi you get this guarantee, even in sub interpreters where atexit callbacks wouldn't normally be called when they are destroyed. For uWSGI, atexit callbacks will not be called at the moment, by Robert is making changes to it so you get a guarantee there as well. It is possible he is only doing this though for case where main interpreter is being used, as doing it for sub interpreters is a bit fiddly. Any pure Python WSGI servers shouldn't have issues so long as they aren't force exiting the whole process and bypassing normal interpreter destruction. Graham ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] A 'shutdown' function in WSGI
2012/2/20 Chris McDonough chr...@plope.com On Mon, 2012-02-20 at 17:39 -0500, PJ Eby wrote: The standard way to do this would be to define an optional server extension API supplied in the environ; for example, a 'x-wsgiorg.register_shutdown' function. Unlikely, AFACIT, as shutdown may happen when no request is active. Even if this somehow happened to not be the case, asking the application to put it in the environ is not useful, as the environ can't really be relied on to retain values up the call stack. Optional server extension APIs are things that the server puts in the environ, not things the app puts there. That's why it's 'register_shutdown', e.g. environ['x-wsgiorg.register_shutdown'](shutdown_function). - C The wsgi.org wiki used to be the place to propose these sorts of things for standardization, but it appears to no longer be a wiki, so the mailing list is probably a good place to discuss such a proposal. On Mon, Feb 20, 2012 at 2:30 PM, Tarek Ziadé ziade.ta...@gmail.com wrote: oops my examples were broken, should be: def hello_world_app(environ, start_response): status = '200 OK' # HTTP Status headers = [('Content-type', 'text/plain')] start_response(status, headers) return [Hello World] def shutdown(): # or maybe something else as an argument I don't know do_some_cleanup() and: $ gunicorn myapp:hello_world_app myapp:shutdown Cheers Tarek ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/pje% 40telecommunity.com ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/chrism%40plope.com ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] A 'shutdown' function in WSGI
On Mon, 2012-02-20 at 20:54 -0500, PJ Eby wrote: 2012/2/20 Chris McDonough chr...@plope.com On Mon, 2012-02-20 at 17:39 -0500, PJ Eby wrote: The standard way to do this would be to define an optional server extension API supplied in the environ; for example, a 'x-wsgiorg.register_shutdown' function. Unlikely, AFACIT, as shutdown may happen when no request is active. Even if this somehow happened to not be the case, asking the application to put it in the environ is not useful, as the environ can't really be relied on to retain values up the call stack. Optional server extension APIs are things that the server puts in the environ, not things the app puts there. That's why it's 'register_shutdown', e.g. environ['x-wsgiorg.register_shutdown'](shutdown_function). I get it now, but it's still not the right thing I don't think. Servers shut down without issuing any requests at all. - C ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] A 'shutdown' function in WSGI
2012/2/21 Chris McDonough chr...@plope.com On Mon, 2012-02-20 at 20:54 -0500, PJ Eby wrote: 2012/2/20 Chris McDonough chr...@plope.com On Mon, 2012-02-20 at 17:39 -0500, PJ Eby wrote: The standard way to do this would be to define an optional server extension API supplied in the environ; for example, a 'x-wsgiorg.register_shutdown' function. Unlikely, AFACIT, as shutdown may happen when no request is active. Even if this somehow happened to not be the case, asking the application to put it in the environ is not useful, as the environ can't really be relied on to retain values up the call stack. Optional server extension APIs are things that the server puts in the environ, not things the app puts there. That's why it's 'register_shutdown', e.g. environ['x-wsgiorg.register_shutdown'](shutdown_function). I get it now, but it's still not the right thing I don't think. Servers shut down without issuing any requests at all. Yes, I also think shutting down the server is completely orthogonal to requests. Maybe another option would be to call the application with the usual callable, but an ending request that's a signal for the application about being shut down. When the app receives that very specific request, it would do the cleaning job. It sounds hackish but would work without changing the standard - C -- Tarek Ziadé | http://ziade.org ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] A 'shutdown' function in WSGI
On Tue, Feb 21, 2012 at 2:39 AM, Graham Dumpleton graham.dumple...@gmail.com wrote: ... Overall the best chance of being able to do anything is relying on atexit. You are though at the mercy of the WSGI hosting mechanism shutting down the process and so the interpreter, in an orderly manner such that atexit callbacks get called. In Apache/mod_wsgi you get this guarantee, even in sub interpreters where atexit callbacks wouldn't normally be called when they are destroyed. For uWSGI, atexit callbacks will not be called at the moment, by Robert is making changes to it so you get a guarantee there as well. It is possible he is only doing this though for case where main interpreter is being used, as doing it for sub interpreters is a bit fiddly. But I don't think you can guarantee that everything is still up in memory by the time atexit gets called, so you can't really call cleanup code there. Any pure Python WSGI servers shouldn't have issues so long as they aren't force exiting the whole process and bypassing normal interpreter destruction. what do you mean by bypassing its destruction ? Cheers Tarek Graham ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/ziade.tarek%40gmail.com -- Tarek Ziadé | http://ziade.org ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com
Re: [Web-SIG] A 'shutdown' function in WSGI
Le 21/02/2012 08:47, Tarek Ziadé a écrit : Yes, I also think shutting down the server is completely orthogonal to requests. If the shutdown callback is next to the application and not registered in a request, why not also have the symmetric server start up callback that would not wait for a request? This would avoid workarounds like Flask.before_first_request. Both of these callbacks could be called once per process (aka. space where requests share memory.) Instead of having to provide two or three objects separately to a server, how about making the callbacks attributes of the application callable? Regards, -- Simon Sapin ___ Web-SIG mailing list Web-SIG@python.org Web SIG: http://www.python.org/sigs/web-sig Unsubscribe: http://mail.python.org/mailman/options/web-sig/archive%40mail-archive.com