mirror of
https://github.com/labring/FastGPT.git
synced 2025-12-26 04:32:50 +00:00
perf: replace redis KEYS with SCAN (#6101)
* perf: replace redis KEYS with SCAN * test: add redis scan mock to fix unit tests * Fix formatting in redis.ts mock functions * fix comment word Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
09b9fa517b
commit
342aca0b21
|
|
@ -12,21 +12,56 @@ const cachePrefix = `VERSION_KEY:`;
|
|||
* @param key SystemCacheKeyEnum
|
||||
* @param id string (teamId, tmbId, etc), if '*' is used, all keys will be refreshed
|
||||
*/
|
||||
export const refreshVersionKey = async (key: `${SystemCacheKeyEnum}`, id?: string | '*') => {
|
||||
export const refreshVersionKey = async (
|
||||
key: `${SystemCacheKeyEnum}`,
|
||||
id?: string | '*'
|
||||
) => {
|
||||
const redis = getGlobalRedisConnection();
|
||||
if (!global.systemCache) initCache();
|
||||
|
||||
const val = randomUUID();
|
||||
const versionKey = id ? `${cachePrefix}${key}:${id}` : `${cachePrefix}${key}`;
|
||||
|
||||
if (id === '*') {
|
||||
const pattern = `${cachePrefix}${key}:*`;
|
||||
const keys = await redis.keys(pattern);
|
||||
if (keys.length > 0) {
|
||||
await redis.del(keys);
|
||||
}
|
||||
} else {
|
||||
await redis.set(versionKey, val);
|
||||
|
||||
let cursor = '0';
|
||||
const batchSize = 1000; // SCAN 每次取多少
|
||||
const delChunk = 500; // 每次 pipeline 删除多少(可按需调)
|
||||
|
||||
let buffer: string[] = [];
|
||||
|
||||
const flush = async () => {
|
||||
if (buffer.length === 0) return;
|
||||
const pipeline = redis.pipeline();
|
||||
for (const k of buffer) pipeline.del(k);
|
||||
await pipeline.exec();
|
||||
buffer = [];
|
||||
};
|
||||
|
||||
do {
|
||||
const [nextCursor, keys] = await redis.scan(
|
||||
cursor,
|
||||
'MATCH',
|
||||
pattern,
|
||||
'COUNT',
|
||||
batchSize
|
||||
);
|
||||
cursor = nextCursor;
|
||||
|
||||
for (const k of keys) {
|
||||
buffer.push(k);
|
||||
if (buffer.length >= delChunk) {
|
||||
await flush();
|
||||
}
|
||||
}
|
||||
} while (cursor !== '0');
|
||||
|
||||
await flush();
|
||||
return;
|
||||
}
|
||||
|
||||
await redis.set(versionKey, val);
|
||||
};
|
||||
|
||||
export const getVersionKey = async (key: `${SystemCacheKeyEnum}`, id?: string) => {
|
||||
|
|
|
|||
|
|
@ -45,8 +45,27 @@ export const getGlobalRedisConnection = () => {
|
|||
|
||||
export const getAllKeysByPrefix = async (key: string) => {
|
||||
const redis = getGlobalRedisConnection();
|
||||
const keys = (await redis.keys(`${FASTGPT_REDIS_PREFIX}${key}:*`)).map((key) =>
|
||||
key.replace(FASTGPT_REDIS_PREFIX, '')
|
||||
);
|
||||
return keys;
|
||||
};
|
||||
const prefix = FASTGPT_REDIS_PREFIX;
|
||||
const pattern = `${prefix}${key}:*`;
|
||||
|
||||
let cursor = '0';
|
||||
const batchSize = 1000; // SCAN 每次取多少
|
||||
const results: string[] = [];
|
||||
|
||||
do {
|
||||
const [nextCursor, keys] = await redis.scan(
|
||||
cursor,
|
||||
'MATCH',
|
||||
pattern,
|
||||
'COUNT',
|
||||
batchSize
|
||||
);
|
||||
cursor = nextCursor;
|
||||
|
||||
for (const k of keys) {
|
||||
results.push(k.startsWith(prefix) ? k.slice(prefix.length) : k);
|
||||
}
|
||||
} while (cursor !== '0');
|
||||
|
||||
return results;
|
||||
};
|
||||
|
|
@ -17,6 +17,7 @@ const createMockRedisClient = () => ({
|
|||
del: vi.fn().mockResolvedValue(1),
|
||||
exists: vi.fn().mockResolvedValue(0),
|
||||
keys: vi.fn().mockResolvedValue([]),
|
||||
scan: vi.fn().mockResolvedValue(['0', []]),
|
||||
|
||||
// Hash operations
|
||||
hget: vi.fn().mockResolvedValue(null),
|
||||
|
|
@ -53,7 +54,14 @@ const createMockRedisClient = () => ({
|
|||
sadd: vi.fn().mockResolvedValue(1),
|
||||
srem: vi.fn().mockResolvedValue(1),
|
||||
smembers: vi.fn().mockResolvedValue([]),
|
||||
sismember: vi.fn().mockResolvedValue(0)
|
||||
sismember: vi.fn().mockResolvedValue(0),
|
||||
|
||||
// pipeline
|
||||
pipeline: vi.fn(() => ({
|
||||
del: vi.fn().mockReturnThis(),
|
||||
unlink: vi.fn().mockReturnThis(),
|
||||
exec: vi.fn().mockResolvedValue([])
|
||||
}))
|
||||
});
|
||||
|
||||
// Mock Redis connections to prevent connection errors in tests
|
||||
|
|
|
|||
Loading…
Reference in New Issue