redis pt 4: presence & more cache invalidation
This commit is contained in:
@@ -1,10 +1,14 @@
|
||||
import { Injectable, Logger, Inject } from '@nestjs/common';
|
||||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||
import { OnEvent } from '@nestjs/event-emitter';
|
||||
import Redis from 'ioredis';
|
||||
import { Server } from 'socket.io';
|
||||
import { UserSocketService } from './user-socket.service';
|
||||
import { UserEvents } from '../../users/events/user.events';
|
||||
import type { AuthenticatedSocket } from '../../types/socket';
|
||||
import { REDIS_CLIENT } from '../../database/redis.module';
|
||||
import { REDIS_CHANNEL } from './ws-events';
|
||||
import { UserSocketService } from './user-socket.service';
|
||||
|
||||
const PRESENCE_UPDATE_THROTTLE_MS = 15_000;
|
||||
|
||||
@Injectable()
|
||||
export class WsNotificationService {
|
||||
@@ -42,6 +46,11 @@ export class WsNotificationService {
|
||||
this.io.to(socketId).emit(event, payload);
|
||||
}
|
||||
|
||||
@OnEvent(UserEvents.PROFILE_UPDATED)
|
||||
async handleUserProfileUpdated(payload: { userId: string }) {
|
||||
await this.publishUserProfileCacheInvalidate(payload.userId);
|
||||
}
|
||||
|
||||
async updateFriendsCache(
|
||||
userId: string,
|
||||
friendId: string,
|
||||
@@ -126,4 +135,63 @@ export class WsNotificationService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async publishUserProfileCacheInvalidate(userId: string) {
|
||||
if (this.redisClient) {
|
||||
try {
|
||||
await this.redisClient.publish(
|
||||
REDIS_CHANNEL.USER_PROFILE_CACHE_INVALIDATE,
|
||||
JSON.stringify({ userId }),
|
||||
);
|
||||
return;
|
||||
} catch (error) {
|
||||
this.logger.warn(
|
||||
'Redis publish failed for user profile cache invalidate; applying local update only',
|
||||
error as Error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
await this.clearSenderNameCache(userId);
|
||||
}
|
||||
|
||||
async clearSenderNameCache(userId: string) {
|
||||
if (!this.io) {
|
||||
return;
|
||||
}
|
||||
|
||||
const socketId = await this.userSocketService.getSocket(userId);
|
||||
if (!socketId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const socket = this.io.sockets.sockets.get(socketId) as
|
||||
| AuthenticatedSocket
|
||||
| undefined;
|
||||
if (!socket?.data) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket.data.senderName = undefined;
|
||||
socket.data.senderNameCachedAt = undefined;
|
||||
}
|
||||
|
||||
async maybeTouchPresence(client: AuthenticatedSocket): Promise<void> {
|
||||
const userId = client.data.userId;
|
||||
if (!userId) {
|
||||
return;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const lastSeenAt = client.data.lastSeenAt;
|
||||
if (
|
||||
typeof lastSeenAt === 'number' &&
|
||||
now - lastSeenAt < PRESENCE_UPDATE_THROTTLE_MS
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
client.data.lastSeenAt = now;
|
||||
await this.userSocketService.touchLastSeen(userId);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user