ah yes, this is a bug, probably in Neutron's import system though there may be a way to address it in SQLAlchemy by adding a mutex, it's a race. note that the stack trace is generated on a different request id while the SQLAlchemy ORM logging shows it is still in the process of mapping the BGPVPNPortAssociationRoute class itself.
What I need to understand is how is the bgvpn_db.py module being imported? That is, above the logging given above, I assume there's some kind of dynamic module importing that occurs within a service thread and is not simply when the whole application starts up?
I'd like you to try this patch in SQLAlchemy assuming you can reproduce, if you can tell me the specific SQLAlchemy version that's here I can reformat this patch against that version, this is against latest 1.2, the method in question is the "def mapper()" inside of relationships.py:
diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py
index 356b673b66..014004f005 100644
--- a/lib/sqlalchemy/orm/relationships.py
+++ b/lib/sqlalchemy/orm/relationships.py
@@ -45,7 +45,7 @@ from ..sql.util import ClauseAdapter
from ..sql.util import join_condition
from ..sql.util import selectables_overlap
from ..sql.util import visit_binary_product
-
+from .mapper import _CONFIGURE_MUTEX
def remote(expr):
"""Annotate a portion of a primaryjoin expression
@@ -1724,23 +1724,29 @@ class RelationshipProperty(StrategizedProperty):
This is a lazy-initializing static attribute.
"""
- if util.callable(self.argument) and not isinstance(
- self.argument, (type, mapperlib.Mapper)
- ):
- argument = self.argument()
- else:
- argument = self.argument
- if isinstance(argument, type):
- mapper_ = mapperlib.class_mapper(argument, configure=False)
- elif isinstance(self.argument, mapperlib.Mapper):
- mapper_ = argument
- else:
- raise sa_exc.ArgumentError(
- "relationship '%s' expects "
- "a class or a mapper argument (received: %s)"
- % (self.key, type(argument))
- )
+ _CONFIGURE_MUTEX.acquire()
+ try:
+ if util.callable(self.argument) and not isinstance(
+ self.argument, (type, mapperlib.Mapper)
+ ):
+ argument = self.argument()
+ else:
+ argument = self.argument
+
+ if isinstance(argument, type):
+ mapper_ = mapperlib.class_mapper(argument, configure=False)
+ elif isinstance(self.argument, mapperlib.Mapper):
+ mapper_ = argument
+ else:
+ raise sa_exc.ArgumentError(
+ "relationship '%s' expects "
+ "a class or a mapper argument (received: %s)"
+ % (self.key, type(argument))
+ )
+ finally:
+ _CONFIGURE_MUTEX.release()
+
return mapper_
ah yes, this is a bug, probably in Neutron's import system though there may be a way to address it in SQLAlchemy by adding a mutex, it's a race. note that the stack trace is generated on a different request id while the SQLAlchemy ORM logging shows it is still in the process of mapping the BGPVPNPortAssoc iationRoute class itself.
What I need to understand is how is the bgvpn_db.py module being imported? That is, above the logging given above, I assume there's some kind of dynamic module importing that occurs within a service thread and is not simply when the whole application starts up?
I'd like you to try this patch in SQLAlchemy assuming you can reproduce, if you can tell me the specific SQLAlchemy version that's here I can reformat this patch against that version, this is against latest 1.2, the method in question is the "def mapper()" inside of relationships.py:
diff --git a/lib/sqlalchem y/orm/relations hips.py b/lib/sqlalchem y/orm/relations hips.py .014004f005 100644 y/orm/relations hips.py y/orm/relations hips.py product
index 356b673b66.
--- a/lib/sqlalchem
+++ b/lib/sqlalchem
@@ -45,7 +45,7 @@ from ..sql.util import ClauseAdapter
from ..sql.util import join_condition
from ..sql.util import selectables_overlap
from ..sql.util import visit_binary_
-
+from .mapper import _CONFIGURE_MUTEX
def remote(expr): perty(Strategiz edProperty) :
"""Annotate a portion of a primaryjoin expression
@@ -1724,23 +1724,29 @@ class RelationshipPro
This is a lazy-initializing static attribute.
""" self.argument) and not isinstance(
- if util.callable(
- self.argument, (type, mapperlib.Mapper)
- ):
- argument = self.argument()
- else:
- argument = self.argument
- if isinstance( argument, type): class_mapper( argument, configure=False) self.argument, mapperlib.Mapper): ArgumentError( MUTEX.acquire( ) self.argument) and not isinstance( argument, type): class_mapper( argument, configure=False) self.argument, mapperlib.Mapper): ArgumentError( MUTEX.release( )
- mapper_ = mapperlib.
- elif isinstance(
- mapper_ = argument
- else:
- raise sa_exc.
- "relationship '%s' expects "
- "a class or a mapper argument (received: %s)"
- % (self.key, type(argument))
- )
+ _CONFIGURE_
+ try:
+ if util.callable(
+ self.argument, (type, mapperlib.Mapper)
+ ):
+ argument = self.argument()
+ else:
+ argument = self.argument
+
+ if isinstance(
+ mapper_ = mapperlib.
+ elif isinstance(
+ mapper_ = argument
+ else:
+ raise sa_exc.
+ "relationship '%s' expects "
+ "a class or a mapper argument (received: %s)"
+ % (self.key, type(argument))
+ )
+ finally:
+ _CONFIGURE_
+
return mapper_
@util. memoized_ property