-
Notifications
You must be signed in to change notification settings - Fork 0
Add Redis storage #9
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
Merged
Merged
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
82f6f33
refactor storages
Nottezz 2bce822
add redis storage
Nottezz ae69834
add redis example
Nottezz 7254961
add methods for dumps json
Nottezz 991c2d4
move storage fixtures to conftest
Nottezz 3e4126f
add tests for serializers.py
Nottezz 822adc1
get items from dict
Nottezz 7b57ddf
import Redis storage
Nottezz dc1aed3
add redis storage tests
Nottezz 7fcc18c
fix linters
Nottezz e39538f
mypy fix
Nottezz b8788b8
add redis as optional dep
Nottezz 281fac3
Merge branch 'main' into feature/redis-storage
Nottezz c1746d4
resolve conflicts
Nottezz cbfea2b
edit example
Nottezz 4584904
method dumps at jsonserializer has become async
Nottezz 3cda130
RedisStorage for imports
Nottezz e98f898
edit unit-tests
Nottezz 02c57ed
edit redis import
Nottezz 9be6d8e
edit redis import x2
Nottezz f409ce1
ci mypy fix
Nottezz 166a1e5
remove ignore imports
Nottezz 0243ad7
async type for Redis
Nottezz 7f5d674
add command for install with redis
Nottezz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| """An example of using Fast Cache Middleware with rout resolution and Redis storage. | ||
|
|
||
| to install using Redis, run this command: pip install fast-cache-middleware[redis] | ||
|
|
||
| Demonstrates: | ||
| 1. Analysis of routes at the start of the application; | ||
| 2. Extracting configuration cache from dependencies; | ||
| 3. Automatic caching of GET requests in Redis; | ||
| 4. Cache invalidation in case of modifying requests. | ||
| """ | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. можно дописать как ставить с екстрой, чтобы редис поставился |
||
|
|
||
| import logging | ||
| import time | ||
| import typing as tp | ||
|
|
||
| import uvicorn | ||
| from fastapi import FastAPI, HTTPException, Request | ||
| from pydantic import BaseModel, Field | ||
| from redis.asyncio import Redis # async only | ||
|
|
||
| from fast_cache_middleware import ( | ||
| CacheConfig, | ||
| CacheDropConfig, | ||
| FastCacheMiddleware, | ||
| RedisStorage, | ||
| ) | ||
|
|
||
| # Creating a Flash API application | ||
| app = FastAPI(title="FastCacheMiddleware Redis Example") | ||
| # Initializing Redis | ||
| redis = Redis(host="127.0.0.1", port=6379, db=0, decode_responses=True) | ||
|
|
||
| # Adding middleware - it will analyze the routes at the first request. | ||
| app.add_middleware(FastCacheMiddleware, storage=RedisStorage(redis_client=redis)) | ||
|
|
||
|
|
||
| def custom_key_func(request: Request) -> str: | ||
| user_id = request.headers.get("user-id", "anonymous") | ||
| return f"{request.url.path}:user:{user_id}" | ||
|
|
||
|
|
||
| class User(BaseModel): | ||
| name: str | ||
| email: str | ||
|
|
||
|
|
||
| class FullUser(User): | ||
| user_id: int | ||
|
|
||
|
|
||
| class UserResponse(FullUser): | ||
| timestamp: float = Field(default_factory=time.time) | ||
|
|
||
|
|
||
| _USERS_STORAGE: tp.Dict[int, User] = { | ||
| 1: User(name="John Doe", email="john.doe@example.com"), | ||
| 2: User(name="Jane Doe", email="jane.doe@example.com"), | ||
| } | ||
|
|
||
|
|
||
| # Routers with different caching configurations | ||
|
|
||
|
|
||
| @app.get( | ||
| "/users/{user_id}", | ||
| dependencies=[CacheConfig(max_age=120, key_func=custom_key_func)], | ||
| ) | ||
| async def get_user(user_id: int) -> UserResponse: | ||
| """Getting a user with a custom caching key. | ||
|
|
||
| The cache key includes the user-id from the headers for personalization. | ||
| """ | ||
| user = _USERS_STORAGE.get(user_id) | ||
| if not user: | ||
| raise HTTPException(status_code=404, detail="User not found") | ||
|
|
||
| return UserResponse(user_id=user_id, name=user.name, email=user.email) | ||
|
|
||
|
|
||
| @app.get("/users", dependencies=[CacheConfig(max_age=120)]) | ||
| async def get_users() -> tp.List[UserResponse]: | ||
| return [ | ||
| UserResponse(user_id=user_id, name=user.name, email=user.email) | ||
| for user_id, user in _USERS_STORAGE.items() | ||
| ] | ||
|
|
||
|
|
||
| @app.post("/users/{user_id}", dependencies=[CacheDropConfig(paths=["/users"])]) | ||
| async def create_user(user_id: int, user_data: User) -> UserResponse: | ||
| """Creating a user with a cache disability. | ||
|
|
||
| This POST request disables the cache for all /users/* paths. | ||
| """ | ||
| _USERS_STORAGE[user_id] = user_data | ||
|
|
||
| return UserResponse(user_id=user_id, name=user_data.name, email=user_data.email) | ||
|
|
||
|
|
||
| @app.delete("/users/{user_id}", dependencies=[CacheDropConfig(paths=["/users"])]) | ||
| async def delete_user(user_id: int) -> UserResponse: | ||
| """Deleting a user with a cache disability. | ||
|
|
||
| This DELETE request disables the cache for all /users/* paths. | ||
| """ | ||
| user = _USERS_STORAGE.get(user_id) | ||
| if not user: | ||
| raise HTTPException(status_code=404, detail="User not found") | ||
| del _USERS_STORAGE[user_id] | ||
|
|
||
| return UserResponse(user_id=user_id, name=user.name, email=user.email) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| logging.basicConfig( | ||
| level=logging.DEBUG, | ||
| format="[-] %(asctime)s [%(levelname)s] %(module)s-%(lineno)d - %(message)s", | ||
| ) | ||
|
|
||
| print("🚀 Running Fast Cache Middleware Redis Example...") | ||
| print("\n📋 Available endpoints:") | ||
| print(" GET /users/{user_id} - getting the user (2 min cache)") | ||
| print(" GET /users - list of users (2 min cache)") | ||
| print(" POST /users/{user_id} - user creation (disability /users)") | ||
| print(" DELETE /users/{user_id} - deleting a user (invalidation /users)") | ||
|
|
||
| print("\n💡 For testing purposes:") | ||
| print(" curl http://localhost:8000/users/1") | ||
| print(" curl http://localhost:8000/users") | ||
| print( | ||
| ' curl -X POST http://localhost:8000/users/1 -H "Content-Type: application/json" -d \'{"name": "John", "email": "john@example.com"}\'' | ||
| ) | ||
| print(" curl -X DELETE http://localhost:8000/users/1") | ||
| print() | ||
|
|
||
| uvicorn.run(app, host="127.0.0.1", port=8000) | ||
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| from .base_storage import BaseStorage | ||
| from .in_memory_storage import InMemoryStorage | ||
| from .redis_storage import RedisStorage |
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| import re | ||
| from typing import Optional, Tuple, TypeAlias, Union | ||
|
|
||
| from starlette.requests import Request | ||
| from starlette.responses import Response | ||
|
|
||
| from fast_cache_middleware.exceptions import StorageError | ||
| from fast_cache_middleware.serializers import BaseSerializer, JSONSerializer, Metadata | ||
|
|
||
| StoredResponse: TypeAlias = Tuple[Response, Request, Metadata] | ||
|
|
||
|
|
||
| class BaseStorage: | ||
| """Base class for cache storage. | ||
|
|
||
| Args: | ||
| serializer: Serializer for converting Response/Request to string/bytes | ||
| ttl: Cache lifetime in seconds. None for permanent storage | ||
| """ | ||
|
|
||
| def __init__( | ||
| self, | ||
| serializer: Optional[BaseSerializer] = None, | ||
| ttl: Optional[Union[int, float]] = None, | ||
| ) -> None: | ||
| self._serializer = serializer or JSONSerializer() | ||
|
|
||
| if ttl is not None and ttl <= 0: | ||
| raise StorageError("TTL must be positive") | ||
|
|
||
| self._ttl = ttl | ||
|
|
||
| async def store( | ||
| self, key: str, response: Response, request: Request, metadata: Metadata | ||
| ) -> None: | ||
| raise NotImplementedError() | ||
|
|
||
| async def retrieve(self, key: str) -> Optional[StoredResponse]: | ||
Nottezz marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| raise NotImplementedError() | ||
|
|
||
| async def remove(self, path: re.Pattern) -> None: | ||
| raise NotImplementedError() | ||
|
|
||
| async def close(self) -> None: | ||
| raise NotImplementedError() | ||
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
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.