importlater() causes a blocking condition
Hi,
an application of mine has been working fine up to SQLA 0.6.5, but from 0.6.6 on* "database access" blocks in about one of three application runs. When the "database access" blocks, the rest of the application that is up&running to this point and does not use the database, continues to work. Everything else comes to a halt. This halt occurs with the first database actions performed right after the application startup. The application will not shut down (gracefully at least) and has to be terminated.
The application works with many threads that are spawned during startup after the applcation basics are set up. Many of those work with the database. It's not unlikely that a few happen to start their database activity at the same time.
After all kinds of debugging I ended up comparing 0.6.5 to 0.6.6 for a second time and the appearance of importlater() caught my eye. In the past I found that python, under certain conditions, can block/deadlock during an import. I extended importlater()'s _il_module property to print
print >> sys.stderr, u"from %s import %s" % (self._il_path, self._il_addtl)
before importing and, as the finally-step of an added try-finally construct print
print >> sys.stderr, u"from %s import %s done" % (self._il_path, self._il_addtl)
after the import.
In every case, where the application did block in a run, the result was identical:
from sqlalchemy.engine import url
from sqlalchemy.engine import url done
from sqlalchemy.orm import mapperlib
from sqlalchemy.orm import mapperlib done
from sqlalchemy.orm import properties
from sqlalchemy.orm import properties done
from sqlalchemy.sql import util
from sqlalchemy.sql import util done
from sqlalchemy.sql import functions
from sqlalchemy.sql import functions done
from sqlalchemy.orm import util
In every case, where the application did NOT block in a run, the result was identical:
from sqlalchemy.engine import url
from sqlalchemy.engine import url done
from sqlalchemy.orm import mapperlib
from sqlalchemy.orm import mapperlib done
from sqlalchemy.orm import properties
from sqlalchemy.orm import properties done
from sqlalchemy.sql import util
from sqlalchemy.sql import util done
from sqlalchemy.sql import functions
from sqlalchemy.sql import functions done
from sqlalchemy.orm import util
from sqlalchemy.orm import util done
It appears that from sqlalchemy.orm import util can cause the interpreter** to block at times. This import is only performed once in all sqlalchemy 0.6.6 code, in orm/attributes.py.
I've been trying to isolate the issue and build a small environment to reproduce it. Unfortunately I wasn't able to cause the import at all. I'm not sure under which conditions it is performed and how I have to set up my entities for it to occur. A traceback.print_stack() in orm/attributes, right before
self.is_equal = mapperutil.identity_equal
gave the following stack trace:
File "/usr/lib/python2.6/threading.py", line 504, in __bootstrap
self.__bootstrap_inner()
File "/usr/lib/python2.6/threading.py", line 532, in __bootstrap_inner
self.run()
File "include/xx/xxxxxxxx/xxxx/xxxx.py", line 239, in run_with_except_hook
run_old(*args, **kw)
File "include/xx/xxxxxxxx/xxx/xxxxxxx/xxxxx/xxxxxx.py", line 38, in run
self._process_spool()
File "include/xx/xxxxxxxx/xxx/xxxxxxx/xxxxx/xxxxxx.py", line 58, in _process_spool
self._try_to_process_spool()
File "include/xx/xxxxxxxx/xxx/xxxxxxx/xxxxx/xxxxxx.py", line 70, in _try_to_process_spool
self._fetch_spool_entries()
File "include/xx/xxxxxxxx/xxx/xxxxxxx/xxxxx/xxxxxx.py", line 75, in _fetch_spool_entries
query = self._session.query(MyEntity).\
File "include/sqlalchemy/orm/session.py", line 862, in query
return self._query_cls(entities, self, **kwargs)
File "include/sqlalchemy/orm/query.py", line 107, in __init__
self._set_entities(entities)
File "include/sqlalchemy/orm/query.py", line 116, in _set_entities
self._setup_aliasizers(self._entities)
File "include/sqlalchemy/orm/query.py", line 131, in _setup_aliasizers
_entity_info(entity)
File "include/sqlalchemy/orm/util.py", line 548, in _entity_info
mapper = mapper.compile()
File "include/sqlalchemy/orm/mapper.py", line 816, in compile
mapper._post_configure_properties()
File "include/sqlalchemy/orm/mapper.py", line 849, in _post_configure_properties
prop.post_instrument_class(self)
File "include/sqlalchemy/orm/interfaces.py", line 688, in post_instrument_class
self.strategy.init_class_attribute(mapper)
File "include/sqlalchemy/orm/strategies.py", line 404, in init_class_attribute
not self.use_get,
File "include/sqlalchemy/orm/strategies.py", line 70, in _register_attribute
**kw
File "include/sqlalchemy/orm/attributes.py", line 1497, in register_attribute_impl
impl = ScalarObjectAttributeImpl(class_, key, callable_, **kw)
File "include/sqlalchemy/orm/attributes.py", line 573, in __init__
print_stack()
I found that forcing the lazy import
from sqlalchemy.orm.attributes import mapperutil
mapperutil.identity_equal
to be performed before the first database operation and before all threads are started, will work around the issue.
Let me know if I can provide further details.
Kind regards,[BR] Thomas Wiebe
- tested with 0.6.6, 0.6.7, 0.6.8, 0.7.2[BR] ** tested with python 2.5.4 and 2.6.5 on x86_64 Linux machines
Comments (19)
-
repo owner -
repo owner - changed milestone to 0.7.3
-
repo owner also what happens if you do something like this: place an import of everything in sqlalchemy/orm/init.py:
from sqlalchemy.orm import collections ,dependency, \ mapper, util, dynamic, \ interfaces, \ query, strategies, sync, \ unitofwork, session, attributes
this would ensure everything is actually in sys.modules by the time things run. i can try adding something like that, perhaps modifying importlater() to only search in sys.modules preventing an actual code load from occurring in a non-main thread.
-
Account Deleted I was so vague about the certain conditions I ran into, as I actually don't recall them anymore. Two issues I found googling, though they might be resolved already and probably are not related to this particular deadlock anyway:[BR] http://bugs.python.org/issue683658 [BR] http://bugs.python.org/issue1590864
I try to avoid function-level/lazy imports where possible ever since I ran into an import deadlock. Maybe that's just what we need to do here, painful as it is: Restructure the code to get rid of circular imports.
As for the second possible workaround you posted:[BR] When placed on top of the existing imports in orm/init.py, we run into a circular import:
File "include/sqlalchemy/orm/__init__.py", line 15, in <module> from sqlalchemy.orm import collections ,dependency, \ File "include/sqlalchemy/orm/dynamic.py", line 18, in <module> from sqlalchemy.orm import ( File "include/sqlalchemy/orm/strategies.py", line 19, in <module> from sqlalchemy.orm import session as sessionlib File "include/sqlalchemy/orm/session.py", line 14, in <module> from sqlalchemy.orm import ( File "include/sqlalchemy/orm/query.py", line 29, in <module> from sqlalchemy.orm import ( ImportError: cannot import name object_mapper
When placed at the bottom, it has no effect. The deadlock still occurs.
As for the first possible workaround you posted:[BR] compile_mappers() helps when placed before the database action begins.
Best, Thomas
-
repo owner Replying to guest:
As for the second possible workaround you posted:[BR]
When placed at the bottom, it has no effect. The deadlock still occurs.
right, I meant at the bottom. So when that occurs, all the modules in the ORM are loaded. At that point it's assumed that any call to import within the ORM will simply be returning something from sys.modules. However, import probably still uses a mutex for this operation. So I'm thinking that importlater() just looks directly in sys.modules and it's assumed things have been worked out beforehand - or maybe does some kind of fallback approach. I'd need to give you a patch for that as it is a more involved change.
As for the first possible workaround you posted:[BR] compile_mappers() helps when placed before the database action begins.
right that should ensure all the importlaters actually work out.
-
repo owner OK not so involved, abstraction to the rescue. So 2279.patch is against 0.6 since that's your target platform. That should ensure that import is never called period by importlater(), as the base init.py of core and ORM force it to import everything. Once that works I'll modify importlater() further so that the import is only called by this initialization routine, and never on the fly.
-
repo owner ..so, let me know if 2279.patch works ! thanks
-
Account Deleted The patch works. Thanks Mike!
Best, Thomas
-
repo owner bang ! wow that's great. I'll tag this on 0.6.
-
repo owner - changed status to resolved
OK we've got those in 0.7, 0.6:
2aa80d40d2c9a00bb87a145bba1f01c327b6000b 944e9029006792f0052d9870100d2f7c801942f1
thanks for the help !
-
Account Deleted - removed status
- changed status to open
Thank you, Michael!
I have just made the first steps to convert the application to 0.7, giving 0.7.2 a try. I applied your 0.7-patch. Unfortunately we didn't fix everything, only the importlater()-cases.
It turns out, that other function-level imports have the same effect. gdb's backtrace, matching all of the threads I spot-checked:
#0 0x00007f7d3a144b50 in sem_wait () from /lib/libpthread.so.0 #1 0x00000000004d4498 in PyThread_acquire_lock (lock=0xe1fb80, waitflag=128) at ../Python/thread_pthread.h:349 #2 0x00000000004bbb13 in _PyImport_AcquireLock () at ../Python/import.c:277 #3 0x00000000004bfbfe in PyImport_ImportModuleLevel (name=0xe1fb80 "", globals=<unknown at remote 0x80>, locals=0x0, fromlist=<unknown at remote 0x-1>, level=<value optimized out>) at ../Python/import.c:2183 #4 0x00000000004a16ab in builtin___import__ (self=<value optimized out>, args=<value optimized out>, kwds=<value optimized out>) at ../Python/bltinmodule.c:48 #5 0x000000000041f0c7 in PyObject_Call (func=<built-in function __import__>, arg=<unknown at remote 0x80>, kw=0x0) at ../Objects/abstract.c:2492 #6 0x00000000004a226f in PyEval_CallObjectWithKeywords (func=<built-in function __import__>, arg= ('sqlalchemy.orm', {'InstrumentationEvents': <_EventMeta(class_instrument=<function at remote 0xec6758>, __module__='sqlalchemy.orm.events', attribute_instrument=<function at remote 0xec6848>, _listen=<classmethod at remote 0xeb9d00>, dispatch=<type at remote 0xee95d0>, _remove=<classmethod at remote 0xeb9d38>, class_uninstrument=<function at remote 0xec67d0>, __doc__="Events related to class instrumentation events.\n\n The listeners here support being established against\n any new style class, that is any object that is a subclass\n of 'type'. Events will then be fired off for events\n against that class as well as all subclasses.\n 'type' itself is also accepted as a target\n in which case the events fire for all classes.\n\n ", _accept_with=<classmethod at remote 0xeb9cc8>) at remote 0xee8b30>, 'MapperEvents': <_EventMeta(append_result=<function at remote 0xeca1b8>, __module__='sqlalchemy.orm.events', create_instance=<function at remote 0xeca140>, translate_row=<function at remote 0xe...(truncated), kw=<value optimized out>) at ../Python/ceval.c:3619 #7 0x00000000004a55fc in PyEval_EvalFrameEx (f= Frame 0x46a41f0, for file include/sqlalchemy/orm/events.py, line 797, in _accept_with (cls=<_EventMeta(__module__='sqlalchemy.orm.events', dispatch=<type at remote 0xef1e80>, after_bulk_delete=<function at remote 0xecab18>, after_begin=<function at remote 0xeca9b0>, after_flush_postexec=<function at remote 0xeca938>, before_commit=<function at remote 0xeca6e0>, __doc__='Define events specific to :class:`.Session` lifecycle.\n\n e.g.::\n\n from sqlalchemy import event\n from sqlalchemy.orm import sessionmaker\n\n def my_before_commit(session):\n print "before commit!"\n\n Session = sessionmaker()\n\n event.listen(Session, "before_commit", my_before_commit)\n\n The :func:`~.event.listen` function will accept\n :class:`.Session` objects as well as the return result\n of :func:`.sessionmaker` and :func:`.scoped_session`.\n\n Additionally, it accepts the :class:`.Session` class which\n will apply listeners to all :clas...(truncated), throwflag=<value optimized out>) at ../Python/ceval.c:2159 #8 0x00000000004a8550 in fast_function (f= Frame 0x46a3eb0, for file include/sqlalchemy/event.py, line 35, in listen (target=<Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<function at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={}, password='XXX', port=None) at remote 0x106b...(truncated), throwflag=<value optimized out>) at ../Python/ceval.c:3836 #9 call_function (f= Frame 0x46a3eb0, for file include/sqlalchemy/event.py, line 35, in listen (target=<Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<function at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={}, password='XXX', port=None) at remote 0x106b...(truncated), throwflag=<value optimized out>) at ../Python/ceval.c:3771 #10 PyEval_EvalFrameEx (f= Frame 0x46a3eb0, for file include/sqlalchemy/event.py, line 35, in listen (target=<Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<function at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={}, password='XXX', port=None) at remote 0x106b...(truncated), throwflag=<value optimized out>) at ../Python/ceval.c:2412 #11 0x00000000004a9671 in PyEval_EvalCodeEx (co=0xca8e40, globals=<value optimized out>, locals=<value optimized out>, args=0x3, argcount=<value optimized out>, kws=<value optimized out>, kwcount=0, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3000 #12 0x00000000004a7809 in fast_function (f= Frame 0x46a3c80, for file include/sqlalchemy/orm/deprecated_interfaces.py, line 417, in _adapt_listener (cls=<type at remote 0xefe5c0>, self=<Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<function at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={...(truncated), throwflag=<value optimized out>) at ../Python/ceval.c:3846 #13 call_function (f= Frame 0x46a3c80, for file include/sqlalchemy/orm/deprecated_interfaces.py, line 417, in _adapt_listener (cls=<type at remote 0xefe5c0>, self=<Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<function at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={...(truncated), throwflag=<value optimized out>) at ../Python/ceval.c:3771 #14 PyEval_EvalFrameEx (f= Frame 0x46a3c80, for file include/sqlalchemy/orm/deprecated_interfaces.py, line 417, in _adapt_listener (cls=<type at remote 0xefe5c0>, self=<Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<function at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={...(truncated), throwflag=<value optimized out>) at ../Python/ceval.c:2412 ---Type <return> to continue, or q <return> to quit--- #15 0x00000000004a8550 in fast_function (f= Frame 0x46a3050, for file include/sqlalchemy/orm/session.py, line 514, in __init__ (self=<Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<function at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={}, password='XXX', port=None) at remote...(truncated), throwflag=<value optimized out>) at ../Python/ceval.c:3836 #16 call_function (f= Frame 0x46a3050, for file include/sqlalchemy/orm/session.py, line 514, in __init__ (self=<Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<function at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={}, password='XXX', port=None) at remote...(truncated), throwflag=<value optimized out>) at ../Python/ceval.c:3771 #17 PyEval_EvalFrameEx (f= Frame 0x46a3050, for file include/sqlalchemy/orm/session.py, line 514, in __init__ (self=<Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<function at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={}, password='XXX', port=None) at remote...(truncated), throwflag=<value optimized out>) at ../Python/ceval.c:2412 #18 0x00000000004a9671 in PyEval_EvalCodeEx (co=0xfa85d0, globals=<value optimized out>, locals=<value optimized out>, args=0xb, argcount=<value optimized out>, kws=<value optimized out>, kwcount=5, defs=0xff1860, defcount=10, closure= 0x0) at ../Python/ceval.c:3000 #19 0x000000000053771d in function_call (func=<function at remote 0x101dcf8>, arg= (<Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<function at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={}, password='XXX', port=None) at remote 0x106bf90>, _echo=None, logger=<Logger(name='sqlalchemy.engine.base.Engine', parent=<Logger(name='sqlalchemy.engine', pa...(truncated), kw= {'autoflush': True, 'autocommit': True, 'expire_on_commit': True, 'extension': [__doc__='Registry used for rollback integrity', _local=<thread._local at remote 0x10526f0>) at remote 0x109bba0>, _exception_handler=<function at remote 0x105b938>) at remote 0x106b510>, <CommitIntegrityHandlerSessionExtension(_integrity_registry_class=<Scope(__module__='XXX', __doc__='Registry used for commit integrity', _local=<thread._local at remote 0x10524e0>) at remote 0x108bf60>, _exception_handler=<function at remote 0x105b938>) at remote 0x106bd90>](<RollbackIntegrityHandlerSessionExtension(_integrity_registry_class=<Scope(__module__='XXX',), 'bind': <Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0...(truncated)) at ../Objects/funcobject.c:524 #20 0x000000000041f0c7 in PyObject_Call (func=<function at remote 0x101dcf8>, arg=<unknown at remote 0x80>, kw=0x0) at ../Objects/abstract.c:2492 #21 0x00000000004a6351 in ext_do_call (f= Frame 0x46a2d30, for file include/sqlalchemy/orm/session.py, line 87, in __init__ (self=<Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<function at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={}, password='XXX', port=None) at remote ...(truncated), throwflag=<value optimized out>) at ../Python/ceval.c:4063 #22 PyEval_EvalFrameEx (f= Frame 0x46a2d30, for file include/sqlalchemy/orm/session.py, line 87, in __init__ (self=<Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<function at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={}, password='XXX', port=None) at remote ...(truncated), throwflag=<value optimized out>) at ../Python/ceval.c:2452 #23 0x00000000004a9671 in PyEval_EvalCodeEx (co=0xfa47b0, globals=<value optimized out>, locals=<value optimized out>, args=0x1, argcount=<value optimized out>, kws=<value optimized out>, kwcount=2, defs=0x0, defcount=0, closure= (<cell at remote 0x1044e88>, <cell at remote 0x1044ec0>)) at ../Python/ceval.c:3000 #24 0x000000000053771d in function_call (func=<function at remote 0x1046e60>, arg= (<Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<function at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={}, password='XXX', port=None) at remote 0x106bf90>, _echo=None, logger=<Logger(name='sqlalchemy.engine.base.Engine', parent=<Logger(name='sqlalchemy.engine', pa...(truncated), kw={'autoflush': True, 'autocommit': True}) at ../Objects/funcobject.c:524 #25 0x000000000041f0c7 in PyObject_Call (func=<function at remote 0x1046e60>, arg=<unknown at remote 0x80>, kw=0x0) at ../Objects/abstract.c:2492 #26 0x0000000000427dff in instancemethod_call (func=<function at remote 0x1046e60>, arg= (<Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<functio---Type <return> to continue, or q <return> to quit--- n at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={}, password='XXX', port=None) at remote 0x106bf90>, _echo=None, logger=<Logger(name='sqlalchemy.engine.base.Engine', parent=<Logger(name='sqlalchemy.engine', pa...(truncated), kw={'autoflush': True, 'autocommit': True}) at ../Objects/classobject.c:2579 #27 0x000000000041f0c7 in PyObject_Call (func=<instancemethod at remote 0x45ec140>, arg=<unknown at remote 0x80>, kw=0x0) at ../Objects/abstract.c:2492 #28 0x0000000000477bff in slot_tp_init (self= <Session(autocommit=True, autoflush=True, transaction=None, hash_key=73323024, expire_on_commit=True, _new={}, bind=<Engine(engine=<...>, dialect=<MySQLDialect_mysqldb(_ischema=None, encoding='utf-8', convert_unicode=False, positional=True, type_compiler=<MySQLTypeCompiler(dialect=<...>) at remote 0x106bdd0>, _encoder=<built-in function utf_8_encode>, label_length=None, _description_decoder=<function at remote 0x7f7d38ef8d70>, paramstyle='format', _decoder=<function at remote 0x1064de8>, identifier_preparer=<MySQLIdentifierPreparer_mysqldb(omit_schema=False, dialect=<...>, initial_quote='`', _strings={}, escape_to_quote='``', final_quote='`', escape_quote='`') at remote 0x106bd10>, dbapi=<module at remote 0x7f7d38da9c90>) at remote 0x106bbd0>, url=<URL(username='XXX', database='XXX', host='XXX', drivername='mysql', query={}, password='XXX', port=None) at remote 0x106bf90>, _echo=None, logger=<Logger(name='sqlalchemy.engine.base.Engine', parent=<Logger(name='sqlalchemy.engine', par...(truncated), args=(), kwds={'autoflush': True, 'autocommit': True}) at ../Objects/typeobject.c:5633 #29 0x000000000046f47f in type_call (type=0x1086720, args=(), kwds={'autoflush': True, 'autocommit': True}) at ../Objects/typeobject.c:742 #30 0x000000000041f0c7 in PyObject_Call (func=<type at remote 0x1086720>, arg=<unknown at remote 0x80>, kw=0x0) at ../Objects/abstract.c:2492 #31 0x00000000004a6351 in ext_do_call (f= Frame 0x46a2910, for file include/sqlalchemy/orm/scoping.py, line 47, in __call__ (self=<ScopedSession(session_factory=<type at remote 0x1086720>, registry=<ThreadLocalRegistry(createfunc=<type at remote 0x1086720>, registry=<thread._local at remote 0x1037750>) at remote 0x1048590>) at remote 0x1048510>, kwargs={'autoflush': True, 'autocommit': True}, scope=False), throwflag=<value optimized out>) at ../Python/ceval.c:4063 #32 PyEval_EvalFrameEx (f= Frame 0x46a2910, for file include/sqlalchemy/orm/scoping.py, line 47, in __call__ (self=<ScopedSession(session_factory=<type at remote 0x1086720>, registry=<ThreadLocalRegistry(createfunc=<type at remote 0x1086720>, registry=<thread._local at remote 0x1037750>) at remote 0x1048590>) at remote 0x1048510>, kwargs={'autoflush': True, 'autocommit': True}, scope=False), throwflag=<value optimized out>) at ../Python/ceval.c:2452 #33 0x00000000004a9671 in PyEval_EvalCodeEx (co=0xf84288, globals=<value optimized out>, locals=<value optimized out>, args=0x1, argcount=<value optimized out>, kws=<value optimized out>, kwcount=2, defs=0x0, defcount=0, closure=0x0) at ../Python/ceval.c:3000 ...
compile_mappers() does not help in this case either. The next frames show, that this happens on instantiating a ScopedSession.
Any ideas?
Best regards, Thomas
-
repo owner does that trace indicate the offending "import" statement ? it appears to start from the import call itself without any context. All of the .py files named there do not contain in-function "import" statements. There should be few if any. I just did a spot check through most of the .py files in orm, there's just one inside the constructor of RelationshipProperty() which itself is typically called at the module level at class configuration time.
Still puzzled what is so different about your application that nobody else has ever reported this issue ? are you calling upon create_engine(), mapper(), sessionmaker(), etc. in some unusual way (i.e. those are all module-level configurational functions, are you calling them in some high-volume, ad-hoc way ?)
-
repo owner also, you indicated that the introduction of importlater() in 0.6.6 was the factor that introduced the issue - since that is fixed and you still have the issue, is it no longer the case that importlater() is really the problem ?
-
Account Deleted Actually they do contain function-level imports. See
#7, which points to orm/events.py:797, which isfrom sqlalchemy.orm import ScopedSession, Session
in SQLA 0.7.2.
As to the application: I, too, am puzzled. I am defining the environment up front:
metadata = MetaData() Base = declarative_base(metadata=metadata) Session = sessionmaker() ScopedSession = scoped_session(sessionmaker())
and then, after the configuration files and options are read, but before any other threads are started, creating the engine (with a custom creator, thread-safe) and binding it to the environment:
metadata.bind = engine Session.configure(bind=engine) ScopedSession.configure(bind=engine)
As soon as the application itself is set up, I start a couple of modules. Many of these work with the database and perform database action right away. This is where these blocking conditions occur, when they do.
I have never seen them with <0.6.6. The application went live about a year ago with 0.6.4. Up to 0.6.5 I upgraded with each release. Development started somewhere in the 0.5 line. I stayed at 0.6.5 due to the blocking conditions.
When I had time to debug the block (again), I stumbled upon the importlater() issue. Your fix worked. The patched 0.6.8 works like a charme.
Today, as another task, I evaluated upgrading to 0.7, where this new condition occurred. It is not directly related to importlater(), so a new ticket might be appropriate, but it is related to lazy imports - in this case function-level imports.
-
Account Deleted Addendum: I also use two SessionExtensions and a custom QueuePool (see
#2254). I planned to migrate them to events in the next step. -
repo owner f0f135b6bd20f7ddcce05711d0185c9c03bf2f41 and 4c62bf89c9958f0a4d0cc5b66f07050f8e2cf220 remove inline imports from both events.py. Try again please ?
-
Account Deleted You're the man! :-)
The patch works. Thanks mate!
Best, Thomas
-
repo owner - changed status to resolved
terrific
-
repo owner - removed milestone
Removing milestone: 0.6.9 (automated comment)
- Log in to comment
if the stack trace is showing how we get to the importlater() in question, have you tried calling compile_mappers() before threads start up ? since that is illustrating work within the compile phase - it also uses a lock. Are these "certain conditions" include that the import occurs within a mutex ? if the limiting factor is, "dont use importlaters inside the compile phase", that at least narrows things down. As it stands just rearranging imports will only cause this kind of issue to pop up elsewhere. A major re-organization of all modules such that no cycles occur in imports would be an enormous and very messy undertaking so I'd like to avoid that.