Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flask long lived global resources [duplicate]

Tags:

python

flask

I'm using Flask to build my web application, and I'd like to register a global resource that represents a connection to a remote service which lasts longer than a request (in this case, the connection is a SOAP connection which can be valid for up to 30 days).

Another example could be a database like MongoDB which handles connection-pooling in the driver, and would perform badly if you created a new connection on each request.

Neither the Application Context, nor the Request Context seem appropriate for this task.

The question "Pass another object to the main flask application" suggests that we store such resources on the app.config dictionary.

like image 204
idbentley Avatar asked Oct 23 '13 16:10

idbentley


2 Answers

This is a Python problem, the reason Flask and other web frameworks do not allow this/make it very difficult is because there is no defined memory model for concurrency. E.g. suppose your webserver serves requests in their own processes (e.g. Gunicorn). This will result in processes forking off the main process, and thus copying your "global" variable. If you run a server on windows then due to the lack of a fork it will instead make a new process which reimports all modules, probably producing a clean version of your global state which does not remember any changes made to it in the main process pre-thread. Or not finding it at all if you declare it behind a name guard.

There are only two possible ways to consistently do this "right". One is to build a webapp specifically to hold your global state and which is restricted to a single thread, and have your other webapp call state from this webapp.

The second, is to use Jython + threads. Since Jython runs in a JVM it has a well defined memory model with shared state, so you can use all the java EE tricks like container managed concurrency....

like image 124
phil_20686 Avatar answered Oct 04 '22 16:10

phil_20686


If it MUST coincide with the instantiation of your app, then you should subclass Flask. This really doesn't do a whole lot if all your doing is attaching a resource to the object, considering the creation of the app is a process. Truth is, you probably don't need to do this if the application doesn't need to use your resource during instantiation.

class MyApp(Flask):
    def __init__(self, *args, **kwargs):
        setattr(self, 'some_resource', SomeResource())
        super(Flask, self).__init__(*args, **kwargs)

app = MyApp(__name__)
app.some_resource.do_something()

Unless you have some specific use case, you are probably better off writing a wrapper class, turning it into a flask-extension, creating it on the module level and storing it on app.extensions.

class MyExtensions(object):
    def __init__(self, app=None):
        self.app = app
        if app is not None:
            self.init_app(app)

    def init_app(self, app):
        app.extensions['my_extension'] = SomeResource()

app = Flask(__name__)
my_extension = MyExtension(app)

Then you can choose if you want to give each app its own resource (like above), or if you'd rather use a shared resource that is always pointing to the current app

from flask import _request_ctx_stack
try:
    from flask import _app_ctx_stack
except ImportError:
    _app_ctx_stack = None

stack = _app_ctx_stack or _request_ctx_stack

class SomeResource(object):
    def do_something(self):
        ctx = stack.top
        app = ctx.app
my_resource = SomeResource()
my_resource.do_something()

I don't think you want to store this on the application context, because " it starts when the Flask object is instantiated, and it implicitly ends when the first request comes in"

Instead, you want your resource created at the module level. Then you can attach it to the application as an extension, or yes, even in the config- though it would be more consistent and logical to make a quick extension out of it.

like image 39
tryexceptcontinue Avatar answered Oct 04 '22 17:10

tryexceptcontinue