Skip to content

fix(litestar): key the filter dependency cache on dep_defaults too - #786

Open
ruicleite96 wants to merge 1 commit into
litestar-org:mainfrom
ruicleite96:fix-litestar-filter-cache-defaults
Open

fix(litestar): key the filter dependency cache on dep_defaults too#786
ruicleite96 wants to merge 1 commit into
litestar-org:mainfrom
ruicleite96:fix-litestar-filter-cache-defaults

Conversation

@ruicleite96

@ruicleite96 ruicleite96 commented Aug 16, 2026

Copy link
Copy Markdown

Problem

create_filter_dependencies keys its cache on the config alone:

cache_key = hash((_CACHE_NAMESPACE, make_hashable(config)))

but dep_defaults shapes the generated dependencies just as much. It supplies DEFAULT_PAGINATION_SIZE and names all seven dependencies in the returned dict, and the function reads eight of its attributes while building them.

So two callers passing the same config with different defaults share one set, and whichever was built first wins for the whole process:

config = {"pagination_type": "limit_offset"}

class BigPages(DependencyDefaults):
    DEFAULT_PAGINATION_SIZE = 100

create_filter_dependencies(config)              # page_size default 20
create_filter_dependencies(config, BigPages())  # page_size default 20  <- asked for 100

Reversing the build order flips it: the caller who wanted the stock 20 gets 100 instead. Renaming a dependency key fails the same way — a caller overriding LIMIT_OFFSET_FILTER_DEPENDENCY_KEY is handed the previous caller's key and its own is simply absent from the dict, so resolution fails or silently binds nothing.

This is quiet: the app starts, the dependencies exist, and they are configured for somebody else.

Change

Build the key from the defaults' values alongside the config. Keying by value rather than by instance matters — keying on the object would make every explicit dep_defaults a cache miss and defeat the cache entirely, so two equal DependencyDefaults still share one set.

Scope

The FastAPI provider had the same defect and is fixed in #784. This is the Litestar half, which I kept separate so each stays scoped to one extension; the two do not touch the same files and can land in either order.

Note that the other problem #784 fixes — a module-level aggregate function whose __signature__ was rewritten per config — does not affect Litestar. Its provide_filters is defined inside _create_filter_aggregate_function (providers.py:745), so it is already a fresh function per config. I checked by building the dependencies rather than by reading: {"search": "name"} and {"created_at": True} correctly yield ['search_filter'] and ['created_filter'].

Tests

tests/unit/test_extensions/test_litestar/test_provider_cache_defaults.py:

  • one config with two different dep_defaults gets two dependency sets, each with its own page size;
  • two equal DependencyDefaults still share one set, so the fix does not defeat the cache;
  • a renamed dependency key gets its own set rather than the previous caller's.

The first and third fail on main and pass here; the second passes both ways and is there to pin that the cache still works.

Full tests/unit/test_extensions/ passes (606). ruff check, ruff format, mypy, pyright and slotscheck are clean.


📚 Documentation preview: https://litestar-org.github.io/advanced-alchemy-docs-preview/786

`create_filter_dependencies` keyed on the config alone, but `dep_defaults`
shapes the dependencies just as much — it names all seven of them and supplies
the default page size. Two callers passing the same config with different
defaults therefore shared one set, and whichever was built first won for the
whole process: a caller asking for `DEFAULT_PAGINATION_SIZE = 100` silently
served 20, and one renaming `LIMIT_OFFSET_FILTER_DEPENDENCY_KEY` got the
previous caller's key instead of its own.

Key by the defaults' values rather than the instance, so two equal
`DependencyDefaults` still share one set and the cache stays useful.
@codecov-commenter

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.45%. Comparing base (c5ea0c9) to head (46cac29).
⚠️ Report is 3 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #786      +/-   ##
==========================================
+ Coverage   82.43%   82.45%   +0.02%     
==========================================
  Files         105      105              
  Lines        9039     9042       +3     
  Branches     1219     1219              
==========================================
+ Hits         7451     7456       +5     
+ Misses       1262     1260       -2     
  Partials      326      326              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants