long2ice / fastapi-cache

fastapi-cache is a tool to cache fastapi response and function result, with backends support redis and memcached.
https://github.com/long2ice/fastapi-cache
Apache License 2.0
1.38k stars 167 forks source link

Caching does not work with dependencies #279

Closed entl closed 1 year ago

entl commented 1 year ago

I have a simple crud app. I am using dependencies to get database session. Here is the code to init fastapi-cache

@asynccontextmanager
async def lifespan(app: FastAPI):
    redis = aioredis.from_url("redis://localhost")
    FastAPICache.init(RedisBackend(redis), prefix="fastapi-cache")
    yield
    await redis.close()

app = FastAPI(lifespan=lifespan)

When I try to get data from endpoint without dependencies, I get value from the cache. However, when I try to add get db dependency, endpoint is being run fully and new cache is produced.

@router.get("/{username}", response_model=schemas.UserOut)
@cache(expire=60)
async def get_user(username: str, db: Annotated[AsyncSession, Depends(get_db)]):
    user = await crud.get_user_with_username(db, username)
    user.profile_image = schemas.ProfileImageOut(filename=user.profile_image)
    return user

Code for getting db.

async def get_db():
    async with async_session() as session:
        yield session

Is there any way, I can exclude AsyncSession from the cache generation? I did not found this in the documentation.

I am using fastapi-cache2==0.2.1, redis==4.6.0

fedexist commented 1 year ago

Hi @entl, I had the same issue and it should be caused by the usage of the db kwarg inside the default key builder. You should fix by creating a custom key builder that doesn't use the db session:

def no_db_session_key_builder(
    func: Callable[..., Any],
    namespace: str = "",
    *,
    request: Optional[Request] = None,
    response: Optional[Response] = None,
    args: Tuple[Any, ...],
    kwargs: Dict[str, Any],
) -> str:
    # remove db from kwargs so that it's not included in the cache key
    kwargs.pop("db")
    cache_key = hashlib.md5(f"{func.__module__}:{func.__name__}:{args}:{kwargs}".encode()).hexdigest()  # noqa: S324
    return f"{namespace}:{cache_key}"
eNTi commented 1 year ago

Interesting... I wasn't mentioned but got an email that I was...

fedexist commented 1 year ago

Interesting... I wasn't mentioned but got an email that I was...

Yes, sorry, I messed up, it was a typo (entl -> enti) with the help of github autocomplete :)

entl commented 1 year ago

Hi @entl, I had the same issue and it should be caused by the usage of the db kwarg inside the default key builder. You should fix by creating a custom key builder that doesn't use the db session:


def no_db_session_key_builder(

    func: Callable[..., Any],

    namespace: str = "",

    *,

    request: Optional[Request] = None,

    response: Optional[Response] = None,

    args: Tuple[Any, ...],

    kwargs: Dict[str, Any],

) -> str:

    # remove db from kwargs so that it's not included in the cache key

    kwargs.pop("db")

    cache_key = hashlib.md5(f"{func.__module__}:{func.__name__}:{args}:{kwargs}".encode()).hexdigest()  # noqa: S324

    return f"{namespace}:{cache_key}"

Hi, thanks for your reply. I have found another solution but forgot to close the issue. I have read documentation couple more times thoroughly and understood that cache decorator uses repr of the argument to generate md5 hash. So I added repr to database class, which returns constant value.

eNTi commented 1 year ago

Actually I think this is a bug.

entl commented 1 year ago

Hi @entl, I had the same issue and it should be caused by the usage of the db kwarg inside the default key builder. You should fix by creating a custom key builder that doesn't use the db session:


def no_db_session_key_builder(

    func: Callable[..., Any],

    namespace: str = "",

    *,

    request: Optional[Request] = None,

    response: Optional[Response] = None,

    args: Tuple[Any, ...],

    kwargs: Dict[str, Any],

) -> str:

    # remove db from kwargs so that it's not included in the cache key

    kwargs.pop("db")

    cache_key = hashlib.md5(f"{func.__module__}:{func.__name__}:{args}:{kwargs}".encode()).hexdigest()  # noqa: S324

    return f"{namespace}:{cache_key}"

I guess creating custom key builder is better approach than using repr function)

IOR88 commented 7 months ago

Hi @long2ice wasn't sure who to ping, but is this not an issue ? Is it possible to get an advice or reference to documentation about how to use fastapi-cache with Dependencies ?

parikls commented 4 months ago

This looks like a bug. IMO dependencies should be ignored. What really matters are a URL params, query params, etc.