summaryrefslogtreecommitdiff
path: root/test/aaa_profiling
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2019-12-18 21:50:24 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2019-12-19 10:19:29 -0500
commit7e9f273835ac68df894568ba4292bfbc74ce187b (patch)
tree96d3f1590ff45b90a791f92c622737ed3e89fcfb /test/aaa_profiling
parent255a6ee18b9d68b5150f1793e0a318d8ccd913bf (diff)
downloadsqlalchemy-7e9f273835ac68df894568ba4292bfbc74ce187b.tar.gz
Don't apply aliasing + adaption for simple relationship joins
Identified a performance issue in the system by which a join is constructed based on a mapped relationship. The clause adaption system would be used for the majority of join expressions including in the common case where no adaptation is needed. The conditions under which this adaptation occur have been refined so that average non-aliased joins along a simple relationship without a "secondary" table use about 70% less function calls. Change-Id: Ifbe04214576e5a9fac86ca80c1dc7145c27cd50a
Diffstat (limited to 'test/aaa_profiling')
-rw-r--r--test/aaa_profiling/test_orm.py91
1 files changed, 91 insertions, 0 deletions
diff --git a/test/aaa_profiling/test_orm.py b/test/aaa_profiling/test_orm.py
index 209bc02e3..e2df85a33 100644
--- a/test/aaa_profiling/test_orm.py
+++ b/test/aaa_profiling/test_orm.py
@@ -1,12 +1,16 @@
+from sqlalchemy import and_
from sqlalchemy import ForeignKey
from sqlalchemy import inspect
from sqlalchemy import Integer
+from sqlalchemy import join
from sqlalchemy import String
from sqlalchemy import testing
+from sqlalchemy.orm import aliased
from sqlalchemy.orm import Bundle
from sqlalchemy.orm import configure_mappers
from sqlalchemy.orm import defaultload
from sqlalchemy.orm import defer
+from sqlalchemy.orm import join as orm_join
from sqlalchemy.orm import joinedload
from sqlalchemy.orm import Load
from sqlalchemy.orm import mapper
@@ -818,6 +822,93 @@ class JoinedEagerLoadTest(fixtures.MappedTest):
go()
+class JoinConditionTest(fixtures.DeclarativeMappedTest):
+ @classmethod
+ def setup_classes(cls):
+ class A(cls.DeclarativeBasic):
+ __tablename__ = "a"
+
+ id = Column(Integer, primary_key=True)
+ b_id = Column(ForeignKey("b.id"))
+ b = relationship("B")
+
+ class B(cls.DeclarativeBasic):
+ __tablename__ = "b"
+
+ id = Column(Integer, primary_key=True)
+ d_id = Column(ForeignKey("d.id"))
+
+ class C(cls.DeclarativeBasic):
+ __tablename__ = "c"
+
+ id = Column(Integer, primary_key=True)
+ a_id = Column(ForeignKey("a.id"))
+ d_id = Column(ForeignKey("d.id"))
+
+ class D(cls.DeclarativeBasic):
+ __tablename__ = "d"
+
+ id = Column(Integer, primary_key=True)
+
+ j = join(B, D, B.d_id == D.id).join(C, C.d_id == D.id)
+
+ A.d = relationship(
+ "D",
+ secondary=j,
+ primaryjoin=and_(A.b_id == B.id, A.id == C.a_id),
+ secondaryjoin=D.id == B.d_id,
+ uselist=False,
+ viewonly=True,
+ )
+
+ def test_a_to_b_plain(self):
+ A, B = self.classes("A", "B")
+
+ # should not use aliasing or adaption so should be cheap
+ @profiling.function_call_count(times=50)
+ def go():
+ orm_join(A, B, A.b)
+
+ go()
+
+ def test_a_to_b_aliased(self):
+ A, B = self.classes("A", "B")
+
+ a1 = aliased(A)
+
+ # uses aliasing, therefore adaption which is expensive
+ @profiling.function_call_count(times=50)
+ def go():
+ orm_join(a1, B, a1.b)
+
+ go()
+
+ def test_a_to_d(self):
+ A, D = self.classes("A", "D")
+
+ # the join condition between A and D uses a secondary selectable with
+ # overlap so incurs aliasing, which is expensive, there is also a check
+ # that determines that this overlap exists which is not currently
+ # cached
+ @profiling.function_call_count(times=50)
+ def go():
+ orm_join(A, D, A.d)
+
+ go()
+
+ def test_a_to_d_aliased(self):
+ A, D = self.classes("A", "D")
+
+ a1 = aliased(A)
+
+ # aliased, uses adaption therefore expensive
+ @profiling.function_call_count(times=50)
+ def go():
+ orm_join(a1, D, a1.d)
+
+ go()
+
+
class BranchedOptionTest(fixtures.MappedTest):
@classmethod
def define_tables(cls, metadata):