Reloader causing locking errors with ZODB
- From:
- Dag Odenhall
- Date:
- 2010-10-19 @ 11:51
Should maybe file a bug report, but I'd like to see some discussion and
see if someone can think of a solution.
Using Flask with ZODB, the reloader causes this error from ZODB:
(stutuz)dag@gumri:~/Dokument/stutuz$ ./manage.py runserver -r
* Running on http://127.0.0.1:5000/
* Restarting with reloader...
No handlers could be found for logger "zc.lockfile"
Traceback (most recent call last):
File "./manage.py", line 22, in <module>
manager.run()
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/Flask_Script-0.3.1-py2.6.egg/flaskext/script.py",
line 684, in run
sys.argv[2:])
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/Flask_Script-0.3.1-py2.6.egg/flaskext/script.py",
line 653, in handle
app = self.create_app(**app_namespace.__dict__)
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/Flask_Script-0.3.1-py2.6.egg/flaskext/script.py",
line 432, in create_app
return self.app(**kwargs)
File "/home/dag/Dokument/stutuz/stutuz/__init__.py", line 24, in
create_app
app.config.from_object(config)
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/Flask-0.6-py2.6.egg/flask/config.py",
line 146, in from_object
obj = import_string(obj)
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/Werkzeug-0.6.2-py2.6.egg/werkzeug/utils.py",
line 526, in import_string
return getattr(__import__(module, None, None, [obj]), obj)
File "/home/dag/Dokument/stutuz/stutuz/configs/development.py", line
22, in <module>
ZODB_STORAGE = FileStorage('var/db/development.fs')
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/ZODB3-3.10.0-py2.6-linux-i686.egg/ZODB/FileStorage/FileStorage.py",
line 126, in __init__
self._lock_file = LockFile(file_name + '.lock')
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/zc.lockfile-1.0.0-py2.6.egg/zc/lockfile/__init__.py",
line 76, in __init__
_lock_file(fp)
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/zc.lockfile-1.0.0-py2.6.egg/zc/lockfile/__init__.py",
line 59, in _lock_file
raise LockError("Couldn't lock %r" % file.name)
zc.lockfile.LockError: Couldn't lock u'var/db/development.fs.lock'
I know it's related to the reloader because it runs fine without it. The
relevant ZODB code:
_flags = fcntl.LOCK_EX | fcntl.LOCK_NB
def _lock_file(file):
try:
fcntl.flock(file.fileno(), _flags)
except IOError:
raise LockError("Couldn't lock %r" % file.name)
Changing it to reraise the IOError:
(stutuz)dag@gumri:~/Dokument/stutuz$ ./manage.py runserver -r
* Running on http://127.0.0.1:5000/
* Restarting with reloader...
No handlers could be found for logger "zc.lockfile"
Traceback (most recent call last):
File "./manage.py", line 22, in <module>
manager.run()
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/Flask_Script-0.3.1-py2.6.egg/flaskext/script.py",
line 684, in run
sys.argv[2:])
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/Flask_Script-0.3.1-py2.6.egg/flaskext/script.py",
line 653, in handle
app = self.create_app(**app_namespace.__dict__)
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/Flask_Script-0.3.1-py2.6.egg/flaskext/script.py",
line 432, in create_app
return self.app(**kwargs)
File "/home/dag/Dokument/stutuz/stutuz/__init__.py", line 24, in
create_app
app.config.from_object(config)
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/Flask-0.6-py2.6.egg/flask/config.py",
line 146, in from_object
obj = import_string(obj)
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/Werkzeug-0.6.2-py2.6.egg/werkzeug/utils.py",
line 526, in import_string
return getattr(__import__(module, None, None, [obj]), obj)
File "/home/dag/Dokument/stutuz/stutuz/configs/development.py", line
22, in <module>
ZODB_STORAGE = FileStorage('var/db/development.fs')
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/ZODB3-3.10.0-py2.6-linux-i686.egg/ZODB/FileStorage/FileStorage.py",
line 126, in __init__
self._lock_file = LockFile(file_name + '.lock')
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/zc.lockfile-1.0.0-py2.6.egg/zc/lockfile/__init__.py",
line 77, in __init__
_lock_file(fp)
File
"/home/dag/.virtualenvs/stutuz/lib/python2.6/site-packages/zc.lockfile-1.0.0-py2.6.egg/zc/lockfile/__init__.py",
line 57, in _lock_file
fcntl.flock(file.fileno(), _flags)
IOError: [Errno 11] Resource temporarily unavailable
As I mentioned, it runs fine without the reloader and also unit tests
for read and write in a route also passes.
I can live without the reloader but it's very handy! I also plan to
release a Flask-ZODB extension eventually so would be nice to solve this
issue. Any ideas?
Dag
Re: [flask] Reloader causing locking errors with ZODB
- From:
- Simon Sapin
- Date:
- 2010-10-19 @ 12:32
Le 19/10/2010 20:51, Dag Odenhall a écrit :
> Should maybe file a bug report, but I'd like to see some discussion
> and see if someone can think of a solution.
>
> Using Flask with ZODB, the reloader causes this error from ZODB:
> [...]
Hi,
The reloader actually spawns a new Python process (and kill it and start
another one to reload), but your code may already have been imported
when that happens. This means that anything that gets done at import
time (such as initializing your Flask app) is done twice. (Once per
process.)
In your code, this is probably it:
File "/home/dag/Dokument/stutuz/stutuz/configs/development.py", line
22, in <module>
ZODB_STORAGE = FileStorage('var/db/development.fs')
Maybe you can setup ZODB "lazily": not at import time but the first time
it is used. Werkzeug's cached_property might help:
http://werkzeug.pocoo.org/documentation/0.6.2/utils.html#werkzeug.cached_property
Regards,
--
Simon Sapin
Re: [flask] Reloader causing locking errors with ZODB
- From:
- Armin Ronacher
- Date:
- 2010-10-19 @ 15:25
Hi,
Alternatively create a separate file called "run.py" and run the Flask
server like this:
def import_on_first_request(environ, start_response):
from yourapplication import app
app.debug = True
return app(environ, start_response)
from werkzeug import run_simple
run_simple('localhost', 5000, import_on_first_request)
And then run this file instead.
Regards,
Armin
Re: [flask] Reloader causing locking errors with ZODB
- From:
- Dag Odenhall
- Date:
- 2010-10-19 @ 16:02
On Tue, 2010-10-19 at 17:25 +0200, Armin Ronacher wrote:
> Hi,
>
> Alternatively create a separate file called "run.py" and run the Flask
> server like this:
>
> def import_on_first_request(environ, start_response):
> from yourapplication import app
> app.debug = True
> return app(environ, start_response)
>
> from werkzeug import run_simple
> run_simple('localhost', 5000, import_on_first_request)
>
> And then run this file instead.
>
>
> Regards,
> Armin
Thanks; though I prefer the solution that works "naturally" with
existing development server setups.
Maybe Flask-Script should do this?
Re: [flask] Reloader causing locking errors with ZODB
- From:
- danjac354@gmail.com
- Date:
- 2010-10-19 @ 16:08
> Thanks; though I prefer the solution that works "naturally" with
> existing development server setups.
>
> Maybe Flask-Script should do this?
>
>
In this case you might be better using an app factory that creates
your app dynamically rather than a "bare" app.
Re: [flask] Reloader causing locking errors with ZODB
- From:
- Dag Odenhall
- Date:
- 2010-10-19 @ 16:32
On Tue, 2010-10-19 at 17:08 +0100, danjac354@gmail.com wrote:
> > Thanks; though I prefer the solution that works "naturally" with
> > existing development server setups.
> >
> > Maybe Flask-Script should do this?
> >
> >
> In this case you might be better using an app factory that creates
> your app dynamically rather than a "bare" app.
I am using the create_app pattern if that's what you mean? Problem is
(was) that it's loading the configuration directly rather than as Armin
suggests, on the first request. Manager needs to do this before it runs
the server though but maybe it'd work if it'd create a new app for the
server.
def import_on_first_request(environ, start_response):
app = create_app(**opts) # Ignoring self.app
return app(environ, start_response)
Duno if this would still be dysfunctional. I guess I could try by making
a separate manager command.
Re: [flask] Reloader causing locking errors with ZODB
- From:
- danjac354@gmail.com
- Date:
- 2010-10-19 @ 16:37
Maybe you could do that in a Server command subclass (override
handle() or run()). Not quite sure though.
On 19 October 2010 17:32, Dag Odenhall <dag.odenhall@gmail.com> wrote:
> On Tue, 2010-10-19 at 17:08 +0100, danjac354@gmail.com wrote:
>> > Thanks; though I prefer the solution that works "naturally" with
>> > existing development server setups.
>> >
>> > Maybe Flask-Script should do this?
>> >
>> >
>> In this case you might be better using an app factory that creates
>> your app dynamically rather than a "bare" app.
>
> I am using the create_app pattern if that's what you mean? Problem is
> (was) that it's loading the configuration directly rather than as Armin
> suggests, on the first request. Manager needs to do this before it runs
> the server though but maybe it'd work if it'd create a new app for the
> server.
>
> def import_on_first_request(environ, start_response):
> app = create_app(**opts) # Ignoring self.app
> return app(environ, start_response)
>
> Duno if this would still be dysfunctional. I guess I could try by making
> a separate manager command.
>
>
Re: [flask] Reloader causing locking errors with ZODB
- From:
- Dag Odenhall
- Date:
- 2010-10-19 @ 13:03
You are absolutely right, thank you very much.
For the curious, I changed the config option to be a callable:
ZODB_STORAGE = lambda: FileStorage('var/db/development.fs')
and made the ZODB a cached_property that calls the config callable:
@cached_property
def db(self):
return DB(self.app.config['ZODB_STORAGE']())
In the testing config I simply don't call (instantiate) the storage
which doesn't take any arguments, so no need for a lambda:
ZODB_STORAGE = DemoStorage
instead of
ZODB_STORAGE = DemoStorage()
Quite a satisfactory solution!