importlater() causes a blocking condition

Issue #2279 resolved
Former user created an issue

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)

  1. Mike Bayer repo owner

    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.

  2. Mike Bayer 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.

  3. Former user 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

  4. Mike Bayer 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.

  5. Mike Bayer 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.

  6. Former user 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

  7. Mike Bayer 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 ?)

  8. Mike Bayer 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 ?

  9. Former user Account Deleted

    Actually they do contain function-level imports. See #7, which points to orm/events.py:797, which is

            from 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.

  10. Former user 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.

  11. Log in to comment