Files
sqlalchemy/examples/performance/short_selects.py
T
Mike Bayer 3dc9a4a239 introduce deferred lambdas
The coercions system allows us to add in lambdas as arguments
to Core and ORM elements without changing them at all.   By allowing
the lambda to produce a deterministic cache key where we can also
cheat and yank out literal parameters means we can move towards
having 90% of "baked" functionality in a clearer way right in
Core / ORM.

As a second step, we can have whole statements inside the lambda,
and can then add generation with __add__(), so then we have
100% of "baked" functionality with full support of ad-hoc
literal values.

Adds some more short_selects tests for the moment for comparison.

Other tweaks inside cache key generation as we're trying to
approach a certain level of performance such that we can
remove the use of "baked" from the loader strategies.

As we have not yet closed #4639, however the caching feature
has been fully integrated as of
b0cfa7379c, we will also
add complete caching documentation here and close that issue
as well.

Closes: #4639
Fixes: #5380
Change-Id: If91f61527236fd4d7ae3cad1f24c38be921c90ba
2020-07-03 23:39:51 -04:00

209 lines
5.8 KiB
Python

"""This series of tests illustrates different ways to SELECT a single
record by primary key
"""
import random
from sqlalchemy import bindparam
from sqlalchemy import Column
from sqlalchemy import create_engine
from sqlalchemy import Integer
from sqlalchemy import select
from sqlalchemy import String
from sqlalchemy.ext import baked
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.future import select as future_select
from sqlalchemy.orm import deferred
from sqlalchemy.orm import Session
from sqlalchemy.sql import lambdas
from . import Profiler
Base = declarative_base()
engine = None
ids = range(1, 11000)
class Customer(Base):
__tablename__ = "customer"
id = Column(Integer, primary_key=True)
name = Column(String(255))
description = Column(String(255))
q = Column(Integer)
p = Column(Integer)
x = deferred(Column(Integer))
y = deferred(Column(Integer))
z = deferred(Column(Integer))
Profiler.init("short_selects", num=10000)
@Profiler.setup
def setup_database(dburl, echo, num):
global engine
engine = create_engine(dburl, echo=echo)
Base.metadata.drop_all(engine)
Base.metadata.create_all(engine)
sess = Session(engine)
sess.add_all(
[
Customer(
id=i,
name="c%d" % i,
description="c%d" % i,
q=i * 10,
p=i * 20,
x=i * 30,
y=i * 40,
)
for i in ids
]
)
sess.commit()
@Profiler.profile
def test_orm_query_classic_style(n):
"""classic ORM query of the full entity."""
session = Session(bind=engine)
for id_ in random.sample(ids, n):
session.query(Customer).filter(Customer.id == id_).one()
@Profiler.profile
def test_orm_query_new_style(n):
"""new style ORM select() of the full entity."""
session = Session(bind=engine)
for id_ in random.sample(ids, n):
stmt = future_select(Customer).where(Customer.id == id_)
session.execute(stmt).scalar_one()
@Profiler.profile
def test_orm_query_new_style_using_embedded_lambdas(n):
"""new style ORM select() of the full entity w/ embedded lambdas."""
session = Session(bind=engine)
for id_ in random.sample(ids, n):
stmt = future_select(lambda: Customer).where(
lambda: Customer.id == id_
)
session.execute(stmt).scalar_one()
@Profiler.profile
def test_orm_query_new_style_using_external_lambdas(n):
"""new style ORM select() of the full entity w/ external lambdas."""
session = Session(bind=engine)
for id_ in random.sample(ids, n):
stmt = lambdas.lambda_stmt(lambda: future_select(Customer))
stmt += lambda s: s.where(Customer.id == id_)
session.execute(stmt).scalar_one()
@Profiler.profile
def test_orm_query_classic_style_cols_only(n):
"""classic ORM query against columns"""
session = Session(bind=engine)
for id_ in random.sample(ids, n):
session.query(Customer.id, Customer.name, Customer.description).filter(
Customer.id == id_
).one()
@Profiler.profile
def test_orm_query_new_style_ext_lambdas_cols_only(n):
"""new style ORM query w/ external lambdas against columns."""
s = Session(bind=engine)
for id_ in random.sample(ids, n):
stmt = lambdas.lambda_stmt(
lambda: future_select(
Customer.id, Customer.name, Customer.description
)
) + (lambda s: s.filter(Customer.id == id_))
s.execute(stmt).one()
@Profiler.profile
def test_baked_query(n):
"""test a baked query of the full entity."""
bakery = baked.bakery()
s = Session(bind=engine)
for id_ in random.sample(ids, n):
q = bakery(lambda s: s.query(Customer))
q += lambda q: q.filter(Customer.id == bindparam("id"))
q(s).params(id=id_).one()
@Profiler.profile
def test_baked_query_cols_only(n):
"""test a baked query of only the entity columns."""
bakery = baked.bakery()
s = Session(bind=engine)
for id_ in random.sample(ids, n):
q = bakery(
lambda s: s.query(Customer.id, Customer.name, Customer.description)
)
q += lambda q: q.filter(Customer.id == bindparam("id"))
q(s).params(id=id_).one()
@Profiler.profile
def test_core_new_stmt_each_time(n):
"""test core, creating a new statement each time."""
with engine.connect() as conn:
for id_ in random.sample(ids, n):
stmt = select([Customer.__table__]).where(Customer.id == id_)
row = conn.execute(stmt).first()
tuple(row)
@Profiler.profile
def test_core_new_stmt_each_time_compiled_cache(n):
"""test core, creating a new statement each time, but using the cache."""
compiled_cache = {}
with engine.connect().execution_options(
compiled_cache=compiled_cache
) as conn:
for id_ in random.sample(ids, n):
stmt = select([Customer.__table__]).where(Customer.id == id_)
row = conn.execute(stmt).first()
tuple(row)
@Profiler.profile
def test_core_reuse_stmt(n):
"""test core, reusing the same statement (but recompiling each time)."""
stmt = select([Customer.__table__]).where(Customer.id == bindparam("id"))
with engine.connect() as conn:
for id_ in random.sample(ids, n):
row = conn.execute(stmt, id=id_).first()
tuple(row)
@Profiler.profile
def test_core_reuse_stmt_compiled_cache(n):
"""test core, reusing the same statement + compiled cache."""
stmt = select([Customer.__table__]).where(Customer.id == bindparam("id"))
compiled_cache = {}
with engine.connect().execution_options(
compiled_cache=compiled_cache
) as conn:
for id_ in random.sample(ids, n):
row = conn.execute(stmt, id=id_).first()
tuple(row)
if __name__ == "__main__":
Profiler.main()