Skip to content

Add Valkey backend support #445

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,19 @@
## Introduction

`fastapi-cache` is a tool to cache FastAPI endpoint and function results, with
backends supporting Redis, Memcached, and Amazon DynamoDB.
backends supporting Redis, Valkey, Memcached, and Amazon DynamoDB.

## Features

- Supports `redis`, `memcache`, `dynamodb`, and `in-memory` backends.
- Supports `redis`, `valkey`, `memcache`, `dynamodb`, and `in-memory` backends.
- Easy integration with [FastAPI](https://fastapi.tiangolo.com/).
- Support for HTTP cache headers like `ETag` and `Cache-Control`, as well as conditional `If-Match-None` requests.

## Requirements

- FastAPI
- `redis` when using `RedisBackend`.
- `valkey` when using `ValkeyBackend`.
- `memcache` when using `MemcacheBackend`.
- `aiobotocore` when using `DynamoBackend`.

Expand Down Expand Up @@ -221,6 +222,12 @@ When using the Redis backend, please make sure you pass in a redis client that d

[redis-decode]: https://redis-py.readthedocs.io/en/latest/examples/connection_examples.html#by-default-Redis-return-binary-responses,-to-decode-them-use-decode_responses=True

### ValkeyBackend

As with the Redis backend, When using the Valkey backend, please make sure you pass in a valkey client that does [_not_ decode responses][valkey-decode] (`decode_responses` **must** be `False`, which is the default). Cached data is stored as `bytes` (binary), decoding these in the Redis client would break caching.

[valkey-decode]: https://valkey-py.readthedocs.io/en/latest/examples/connection_examples.html#By-default-Valkey-return-binary-responses,-to-decode-them-use-decode_responses=True

## Tests and coverage

```shell
Expand Down
37 changes: 37 additions & 0 deletions fastapi_cache/backends/valkey.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
fastapi-cache - Valkey cache backend support
"""
from typing import Optional, Tuple, Union

from valkey.asyncio.client import Valkey
from valkey.asyncio.cluster import ValkeyCluster

from fastapi_cache.types import Backend


class ValkeyBackend(Backend):
"""
Valkey cache backend
"""

def __init__(self, valkey: Union[Valkey, ValkeyCluster]):
self.valkey = valkey
self.is_cluster: bool = isinstance(valkey, ValkeyCluster)

async def get_with_ttl(self, key: str) -> Tuple[int, Optional[bytes]]:
async with self.valkey.pipeline(transaction=not self.is_cluster) as pipe:
return await pipe.ttl(key).get(key).execute() # type: ignore[union-attr,no-any-return]

async def get(self, key: str) -> Optional[bytes]:
return await self.valkey.get(key) # type: ignore[no-any-return]

async def set(self, key: str, value: bytes, expire: Optional[int] = None) -> None:
await self.valkey.set(key, value, ex=expire)

async def clear(self, namespace: Optional[str] = None, key: Optional[str] = None) -> int:
if namespace:
lua = f"for i, name in ipairs(redis.call('KEYS', '{namespace}:*')) do redis.call('DEL', name); end"
return await self.valkey.eval(lua, numkeys=0) # type: ignore[misc,no-any-return]
if key:
return await self.valkey.delete(key) # type: ignore[no-any-return]
return 0
Loading