detached 65825d7

refactor: Optimize message cache for public view and separate authenticated user message handling

- Remove authenticated user message cache method
- Update message retrieval logic to use cache only for public users
- Modify API routes to skip cache for authenticated users
- Enhance logging for public message cache initialization and retrieval
- Prepare for direct database fetching and SSE updates for authenticated users
commit
65825d7e54ade155e47f27a5f0796b9a96df7910
author
Kit · · 2025-02-15 12:47 UTC
parent
21b8492
signature
SSH UnverifiedSHA256:H91DFuEG4jpk5edzLA4aUKkpv4zv9Q339ipRmkph6Tk The key is unknown
4 files · +22 -80
M src/lib/cache/initialize-cache.ts +2 -2
@@ -14,7 +14,7 @@ export async function initializeMessageCache(): Promise<void> {
1414 return;
1515 }
1616 try {
17- log.debug('[Cache] Initializing message cache with pre-population.');
17+ log.debug('[Cache] Initializing message cache with pre-population for public view.');
1818 const fetchedMessages = await db.select()
1919 .from(messages)
2020 .orderBy(desc(messages.timestamp))
@@ -23,7 +23,7 @@ export async function initializeMessageCache(): Promise<void> {
2323 await messageCache.initialize(fetchedMessages.reverse());
2424 isCacheInitialized = true;
2525 const stats = messageCache.getCacheStats();
26- log.debug('[Cache] Message cache initialized successfully:', {
26+ log.debug('[Cache] Message cache initialized successfully for public view:', {
2727 roomCount: stats.roomCount,
2828 totalMessages: stats.totalMessages
2929 });
@@ -14,7 +14,7 @@ export async function initializeMessageCache(): Promise<void> {
14 return;14 return;
15 }15 }
16 try {16 try {
17- log.debug('[Cache] Initializing message cache with pre-population.');17+ log.debug('[Cache] Initializing message cache with pre-population for public view.');
18 const fetchedMessages = await db.select()18 const fetchedMessages = await db.select()
19 .from(messages)19 .from(messages)
20 .orderBy(desc(messages.timestamp))20 .orderBy(desc(messages.timestamp))
@@ -23,7 +23,7 @@ export async function initializeMessageCache(): Promise<void> {
23 await messageCache.initialize(fetchedMessages.reverse());23 await messageCache.initialize(fetchedMessages.reverse());
24 isCacheInitialized = true;24 isCacheInitialized = true;
25 const stats = messageCache.getCacheStats();25 const stats = messageCache.getCacheStats();
26- log.debug('[Cache] Message cache initialized successfully:', {26+ log.debug('[Cache] Message cache initialized successfully for public view:', {
27 roomCount: stats.roomCount,27 roomCount: stats.roomCount,
28 totalMessages: stats.totalMessages28 totalMessages: stats.totalMessages
29 });29 });
M src/lib/cache/message-cache.ts +0 -17
@@ -101,23 +101,6 @@ class MessageCache {
101101 });
102102 }
103103
104- /**
105- * Get messages for authenticated users
106- */
107- public getMessages(roomId: string = DEFAULT_CHAT_ROOM_ID, limit: number = this.maxMessagesPerRoom): Message[] {
108- this.cleanExpiredRooms();
109- const cache = this.rooms.get(roomId);
110-
111- if (!cache) {
112- log.debug('[MessageCache] No messages found for room:', { roomId });
113- return [];
114- }
115-
116- cache.lastUpdated = Date.now();
117- const messages = [...cache.messages];
118- return limit ? messages.slice(-limit) : messages;
119- }
120-
121104 /**
122105 * Get messages for public/non-authenticated users with strict limit
123106 */
@@ -101,23 +101,6 @@ class MessageCache {
101 });101 });
102 }102 }
103 103
104- /**
105- * Get messages for authenticated users
106- */
107- public getMessages(roomId: string = DEFAULT_CHAT_ROOM_ID, limit: number = this.maxMessagesPerRoom): Message[] {
108- this.cleanExpiredRooms();
109- const cache = this.rooms.get(roomId);
110-
111- if (!cache) {
112- log.debug('[MessageCache] No messages found for room:', { roomId });
113- return [];
114- }
115-
116- cache.lastUpdated = Date.now();
117- const messages = [...cache.messages];
118- return limit ? messages.slice(-limit) : messages;
119- }
120-
121 /**104 /**
122 * Get messages for public/non-authenticated users with strict limit105 * Get messages for public/non-authenticated users with strict limit
123 */106 */
M src/lib/states/chat.svelte.ts +1 -0
@@ -398,6 +398,7 @@ class ChatState {
398398 this.messages = uniqueMessages;
399399 }
400400
401+ // Note: For authenticated users, we no longer use the message cache. Messages are fetched directly from the database, and real-time updates are handled via SSE.
401402 async initializeMessages() {
402403 try {
403404 const response = await fetch('/api/chat/messages', {
@@ -398,6 +398,7 @@ class ChatState {
398 this.messages = uniqueMessages;398 this.messages = uniqueMessages;
399 }399 }
400 400
401+ // Note: For authenticated users, we no longer use the message cache. Messages are fetched directly from the database, and real-time updates are handled via SSE.
401 async initializeMessages() {402 async initializeMessages() {
402 try {403 try {
403 const response = await fetch('/api/chat/messages', {404 const response = await fetch('/api/chat/messages', {
M src/routes/api/chat/messages/+server.ts +19 -61
@@ -87,41 +87,22 @@ export async function GET({ request, locals }) {
8787 let fetchedMessages: Message[] = [];
8888
8989 try {
90- // Initialize cache if needed
91- if (!isCacheInitialized) {
92- await initializeCache();
93- }
94-
95- // Try to get messages from cache first
96- if (!beforeTimestamp) {
97- log.debug('Attempting to fetch messages from cache', {
98- roomId,
99- isAuthenticated: !!locals.session
100- });
101-
102- // Use appropriate method based on authentication status
103- const cachedMessages = locals.session
104- ? messageCache.getMessages(roomId)
105- : messageCache.getPublicMessages(roomId);
106-
90+ // For public users (non-authenticated), attempt to fetch messages from cache if no pagination is requested
91+ if (!locals.session && !beforeTimestamp) {
92+ if (!isCacheInitialized) {
93+ await initializeCache();
94+ }
95+ log.debug('Attempting to fetch messages from cache for public view', { roomId });
96+ const cachedMessages = messageCache.getPublicMessages(roomId);
10797 if (cachedMessages && cachedMessages.length > 0) {
108- log.debug('Messages found in cache', {
109- count: cachedMessages.length,
110- roomId,
111- isAuthenticated: !!locals.session
112- });
98+ log.debug('Messages found in cache for public view', { count: cachedMessages.length, roomId });
11399 fetchedMessages = cachedMessages;
114100 }
115101 }
116102
117103 // If no cached messages or pagination requested, fetch from DB
118104 if (fetchedMessages.length === 0 || beforeTimestamp) {
119- log.debug('Fetching messages from database', {
120- beforeTimestamp,
121- roomId,
122- isAuthenticated: !!locals.session
123- });
124-
105+ log.debug('Fetching messages from database', { beforeTimestamp, roomId, isAuthenticated: !!locals.session });
125106 let conditions = eq(messages.chatRoomId, roomId) as SQL<unknown>;
126107
127108 if (beforeTimestamp) {
@@ -139,8 +120,8 @@ export async function GET({ request, locals }) {
139120 .orderBy(desc(messages.timestamp))
140121 .limit(fetchLimit);
141122
142- // Update cache with new messages if not paginating
143- if (!beforeTimestamp && fetchedMessages.length > 0) {
123+ // For public users, update cache with new messages if not paginating
124+ if (!locals.session && !beforeTimestamp && fetchedMessages.length > 0) {
144125 for (const msg of fetchedMessages) {
145126 messageCache.addMessage(msg);
146127 }
@@ -176,19 +157,16 @@ export async function POST({ request, locals }: { request: Request, locals: App.
176157 const { canSend, retryAfter } = updateUserCooldown(locals.user.id);
177158 if (!canSend) {
178159 const maskedUserId = `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}`;
179- log.warn('Rate limited', {
180- userId: maskedUserId,
181- retryAfter
182- });
160+ log.warn('Rate limited', { userId: maskedUserId, retryAfter });
183161 const errorResponse: SendMessageResponse = {
184162 success: false,
185163 error: 'Please wait before sending another message',
186164 retryAfter,
187165 isRateLimited: true
188166 };
189- return new Response(JSON.stringify(errorResponse), {
167+ return new Response(JSON.stringify(errorResponse), {
190168 status: 429,
191- headers: {
169+ headers: {
192170 'Content-Type': 'application/json',
193171 'Retry-After': Math.ceil(retryAfter! / 1000).toString()
194172 }
@@ -197,10 +175,7 @@ export async function POST({ request, locals }: { request: Request, locals: App.
197175
198176 const data = await request.json() as SendMessageRequest;
199177 if (!data.content || !data.userId) {
200- log.warn('Invalid payload received', {
201- hasContent: Boolean(data.content),
202- hasUserId: Boolean(data.userId)
203- });
178+ log.warn('Invalid payload received', { hasContent: Boolean(data.content), hasUserId: Boolean(data.userId) });
204179 const errorResponse: SendMessageResponse = {
205180 success: false,
206181 error: 'Invalid message payload'
@@ -216,7 +191,6 @@ export async function POST({ request, locals }: { request: Request, locals: App.
216191 throw error(403, 'Cannot post messages as another user');
217192 }
218193
219- // Validate that both the chat room and user exist
220194 const chatRoomId = data.chatRoomId || DEFAULT_CHAT_ROOM_ID;
221195
222196 // Check chat room existence
@@ -261,28 +235,14 @@ export async function POST({ request, locals }: { request: Request, locals: App.
261235
262236 // Save to DB
263237 await db.insert(messages).values(newMessage);
264- log.debug('Message saved in DB', {
265- messageId: newMessage.id,
266- chatRoomId: newMessage.chatRoomId,
267- type: newMessage.type,
268- timestamp: newMessage.timestamp
269- });
238+ log.debug('Message saved in DB', { messageId: newMessage.id, chatRoomId: newMessage.chatRoomId, type: newMessage.type, timestamp: newMessage.timestamp });
270239
271- // Update message cache
272- messageCache.addMessage(newMessage);
273- log.debug('Message added to cache', {
274- messageId: newMessage.id,
275- chatRoomId: newMessage.chatRoomId
276- });
240+ // Removed updating message cache for authenticated users; relying on SSE for real-time updates
277241
278242 // Broadcast the new message via the unified SSE emitter
279243 sseEmitter.emit('sse', { type: 'chatMessage', data: newMessage });
280244
281- log.debug('Message processed successfully', {
282- messageId: newMessage.id,
283- userId: `${newMessage.senderId.slice(0, 4)}...${newMessage.senderId.slice(-4)}`,
284- roomId: newMessage.chatRoomId
285- });
245+ log.debug('Message processed successfully', { messageId: newMessage.id, userId: `${newMessage.senderId.slice(0, 4)}...${newMessage.senderId.slice(-4)}`, roomId: newMessage.chatRoomId });
286246
287247 const successResponse: SendMessageResponse = {
288248 success: true,
@@ -297,9 +257,7 @@ export async function POST({ request, locals }: { request: Request, locals: App.
297257 }
298258 );
299259 } catch (error) {
300- log.error('Error processing message', {
301- error: error instanceof Error ? error.message : 'Unknown error'
302- });
260+ log.error('Error processing message', { error: error instanceof Error ? error.message : 'Unknown error' });
303261 const errorResponse: SendMessageResponse = {
304262 success: false,
305263 error: 'Failed to save message'
@@ -87,41 +87,22 @@ export async function GET({ request, locals }) {
87 let fetchedMessages: Message[] = [];87 let fetchedMessages: Message[] = [];
88 88
89 try {89 try {
90- // Initialize cache if needed90+ // For public users (non-authenticated), attempt to fetch messages from cache if no pagination is requested
91- if (!isCacheInitialized) {91+ if (!locals.session && !beforeTimestamp) {
92- await initializeCache();92+ if (!isCacheInitialized) {
93- }93+ await initializeCache();
94-94+ }
95- // Try to get messages from cache first95+ log.debug('Attempting to fetch messages from cache for public view', { roomId });
96- if (!beforeTimestamp) {96+ const cachedMessages = messageCache.getPublicMessages(roomId);
97- log.debug('Attempting to fetch messages from cache', {
98- roomId,
99- isAuthenticated: !!locals.session
100- });
101-
102- // Use appropriate method based on authentication status
103- const cachedMessages = locals.session
104- ? messageCache.getMessages(roomId)
105- : messageCache.getPublicMessages(roomId);
106-
107 if (cachedMessages && cachedMessages.length > 0) {97 if (cachedMessages && cachedMessages.length > 0) {
108- log.debug('Messages found in cache', { 98+ log.debug('Messages found in cache for public view', { count: cachedMessages.length, roomId });
109- count: cachedMessages.length,
110- roomId,
111- isAuthenticated: !!locals.session
112- });
113 fetchedMessages = cachedMessages;99 fetchedMessages = cachedMessages;
114 }100 }
115 }101 }
116 102
117 // If no cached messages or pagination requested, fetch from DB103 // If no cached messages or pagination requested, fetch from DB
118 if (fetchedMessages.length === 0 || beforeTimestamp) {104 if (fetchedMessages.length === 0 || beforeTimestamp) {
119- log.debug('Fetching messages from database', { 105+ log.debug('Fetching messages from database', { beforeTimestamp, roomId, isAuthenticated: !!locals.session });
120- beforeTimestamp,
121- roomId,
122- isAuthenticated: !!locals.session
123- });
124-
125 let conditions = eq(messages.chatRoomId, roomId) as SQL<unknown>;106 let conditions = eq(messages.chatRoomId, roomId) as SQL<unknown>;
126 107
127 if (beforeTimestamp) {108 if (beforeTimestamp) {
@@ -139,8 +120,8 @@ export async function GET({ request, locals }) {
139 .orderBy(desc(messages.timestamp))120 .orderBy(desc(messages.timestamp))
140 .limit(fetchLimit);121 .limit(fetchLimit);
141 122
142- // Update cache with new messages if not paginating123+ // For public users, update cache with new messages if not paginating
143- if (!beforeTimestamp && fetchedMessages.length > 0) {124+ if (!locals.session && !beforeTimestamp && fetchedMessages.length > 0) {
144 for (const msg of fetchedMessages) {125 for (const msg of fetchedMessages) {
145 messageCache.addMessage(msg);126 messageCache.addMessage(msg);
146 }127 }
@@ -176,19 +157,16 @@ export async function POST({ request, locals }: { request: Request, locals: App.
176 const { canSend, retryAfter } = updateUserCooldown(locals.user.id);157 const { canSend, retryAfter } = updateUserCooldown(locals.user.id);
177 if (!canSend) {158 if (!canSend) {
178 const maskedUserId = `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}`;159 const maskedUserId = `${locals.user.id.slice(0, 4)}...${locals.user.id.slice(-4)}`;
179- log.warn('Rate limited', { 160+ log.warn('Rate limited', { userId: maskedUserId, retryAfter });
180- userId: maskedUserId,
181- retryAfter
182- });
183 const errorResponse: SendMessageResponse = {161 const errorResponse: SendMessageResponse = {
184 success: false,162 success: false,
185 error: 'Please wait before sending another message',163 error: 'Please wait before sending another message',
186 retryAfter,164 retryAfter,
187 isRateLimited: true165 isRateLimited: true
188 };166 };
189- return new Response(JSON.stringify(errorResponse), { 167+ return new Response(JSON.stringify(errorResponse), {
190 status: 429,168 status: 429,
191- headers: { 169+ headers: {
192 'Content-Type': 'application/json',170 'Content-Type': 'application/json',
193 'Retry-After': Math.ceil(retryAfter! / 1000).toString()171 'Retry-After': Math.ceil(retryAfter! / 1000).toString()
194 }172 }
@@ -197,10 +175,7 @@ export async function POST({ request, locals }: { request: Request, locals: App.
197 175
198 const data = await request.json() as SendMessageRequest;176 const data = await request.json() as SendMessageRequest;
199 if (!data.content || !data.userId) {177 if (!data.content || !data.userId) {
200- log.warn('Invalid payload received', { 178+ log.warn('Invalid payload received', { hasContent: Boolean(data.content), hasUserId: Boolean(data.userId) });
201- hasContent: Boolean(data.content),
202- hasUserId: Boolean(data.userId)
203- });
204 const errorResponse: SendMessageResponse = {179 const errorResponse: SendMessageResponse = {
205 success: false,180 success: false,
206 error: 'Invalid message payload'181 error: 'Invalid message payload'
@@ -216,7 +191,6 @@ export async function POST({ request, locals }: { request: Request, locals: App.
216 throw error(403, 'Cannot post messages as another user');191 throw error(403, 'Cannot post messages as another user');
217 }192 }
218 193
219- // Validate that both the chat room and user exist
220 const chatRoomId = data.chatRoomId || DEFAULT_CHAT_ROOM_ID;194 const chatRoomId = data.chatRoomId || DEFAULT_CHAT_ROOM_ID;
221 195
222 // Check chat room existence196 // Check chat room existence
@@ -261,28 +235,14 @@ export async function POST({ request, locals }: { request: Request, locals: App.
261 235
262 // Save to DB236 // Save to DB
263 await db.insert(messages).values(newMessage);237 await db.insert(messages).values(newMessage);
264- log.debug('Message saved in DB', { 238+ log.debug('Message saved in DB', { messageId: newMessage.id, chatRoomId: newMessage.chatRoomId, type: newMessage.type, timestamp: newMessage.timestamp });
265- messageId: newMessage.id,
266- chatRoomId: newMessage.chatRoomId,
267- type: newMessage.type,
268- timestamp: newMessage.timestamp
269- });
270 239
271- // Update message cache240+ // Removed updating message cache for authenticated users; relying on SSE for real-time updates
272- messageCache.addMessage(newMessage);
273- log.debug('Message added to cache', {
274- messageId: newMessage.id,
275- chatRoomId: newMessage.chatRoomId
276- });
277 241
278 // Broadcast the new message via the unified SSE emitter242 // Broadcast the new message via the unified SSE emitter
279 sseEmitter.emit('sse', { type: 'chatMessage', data: newMessage });243 sseEmitter.emit('sse', { type: 'chatMessage', data: newMessage });
280 244
281- log.debug('Message processed successfully', {245+ log.debug('Message processed successfully', { messageId: newMessage.id, userId: `${newMessage.senderId.slice(0, 4)}...${newMessage.senderId.slice(-4)}`, roomId: newMessage.chatRoomId });
282- messageId: newMessage.id,
283- userId: `${newMessage.senderId.slice(0, 4)}...${newMessage.senderId.slice(-4)}`,
284- roomId: newMessage.chatRoomId
285- });
286 246
287 const successResponse: SendMessageResponse = {247 const successResponse: SendMessageResponse = {
288 success: true,248 success: true,
@@ -297,9 +257,7 @@ export async function POST({ request, locals }: { request: Request, locals: App.
297 }257 }
298 );258 );
299 } catch (error) {259 } catch (error) {
300- log.error('Error processing message', { 260+ log.error('Error processing message', { error: error instanceof Error ? error.message : 'Unknown error' });
301- error: error instanceof Error ? error.message : 'Unknown error'
302- });
303 const errorResponse: SendMessageResponse = {261 const errorResponse: SendMessageResponse = {
304 success: false,262 success: false,
305 error: 'Failed to save message'263 error: 'Failed to save message'

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 3

Tags 8

↑↓ moveenter openesc close