1- import contextvars
21from fastapi_cache import FastAPICache
32from functools import partial , wraps
43from typing import Optional , Any , Dict , Tuple
54from inspect import signature
6- from contextlib import asynccontextmanager
7- import asyncio
8- import random
9- from collections import defaultdict
105from common .core .config import settings
116from common .utils .utils import SQLBotLogUtil
127from fastapi_cache .backends .inmemory import InMemoryBackend
13- # 使用contextvar来跟踪当前线程已持有的锁
14- _held_locks = contextvars .ContextVar ('held_locks' , default = set ())
15- # 高效锁管理器
16- class LockManager :
17- _locks = defaultdict (asyncio .Lock )
18-
19- @classmethod
20- def get_lock (cls , key : str ) -> asyncio .Lock :
21- return cls ._locks [key ]
228
23- @asynccontextmanager
24- async def _get_cache_lock (key : str ):
25- # 获取当前已持有的锁集合
26- current_locks = _held_locks .get ()
27-
28- # 如果已经持有这个锁,直接yield(锁传递)
29- if key in current_locks :
30- yield
31- return
32-
33- # 否则获取锁并添加到当前上下文中
34- lock = LockManager .get_lock (key )
35- try :
36- await lock .acquire ()
37- # 更新当前持有的锁集合
38- new_locks = current_locks | {key }
39- token = _held_locks .set (new_locks )
40-
41- yield
42-
43- finally :
44- # 恢复之前的锁集合
45- _held_locks .reset (token )
46- if lock .locked ():
47- lock .release ()
9+ from fastapi_cache .decorator import cache as original_cache
4810
4911def custom_key_builder (
5012 func : Any ,
@@ -91,7 +53,6 @@ def cache(
9153 * ,
9254 cacheName : str , # 必须提供cacheName
9355 keyExpression : Optional [str ] = None ,
94- jitter : int = 60 , # 默认抖动60秒
9556):
9657 def decorator (func ):
9758 # 预先生成key builder
@@ -108,30 +69,17 @@ async def wrapper(*args, **kwargs):
10869 # 生成缓存键
10970 cache_key = used_key_builder (
11071 func = func ,
111- namespace = namespace ,
72+ namespace = str ( namespace ) if namespace else "" ,
11273 args = args ,
11374 kwargs = kwargs
11475 )
11576
116- # 防击穿锁
117- async with _get_cache_lock (cache_key ):
118- backend = FastAPICache .get_backend ()
119- # 双重检查
120- if (cached := await backend .get (cache_key )) is not None :
121- SQLBotLogUtil .debug (f"Cache hit: { cache_key } " )
122- return cached
123-
124- # 执行函数并缓存结果
125- result = await func (* args , ** kwargs )
126-
127- actual_expire = expire + random .randint (- jitter , jitter )
128- if await backend .get (cache_key ):
129- await backend .clear (cache_key )
130- await backend .set (cache_key , result , actual_expire )
131-
132- SQLBotLogUtil .debug (f"Cache set: { cache_key } (expire: { actual_expire } s)" )
133- return result
134-
77+ return await original_cache (
78+ expire = expire ,
79+ namespace = str (namespace ) if namespace else "" ,
80+ key_builder = lambda * _ , ** __ : cache_key
81+ )(func )(* args , ** kwargs )
82+
13583 return wrapper
13684 return decorator
13785
@@ -141,32 +89,28 @@ def clear_cache(
14189 cacheName : str ,
14290 keyExpression : Optional [str ] = None ,
14391):
144- """精确清除单个缓存项的装饰器"""
14592 def decorator (func ):
14693 @wraps (func )
14794 async def wrapper (* args , ** kwargs ):
14895 if not settings .CACHE_TYPE or settings .CACHE_TYPE .lower () == "none" :
14996 return await func (* args , ** kwargs )
15097 cache_key = custom_key_builder (
15198 func = func ,
152- namespace = namespace ,
99+ namespace = str ( namespace ) if namespace else "" ,
153100 args = args ,
154101 kwargs = kwargs ,
155102 cacheName = cacheName ,
156103 keyExpression = keyExpression ,
157104 )
158-
159- # 加锁防止竞争
160- async with _get_cache_lock (cache_key ):
161- backend = FastAPICache .get_backend ()
162- result = None
163- if await backend .get (cache_key ):
164- await backend .clear (cache_key )
165- result = await func (* args , ** kwargs )
166- if await backend .get (cache_key ):
167- await backend .clear (cache_key )
168- SQLBotLogUtil .debug (f"Cache cleared: { cache_key } " )
169- return result
105+ backend = FastAPICache .get_backend ()
106+ if await backend .get (cache_key ):
107+ if settings .CACHE_TYPE .lower () == "redis" :
108+ redis = backend .redis
109+ await redis .delete (cache_key )
110+ else :
111+ await backend .clear (key = cache_key )
112+ SQLBotLogUtil .debug (f"Cache cleared: { cache_key } " )
113+ return await func (* args , ** kwargs )
170114
171115 return wrapper
172116 return decorator
0 commit comments