Skip to content

Commit ed3b8da

Browse files
committed
Merge branch 'main' of https://github.com/dataease/SQLBot
2 parents 4b631e9 + 82b01d2 commit ed3b8da

File tree

2 files changed

+19
-74
lines changed

2 files changed

+19
-74
lines changed

backend/common/core/sqlbot_cache.py

Lines changed: 18 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,12 @@
1-
import contextvars
21
from fastapi_cache import FastAPICache
32
from functools import partial, wraps
43
from typing import Optional, Any, Dict, Tuple
54
from inspect import signature
6-
from contextlib import asynccontextmanager
7-
import asyncio
8-
import random
9-
from collections import defaultdict
105
from common.core.config import settings
116
from common.utils.utils import SQLBotLogUtil
127
from 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

4911
def 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

frontend/src/views/dashboard/common/ResourceTree.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ defineExpose({
398398
<el-scrollbar v-loading="copyLoading" class="custom-tree">
399399
<el-tree
400400
ref="resourceListTree"
401+
style="overflow-x: hidden"
401402
menu
402403
:empty-text="t('dashboard.no_dashboard')"
403404
:default-expanded-keys="expandedArray"

0 commit comments

Comments
 (0)