Skip to content

Commit 3275dc0

Browse files
committed
fix: Improve performance up to 50%
1 parent a0d134b commit 3275dc0

3 files changed

Lines changed: 50 additions & 6 deletions

File tree

sqlalchemy_serializer/lib/fields.py

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,25 @@
1-
import functools
21
import inspect
32

43
from sqlalchemy import inspect as sql_inspect
54

5+
# Cache for get_serializable_keys results,
6+
# keyed by (class, serializable_keys_tuple, auto_serialize_properties)
7+
_serializable_keys_cache: dict[tuple[type, tuple | None, bool], set[str]] = {}
8+
9+
10+
def _get_cache_key(model_instance) -> tuple[type, tuple | None, bool]:
11+
"""Generate cache key from model instance configuration.
12+
13+
:param model_instance: Model instance to extract cache key from
14+
:return: Tuple of (class, serializable_keys_tuple, auto_serialize_properties)
15+
"""
16+
cls = model_instance.__class__
17+
serializable_keys = (
18+
tuple(model_instance.serializable_keys) if model_instance.serializable_keys else None
19+
)
20+
auto_serialize_properties = bool(model_instance.auto_serialize_properties)
21+
return (cls, serializable_keys, auto_serialize_properties)
22+
623

724
def get_sql_field_names(model_instance) -> set[str]:
825
""":return: set of sql fields names
@@ -18,18 +35,27 @@ def get_property_field_names(model_instance) -> set[str]:
1835
return {name for name, member in inspect.getmembers(cls) if isinstance(member, property)}
1936

2037

21-
@functools.lru_cache
2238
def get_serializable_keys(model_instance) -> set[str]:
2339
""":return: set of keys available for serialization
2440
:raise: sqlalchemy.exc.NoInspectionAvailable if model_instance is not an sqlalchemy mapper
41+
42+
Results are cached by class and configuration
43+
(serializable_keys, auto_serialize_properties)
44+
rather than by instance, improving cache efficiency
45+
for collections of the same model type.
2546
"""
47+
cache_key = _get_cache_key(model_instance)
48+
49+
if cache_key in _serializable_keys_cache:
50+
return _serializable_keys_cache[cache_key]
51+
2652
if model_instance.serializable_keys:
2753
result = set(model_instance.serializable_keys)
28-
2954
else:
3055
result = get_sql_field_names(model_instance)
3156

3257
if model_instance.auto_serialize_properties:
3358
result.update(get_property_field_names(model_instance))
3459

60+
_serializable_keys_cache[cache_key] = result
3561
return result

tests/test_complex_nested_models.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -622,9 +622,7 @@ def test_serialize_columns_nested_model_fields(get_instance):
622622
serialize_columns={
623623
"string": lambda v: v.lower() if v else None,
624624
"model": (
625-
lambda v: {"custom_id": v.id, "custom_string": v.string.upper()}
626-
if v
627-
else None
625+
lambda v: {"custom_id": v.id, "custom_string": v.string.upper()} if v else None
628626
),
629627
}
630628
)

tests/test_performance.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,3 +199,23 @@ def serialize():
199199
assert isinstance(result, dict)
200200
assert "date" in result
201201
assert "money" in result
202+
203+
204+
def test_benchmark_large_collection_serialization(benchmark, get_instance):
205+
"""Benchmark serialization of a large collection to measure cache efficiency.
206+
207+
This test demonstrates the performance benefit of class-based caching:
208+
- Serializing 1000 instances of the same model class should benefit from
209+
a single cache entry for get_serializable_keys() instead of 1000 entries.
210+
"""
211+
# Create a large collection of the same model type
212+
models = [get_instance(FlatModel) for _ in range(1000)]
213+
214+
def serialize():
215+
return serialize_collection(models, only=("id", "string", "bool", "datetime"))
216+
217+
result = benchmark(serialize)
218+
assert isinstance(result, list)
219+
assert len(result) == 1000
220+
assert all(isinstance(item, dict) for item in result)
221+
assert all("id" in item for item in result)

0 commit comments

Comments
 (0)