Skip to content
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

Cached Requests and Responses #624

Draft
wants to merge 7 commits into
base: main
Choose a base branch
from
Draft
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
6 changes: 6 additions & 0 deletions cashu/core/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ class CoreLightningRestFundingSource(MintSettings):
mint_corelightning_rest_cert: Optional[str] = Field(default=None)


class MintCache(MintSettings):
mint_cache_activate: bool = Field(default=False)
mint_cache_ttl: int = Field(default=3600)


class Settings(
EnvSettings,
LndRPCFundingSource,
Expand All @@ -232,6 +237,7 @@ class Settings(
FakeWalletSettings,
MintLimits,
MintBackends,
MintCache,
MintSettings,
MintInformation,
WalletSettings,
Expand Down
45 changes: 25 additions & 20 deletions cashu/mint/app.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import asyncio
import sys
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from traceback import print_exception

from fastapi import FastAPI, status
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from fastapi_cache import FastAPICache
from fastapi_cache.backends.inmemory import InMemoryBackend
from loguru import logger
from starlette.requests import Request

Expand All @@ -23,27 +28,37 @@

from .middleware import add_middlewares, request_validation_exception_handler

# this errors with the tests but is the appropriate way to handle startup and shutdown
# until then, we use @app.on_event("startup")
# @asynccontextmanager
# async def lifespan(app: FastAPI):
# # startup routines here
# await start_mint_init()
# yield
# # shutdown routines here

@asynccontextmanager
async def lifespan(_: FastAPI) -> AsyncIterator[None]:
if settings.mint_cache_activate:
FastAPICache.init(InMemoryBackend(), prefix="fastapi-cache")
await start_mint_init()
try:
yield
except asyncio.CancelledError:
# Handle the cancellation gracefully
logger.info("Shutdown process interrupted by CancelledError")
finally:
try:
await shutdown_mint_init()
await FastAPICache.clear()
except asyncio.CancelledError:
logger.info("CancelledError during shutdown, shutting down forcefully")


def create_app(config_object="core.settings") -> FastAPI:
configure_logger()

app = FastAPI(
title="Nutshell Cashu Mint",
description="Ecash wallet and mint based on the Cashu protocol.",
title="Nutshell Mint",
description="Ecash mint based on the Cashu protocol.",
version=settings.version,
license_info={
"name": "MIT License",
"url": "https://raw.githubusercontent.com/cashubtc/cashu/main/LICENSE",
},
lifespan=lifespan,
)

return app
Expand Down Expand Up @@ -99,13 +114,3 @@ async def catch_exceptions(request: Request, call_next):
else:
app.include_router(router=router, tags=["Mint"])
app.include_router(router=router_deprecated, tags=["Deprecated"], deprecated=True)


@app.on_event("startup")
async def startup_mint():
await start_mint_init()


@app.on_event("shutdown")
async def shutdown_mint():
await shutdown_mint_init()
45 changes: 43 additions & 2 deletions cashu/mint/router.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import asyncio
import hashlib
import time
from typing import Any, Callable, Dict, Optional, Tuple

from fastapi import APIRouter, Request, WebSocket
from fastapi import APIRouter, WebSocket
from fastapi_cache.decorator import cache
from loguru import logger
from starlette.requests import Request
from starlette.responses import Response

from ..core.errors import KeysetNotFoundError
from ..core.models import (
Expand Down Expand Up @@ -30,7 +35,28 @@
from ..mint.startup import ledger
from .limit import limit_websocket, limiter

router: APIRouter = APIRouter()
router = APIRouter()


def request_key_builder(
func: Callable[..., Any],
namespace: str = "",
*,
request: Optional[Request] = None,
response: Optional[Response] = None,
args: Tuple[Any, ...],
kwargs: Dict[str, Any],
) -> str:
cache_key = ""
if request and request.method.lower() == "get":
cache_key = hashlib.md5( # noqa: S324
f"{func.__module__}:{func.__name__}:{repr(sorted(request.query_params.items()))}:{args}:{kwargs}".encode()
).hexdigest()
elif request and request.method.lower() == "post":
cache_key = hashlib.md5( # noqa: S324
f"{func.__module__}:{func.__name__}:{repr(request.json())}:{args}:{kwargs}".encode()
).hexdigest()
return f"{namespace}:{cache_key}"


@router.get(
Expand Down Expand Up @@ -230,6 +256,11 @@ async def websocket_endpoint(websocket: WebSocket):
),
)
@limiter.limit(f"{settings.mint_transaction_rate_limit_per_minute}/minute")
@cache(
expire=settings.mint_cache_ttl,
namespace="mint-cache",
key_builder=request_key_builder,
)
async def mint(
request: Request,
payload: PostMintRequest,
Expand Down Expand Up @@ -305,6 +336,11 @@ async def get_melt_quote(request: Request, quote: str) -> PostMeltQuoteResponse:
),
)
@limiter.limit(f"{settings.mint_transaction_rate_limit_per_minute}/minute")
@cache(
expire=settings.mint_cache_ttl,
namespace="melt-cache",
key_builder=request_key_builder,
)
async def melt(request: Request, payload: PostMeltRequest) -> PostMeltQuoteResponse:
"""
Requests tokens to be destroyed and sent out via Lightning.
Expand All @@ -327,6 +363,11 @@ async def melt(request: Request, payload: PostMeltRequest) -> PostMeltQuoteRespo
),
)
@limiter.limit(f"{settings.mint_transaction_rate_limit_per_minute}/minute")
@cache(
expire=settings.mint_cache_ttl,
namespace="swap-cache",
key_builder=request_key_builder,
)
async def swap(
request: Request,
payload: PostSwapRequest,
Expand Down
Loading
Loading