11from fastapi_cache import FastAPICache
22from fastapi_cache .decorator import cache as original_cache
33from functools import partial , wraps
4- from typing import Optional , Set , Any , Dict , Tuple
4+ from typing import Optional , Set , Any , Dict , Tuple , Callable
55from inspect import Parameter , signature
66import logging
7+ from contextlib import asynccontextmanager
8+ import asyncio
79
810logger = logging .getLogger (__name__ )
911
12+ # 锁管理
13+ _cache_locks = {}
14+
15+ @asynccontextmanager
16+ async def _get_cache_lock (key : str ):
17+ lock = _cache_locks .setdefault (key , asyncio .Lock ())
18+ async with lock :
19+ try :
20+ yield
21+ finally :
22+ if key in _cache_locks and not lock .locked ():
23+ del _cache_locks [key ]
24+
1025def should_skip_param (param : Parameter ) -> bool :
1126 """判断参数是否应该被忽略(依赖注入参数)"""
1227 return (
@@ -26,9 +41,7 @@ def custom_key_builder(
2641 cacheName : Optional [str ] = None ,
2742 keyExpression : Optional [str ] = None ,
2843) -> str :
29- """
30- 完全兼容FastAPICache的键生成器
31- """
44+ """完全兼容FastAPICache的键生成器"""
3245 if cacheName :
3346 base_key = f"{ namespace } :{ cacheName } :"
3447
@@ -38,7 +51,6 @@ def custom_key_builder(
3851 bound_args = sig .bind_partial (* args , ** kwargs )
3952 bound_args .apply_defaults ()
4053
41-
4254 if keyExpression .startswith ("args[" ):
4355 import re
4456 match = re .match (r"args\[(\d+)\]" , keyExpression )
@@ -47,7 +59,6 @@ def custom_key_builder(
4759 value = bound_args .args [index ]
4860 base_key += f"{ value } :"
4961 else :
50-
5162 parts = keyExpression .split ('.' )
5263 value = bound_args .arguments [parts [0 ]]
5364 for part in parts [1 :]:
@@ -58,37 +69,27 @@ def custom_key_builder(
5869 logger .warning (f"Failed to evaluate keyExpression '{ keyExpression } ': { str (e )} " )
5970
6071 return base_key
61- # 获取函数签名
62- sig = signature (func )
6372
64- # 自动识别要跳过的参数
73+ sig = signature ( func )
6574 auto_skip_args = {
6675 name for name , param in sig .parameters .items ()
6776 if should_skip_param (param )
6877 }
69-
70- # 合并用户指定的额外跳过参数
7178 skip_args = auto_skip_args .union (additional_skip_args or set ())
72-
73- # 过滤kwargs
7479 filtered_kwargs = {
7580 k : v for k , v in kwargs .items () if k not in skip_args
7681 }
7782
78- # 过滤args - 将位置参数映射到它们的参数名
7983 bound_args = sig .bind_partial (* args , ** kwargs )
8084 bound_args .apply_defaults ()
8185
8286 filtered_args = []
8387 for i , (name , value ) in enumerate (bound_args .arguments .items ()):
84- # 只处理位置参数 (在args中的参数)
8588 if i < len (args ) and name not in skip_args :
8689 filtered_args .append (value )
8790 filtered_args = tuple (filtered_args )
8891
89- # 获取默认键生成器
9092 default_key_builder = FastAPICache .get_key_builder ()
91- # 调用默认键生成器(严格按照其要求的参数格式)
9293 return default_key_builder (
9394 func = func ,
9495 namespace = namespace ,
@@ -105,9 +106,7 @@ def cache(
105106 cacheName : Optional [str ] = None ,
106107 keyExpression : Optional [str ] = None ,
107108):
108- """
109- 完全兼容的缓存装饰器
110- """
109+ """完全兼容的缓存装饰器"""
111110 def decorator (func ):
112111 if key_builder is None :
113112 used_key_builder = partial (
@@ -121,24 +120,23 @@ def decorator(func):
121120
122121 @wraps (func )
123122 async def wrapper (* args , ** kwargs ):
124- # 准备键生成器参数
125- key_builder_args = {
126- "func" : func ,
127- "namespace" : namespace ,
128- "args" : args ,
129- "kwargs" : kwargs
130- }
131-
132- # 生成缓存键
133- cache_key = used_key_builder (** key_builder_args )
134- logger .debug (f"Generated cache key: { cache_key } " )
123+ cache_key = used_key_builder (
124+ func = func ,
125+ namespace = namespace or "" ,
126+ args = args ,
127+ kwargs = kwargs
128+ )
135129
136- # 使用原始缓存装饰器
137- return await original_cache (
138- expire = expire ,
139- namespace = namespace ,
140- key_builder = lambda * _ , ** __ : cache_key # 直接使用预生成的key
141- )(func )(* args , ** kwargs )
130+ async with _get_cache_lock (cache_key ):
131+ backend = FastAPICache .get_backend ()
132+ cached_value = await backend .get (cache_key )
133+ if cached_value is not None :
134+ return cached_value
135+
136+ result = await func (* args , ** kwargs )
137+ await backend .set (cache_key , result , expire )
138+ return result
139+
142140 return wrapper
143141 return decorator
144142
@@ -147,17 +145,10 @@ def clear_cache(
147145 cacheName : Optional [str ] = None ,
148146 keyExpression : Optional [str ] = None ,
149147):
150- """
151- 清除缓存的装饰器,参数与 @cache 保持一致
152- 使用方式:
153- @clear_cache(namespace="user", cacheName="info", keyExpression="user_id")
154- async def update_user(user_id: int):
155- ...
156- """
148+ """清除缓存的装饰器"""
157149 def decorator (func ):
158150 @wraps (func )
159151 async def wrapper (* args , ** kwargs ):
160- # 1. 生成缓存键(复用 custom_key_builder 逻辑)
161152 cache_key = custom_key_builder (
162153 func = func ,
163154 namespace = namespace or "" ,
@@ -167,13 +158,10 @@ async def wrapper(*args, **kwargs):
167158 keyExpression = keyExpression ,
168159 )
169160
170- logger .debug (f"Clearing cache for key: { cache_key } " )
171- # 2. 清除缓存
172- if await FastAPICache .get_backend ().get (cache_key ):
161+ async with _get_cache_lock (cache_key ):
173162 await FastAPICache .clear (key = cache_key )
174-
175- # 3. 执行原函数
176- return await func (* args , ** kwargs )
163+ result = await func (* args , ** kwargs )
164+ return result
177165
178166 return wrapper
179167 return decorator
0 commit comments