mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-17 22:22:13 -04:00
a9b62055bf
Supercedes: If78fbb557c6f2cae637799c3fec2cbc5ac248aaf Trying to see if by making the cache key memoized, we still can have the older "identity" form of caching which is the cheapest of all, at the same time as the newer "cache key each time" version that is not nearly as cheap; but still much cheaper than no caching at all. Also needed is a per-execution update of _keymap when we invoke from a cached select, so that Column objects that are anonymous or otherwise adapted will match up. this is analogous to the adaption of bound parameters from the cache key. Adds test coverage for the keymap / construct_params() changes related to caching. Also hones performance to a large extent for statement construction and cache key generation. Also includes a new memoized attribute approach that vastly simplifies the previous approach of "group_expirable_memoized_property" and finally integrates cleanly with _clone(), _generate(), etc. no more hardcoding of attributes is needed, as well as that most _reset_memoization() calls are no longer needed as the reset is inherent in a _generate() call; this also has dramatic performance improvements. Change-Id: I95c560ffcbfa30b26644999412fb6a385125f663
127 lines
3.9 KiB
Python
127 lines
3.9 KiB
Python
from sqlalchemy import Column
|
|
from sqlalchemy import Enum
|
|
from sqlalchemy import ForeignKey
|
|
from sqlalchemy import Integer
|
|
from sqlalchemy import MetaData
|
|
from sqlalchemy import select
|
|
from sqlalchemy import String
|
|
from sqlalchemy import Table
|
|
from sqlalchemy import testing
|
|
from sqlalchemy.orm import join as ormjoin
|
|
from sqlalchemy.orm import mapper
|
|
from sqlalchemy.orm import relationship
|
|
from sqlalchemy.testing import eq_
|
|
from sqlalchemy.testing import fixtures
|
|
from sqlalchemy.testing import profiling
|
|
from sqlalchemy.util import classproperty
|
|
|
|
|
|
class EnumTest(fixtures.TestBase):
|
|
__requires__ = ("cpython", "python_profiling_backend")
|
|
|
|
def setup(self):
|
|
class SomeEnum(object):
|
|
# Implements PEP 435 in the minimal fashion needed by SQLAlchemy
|
|
|
|
_members = {}
|
|
|
|
@classproperty
|
|
def __members__(cls):
|
|
"""simulate a very expensive ``__members__`` getter"""
|
|
for i in range(10):
|
|
x = {}
|
|
x.update({k: v for k, v in cls._members.items()}.copy())
|
|
return x.copy()
|
|
|
|
def __init__(self, name, value):
|
|
self.name = name
|
|
self.value = value
|
|
self._members[name] = self
|
|
setattr(self.__class__, name, self)
|
|
|
|
for i in range(400):
|
|
SomeEnum("some%d" % i, i)
|
|
|
|
self.SomeEnum = SomeEnum
|
|
|
|
@profiling.function_call_count()
|
|
def test_create_enum_from_pep_435_w_expensive_members(self):
|
|
Enum(self.SomeEnum)
|
|
|
|
|
|
class CacheKeyTest(fixtures.TestBase):
|
|
__requires__ = ("cpython", "python_profiling_backend")
|
|
|
|
@testing.fixture(scope="class")
|
|
def mapping_fixture(self):
|
|
# note in order to work nicely with "fixture" we are emerging
|
|
# a whole new model of setup/teardown, since pytest "fixture"
|
|
# sort of purposely works badly with setup/teardown
|
|
|
|
metadata = MetaData()
|
|
parent = Table(
|
|
"parent",
|
|
metadata,
|
|
Column("id", Integer, primary_key=True),
|
|
Column("data", String(20)),
|
|
)
|
|
child = Table(
|
|
"child",
|
|
metadata,
|
|
Column("id", Integer, primary_key=True),
|
|
Column("data", String(20)),
|
|
Column(
|
|
"parent_id", Integer, ForeignKey("parent.id"), nullable=False
|
|
),
|
|
)
|
|
|
|
class Parent(testing.entities.BasicEntity):
|
|
pass
|
|
|
|
class Child(testing.entities.BasicEntity):
|
|
pass
|
|
|
|
mapper(
|
|
Parent,
|
|
parent,
|
|
properties={"children": relationship(Child, backref="parent")},
|
|
)
|
|
mapper(Child, child)
|
|
|
|
return Parent, Child
|
|
|
|
@testing.fixture(scope="function")
|
|
def stmt_fixture_one(self, mapping_fixture):
|
|
# note that by using ORM elements we will have annotations in these
|
|
# items also which is part of the performance hit
|
|
Parent, Child = mapping_fixture
|
|
|
|
return [
|
|
(
|
|
select([Parent.id, Child.id])
|
|
.select_from(ormjoin(Parent, Child, Parent.children))
|
|
.where(Child.id == 5)
|
|
)
|
|
for i in range(100)
|
|
]
|
|
|
|
@profiling.function_call_count(variance=0.15, warmup=2)
|
|
def test_statement_key_is_cached(self, stmt_fixture_one):
|
|
current_key = None
|
|
for stmt in stmt_fixture_one:
|
|
key = stmt._generate_cache_key()
|
|
if current_key:
|
|
eq_(key, current_key)
|
|
else:
|
|
current_key = key
|
|
|
|
@profiling.function_call_count(variance=0.15, warmup=0)
|
|
def test_statement_key_is_not_cached(self, stmt_fixture_one):
|
|
current_key = None
|
|
for stmt in stmt_fixture_one:
|
|
key = stmt._generate_cache_key()
|
|
if current_key:
|
|
eq_(key, current_key)
|
|
else:
|
|
current_key = key
|