Caching is not working on routes that are generated from a factory function
My router is generate from a factory function like this:
def api_router_factory(prefix, schema, model):
router = APIRouter(prefix=prefix, responses={404: {"description": "Not found"}})
router.get("/", response_model=list[schema])(
cache(expire=100)(
multi_fetch_router_factory(multi_fetch_crud_factory(model))
)
)
return router
However, the cache is never HIT. I also tried adding explicit return types to the functions that are generated in the factory functions (multi_fetch_router_factory and multi_fetch_crud_factory), but that does not help.
I do have the same issue when using APIRouter nothing is cached
Eg:
router = APIRouter(
prefix="/emails/provider",
responses={404: {"description": "Not found"}},
)
@router.post(
"/sync/{email_type}",
dependencies=[Security(get_current_user, scopes=["lj:admin:all", "lj:email:read"])],
)
@cache(expire=60, key_builder=custom_key_builder)
async def sync(
email_type: str,
request: Request,
params: Params = Depends(),
current_user: dict = Security(get_current_user, scopes=["lj:admin:all", "lj:email:read"]),
):
It's never hit
I found it did not work with Depends, but im looking into it
in my case the issue was that a new cache key was generated each time. The key builder uses the string representation of the module, function name, arguments, and keyword arguments to generate the cache key. This causes issues when using custom class in Depends, like Params.
class Params:
pass
By default, the string representation of Params is inherited from Python’s object class, which includes the memory address, e.g., <main.Params object at 0x1098a2e90>. When this address changes, a new cache key is generated, leading to a cache miss when expected to hit.
To fix this issue, you can override the __repr__ method in your Params class to provide a consistent string representation. Here’s how you can do it:
from fastapi import Depends, FastAPI
from fastapi_cache import FastAPICache
from fastapi_cache.backends.inmemory import InMemoryBackend
from fastapi_cache.decorator import cache
app = FastAPI()
FastAPICache.init(InMemoryBackend())
class Params:
def __repr__(self):
return "Params()"
@app.get("/test")
@cache(namespace="test", expire=10)
def test(params: Params = Depends()):
return {"ok": True}
Without overriding repr, the generated cache keys can vary each time, like:
:test:01e4b84ba7818803a3a3da1bd1b5f032
:test:f99a3f33170b71f1cf0f65d772eaefbb
:test:cc929569696662be729ab3ec4bcd8f7c
After overriding repr, the cache key becomes consistent and remains the same for identical parameters:
:test:473ba42f62c0c987026985045eccd0a8
This ensures that the cache key remains the same for the same parameters, avoiding cache misses