Skip to content

Commit 75a0f9c

Browse files
committed
Improve /all-chats performance by replacing Redis SCAN with per-user chat index
1 parent 8cb0420 commit 75a0f9c

1 file changed

Lines changed: 33 additions & 12 deletions

File tree

packages/server/api/src/app/ai/chat/ai-chat.service.ts

Lines changed: 33 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,10 @@ export const updateChatName = async (
122122
await createChatContext(chatId, userId, projectId, updatedChatContext);
123123
};
124124

125+
const userChatsIndexKey = (userId: string, projectId: string): string => {
126+
return `${projectId}:${userId}:chats`;
127+
};
128+
125129
export const createChatContext = async (
126130
chatId: string,
127131
userId: string,
@@ -136,6 +140,14 @@ export const createChatContext = async (
136140
context,
137141
chatExpireTime,
138142
);
143+
const indexKey = userChatsIndexKey(userId, projectId);
144+
const existing =
145+
(await cacheWrapper.getSerializedObject<string[]>(indexKey)) ?? [];
146+
147+
if (!existing.includes(chatId)) {
148+
existing.push(chatId);
149+
await cacheWrapper.setSerializedObject(indexKey, existing, chatExpireTime);
150+
}
139151
};
140152

141153
export const getChatContext = async (
@@ -176,22 +188,27 @@ export const getAllChats = async (
176188
userId: string,
177189
projectId: string,
178190
): Promise<{ chatId: string; chatName: string }[]> => {
179-
const pattern = `${projectId}:${userId}:*:context`;
180-
const keys = await cacheWrapper.scanKeys(pattern);
181-
const chats: { chatId: string; chatName: string }[] = [];
191+
const indexKey = userChatsIndexKey(userId, projectId);
192+
const chatIds =
193+
(await cacheWrapper.getSerializedObject<string[]>(indexKey)) ?? [];
182194

183-
for (const key of keys) {
184-
const keyParts = key.split(':');
185-
if (keyParts.length !== 4) {
186-
continue;
187-
}
188-
const longChatId = keyParts[2];
195+
if (chatIds.length === 0) {
196+
return [];
197+
}
198+
199+
const contexts = await Promise.all(
200+
chatIds.map((chatId) => getChatContext(chatId, userId, projectId)),
201+
);
202+
203+
const chats: { chatId: string; chatName: string }[] = [];
189204

190-
const context = await cacheWrapper.getSerializedObject<MCPChatContext>(key);
205+
for (let i = 0; i < chatIds.length; i++) {
206+
const context = contexts[i];
207+
const chatId = chatIds[i];
191208

192209
if (context?.chatName) {
193210
chats.push({
194-
chatId: longChatId,
211+
chatId,
195212
chatName: context.chatName,
196213
});
197214
}
@@ -221,11 +238,15 @@ export const deleteChatHistory = async (
221238
userId: string,
222239
projectId: string,
223240
): Promise<void> => {
224-
await Promise.all([
241+
const indexKey = userChatsIndexKey(userId, projectId);
242+
const [chatIds] = await Promise.all([
243+
cacheWrapper.getSerializedObject<string[]>(indexKey),
225244
cacheWrapper.deleteKey(chatHistoryKey(chatId, userId, projectId)),
226245
cacheWrapper.deleteKey(chatToolsKey(chatId, userId, projectId)),
227246
cacheWrapper.deleteKey(chatContextKey(chatId, userId, projectId)),
228247
]);
248+
const updatedIds = (chatIds ?? []).filter((id) => id !== chatId);
249+
await cacheWrapper.setSerializedObject(indexKey, updatedIds);
229250
};
230251

231252
/**

0 commit comments

Comments
 (0)