feat(cache): add vary to key cached responses on request headers - #309
Merged
Merged
Conversation
@cache(vary=["Accept-Language"]) appends a name=value component per listed header to the key, after whatever the key builder returns, and adds the names to the Vary header of every GET response, including 304s and responses that bypass or are not stored in the backend. invalidate() takes the same list. The Vary helper moves from the session middleware to fastapi_cachex/headers.py so both use it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Stacked on #307 (
build_cache_key, #264): the base isfeat/build-cache-key, and GitHub retargets this PR tomasteronce #307 merges. Review only the top commit.Summary
@cache(vary=["Accept-Language"])gives each value of the listed request headers its own entry and sends the names inVary.name=valuecomponent (name lower-cased, value trimmed, repeated header lines joined with,, missing = empty), escaped with the @cache: public build_cache_key() helper so custom key builders don't hand-roll the format #264 helper (build_cache_keyandvaryshare_append_key_components). E.g.GET|||example.com|||/greeting|||||||accept-language=de.key_builder: vary components are appended after whatever the builder returns, so a custom builder andvarycompose (...|||tenant-1|||accept-language=de).Varyheader: added to every response to a GET on the route: 200 and 304, cache hit/miss, and the paths that skip the backend (private,no_store,no_cache, the @cache stores responses marked private/no-store, with Set-Cookie, or for Authorization requests #296Authorizationbypass, unstoredSet-Cookie/private responses). It runs after the response is built, so the @cache stores responses marked private/no-store, with Set-Cookie, or for Authorization requests #296privateCache-Control variants are unchanged (tested). Names already listed (any case) are not repeated, andVary: *is left alone. Non-GET responses are untouched. Routes withoutvaryhave no extra wrapper at all._add_vary: moved fromsession/middleware.pyto a newfastapi_cachex/headers.py(add_vary), used by both the middleware and@cache.CacheXError):varymust be a list/tuple; a barestr(vary="Accept"),bytes, sets, empty names, non-token names (spaces, commas) and*are rejected. Duplicate names (case-insensitive) collapse to the first spelling.invalidate(request, key_builder=None, vary=None): new optionalvaryso it can rebuild a vary route's key (deletes the variant the request selects).clear_path(path)clears every variant thanks to the @cache: public build_cache_key() helper so custom key builders don't hand-roll the format #264 change.varykeep their keys and get noVary(tested).Accept-Languageto a supported locale with a customkey_builder+build_cache_key; invalidate docs and CACHE_FLOW.md updated; zh-TW mirrors. README does not list decorator parameters, so it is unchanged.Follow-up tracked in #310: a normaliser for
vary=headers (e.g.vary={"Accept-Language": normalise}), so the common "reduce to supported values" case doesn't need a custom key builder.Changelog
changelog.d/268.added.mdTest plan
uv run ruff check fastapi_cachex tests && uv run ruff format --check fastapi_cachex tests && uv run mypy fastapi_cachex --strictuv run pytest(no live servers): 984 passed, 192 skipped, coverage 94%Closes #268