Skip to content

Commit

Permalink
Merge pull request #298 from DongHoonYu96/fix-be-room-clean
Browse files Browse the repository at this point in the history
[BE] fix-room-clean
  • Loading branch information
DongHoonYu96 authored Dec 2, 2024
2 parents 7e75160 + 538dcea commit 51c0ce2
Show file tree
Hide file tree
Showing 3 changed files with 61 additions and 22 deletions.
53 changes: 52 additions & 1 deletion BE/src/game/redis/game-redis-memory.service.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Injectable, Logger } from '@nestjs/common';
import { InjectRedis } from '@nestjs-modules/ioredis';
import Redis from 'ioredis';
import { Cron, CronExpression } from '@nestjs/schedule';
import { REDIS_KEY } from '../../common/constants/redis-key.constant';
import { Cron, CronExpression } from '@nestjs/schedule';

@Injectable()
export class GameRedisMemoryService {
private readonly logger = new Logger(GameRedisMemoryService.name);
private readonly BATCH_SIZE = 100; // ํ•œ ๋ฒˆ์— ์ฒ˜๋ฆฌํ•  ๋ฐฐ์น˜ ํฌ๊ธฐ
private readonly INACTIVE_THRESHOLD = 30 * 60 * 1000; // 30๋ถ„ 30 * 60 * 1000;

private readonly TTL = {
ROOM: 3 * 60 * 60,
Expand All @@ -18,10 +19,60 @@ export class GameRedisMemoryService {

constructor(@InjectRedis() private readonly redis: Redis) {}

/**
* ๋น„ํ™œ์„ฑ ๋ฐฉ ์ฒดํฌ (์ฃผ๊ธฐ์ ์œผ๋กœ ์‹คํ–‰)
*/
/**
* ๋น„ํ™œ์„ฑ ๋ฐฉ์„ ์ฒดํฌํ•˜๊ณ  ์ •๋ฆฌํ•˜๋Š” ํฌ๋ก  ์ž‘์—…
* SCAN์„ ์‚ฌ์šฉํ•˜์—ฌ ๋Œ€๊ทœ๋ชจ ๋ฐฉ ๋ชฉ๋ก๋„ ์•ˆ์ „ํ•˜๊ฒŒ ์ฒ˜๋ฆฌ
*/
@Cron(CronExpression.EVERY_10_MINUTES)
async checkInactiveRooms(): Promise<void> {
this.logger.verbose('๋น„ํ™œ์„ฑ ๋ฐฉ ์ฒดํฌ ์‹œ์ž‘');
try {
const now = Date.now();
let cursor = '0';
let processedCount = 0;

do {
// SCAN์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐฐ์น˜ ๋‹จ์œ„๋กœ ์ฒ˜๋ฆฌ
const [nextCursor, rooms] = await this.redis.sscan(
REDIS_KEY.ACTIVE_ROOMS,
cursor,
'COUNT',
this.BATCH_SIZE
);
cursor = nextCursor;

// ๋ณ‘๋ ฌ๋กœ ๋ฐฉ ์ƒํƒœ ์ฒดํฌ ๋ฐ ์ฒ˜๋ฆฌ
await Promise.all(
rooms.map(async (roomId) => {
try {
const lastActivity = await this.redis.hget(REDIS_KEY.ROOM(roomId), 'lastActivityAt');

if (lastActivity && now - parseInt(lastActivity) > this.INACTIVE_THRESHOLD) {
await this.redis.publish('room:cleanup', roomId);
this.logger.verbose(`๋น„ํ™œ์„ฑ์œผ๋กœ ์ธํ•ด ๋ฐฉ ${roomId} ์ •๋ฆฌ ์‹œ์ž‘`);
processedCount++;
}
} catch (error) {
this.logger.error(`๋ฐฉ ${roomId} ์ฒ˜๋ฆฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: ${error.message}`);
}
})
);
} while (cursor !== '0');

this.logger.verbose(`๋น„ํ™œ์„ฑ ๋ฐฉ ์ฒดํฌ ์™„๋ฃŒ: ${processedCount}๊ฐœ ๋ฐฉ ์ •๋ฆฌ๋จ`);
} catch (error) {
this.logger.error(`๋น„ํ™œ์„ฑ ๋ฐฉ ์ฒดํฌ ์ค‘ ์˜ค๋ฅ˜ ๋ฐœ์ƒ: ${error.message}`);
}
}

/**
* TTL ๊ด€๋ฆฌ๋ฅผ ์œ„ํ•œ ์Šค์ผ€์ค„๋Ÿฌ
* ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ๋กœ ๋ธ”๋กœํ‚น ์ตœ์†Œํ™”
*/

// @Cron(CronExpression.EVERY_MINUTE)
async manageTTL(): Promise<void> {
try {
Expand Down
9 changes: 9 additions & 0 deletions BE/src/game/redis/subscribers/room.cleanup.subscriber.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@ export class RoomCleanupSubscriber extends RedisSubscriber {
try {
const pipeline = this.redis.pipeline();

// 1. ๋ฐฉ์— ์†ํ•œ ํ”Œ๋ ˆ์ด์–ด ๋ชฉ๋ก ๊ฐ€์ ธ์˜ค๊ธฐ, 200๋ช…๋ฏธ๋งŒ -> smembers ์‚ฌ์šฉ!
const players = await this.redis.smembers(REDIS_KEY.ROOM_PLAYERS(roomId));

// 2. ํ”Œ๋ ˆ์ด์–ด ๋ฐ์ดํ„ฐ ์‚ญ์ œ
for (const playerId of players) {
pipeline.del(REDIS_KEY.PLAYER(playerId)); // ํ”Œ๋ ˆ์ด์–ด ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ
pipeline.del(`${REDIS_KEY.PLAYER(playerId)}:Changes`); // ํ”Œ๋ ˆ์ด์–ด Changes ๋ฐ์ดํ„ฐ
}

// 1. ๋ฐฉ ๊ด€๋ จ ๊ธฐ๋ณธ ๋ฐ์ดํ„ฐ ์‚ญ์ œ
pipeline.del(REDIS_KEY.ROOM(roomId));
pipeline.del(REDIS_KEY.ROOM_PLAYERS(roomId));
Expand Down
21 changes: 0 additions & 21 deletions BE/src/game/service/game.room.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import { generateUniquePin } from '../../common/utils/utils';
import SocketEvents from '../../common/constants/socket-events';
import { UpdateRoomOptionDto } from '../dto/update-room-option.dto';
import { UpdateRoomQuizsetDto } from '../dto/update-room-quizset.dto';
import { Cron, CronExpression } from '@nestjs/schedule';
import { Socket } from 'socket.io';
import { KickRoomDto } from '../dto/kick-room.dto';
import { TraceClass } from '../../common/interceptor/SocketEventLoggerInterceptor';
Expand All @@ -18,7 +17,6 @@ import { TraceClass } from '../../common/interceptor/SocketEventLoggerIntercepto
export class GameRoomService {
private readonly logger = new Logger(GameRoomService.name);
private readonly INACTIVE_THRESHOLD = 30 * 60 * 1000; // 30๋ถ„ 30 * 60 * 1000;
// private readonly PLAYER_GRACE_PERIOD = 10; // 10์ดˆ

constructor(
@InjectRedis() private readonly redis: Redis,
Expand Down Expand Up @@ -255,25 +253,6 @@ export class GameRoomService {
await pipeline.exec();
}

/**
* ๋น„ํ™œ์„ฑ ๋ฐฉ ์ฒดํฌ (์ฃผ๊ธฐ์ ์œผ๋กœ ์‹คํ–‰)
*/
@Cron(CronExpression.EVERY_MINUTE)
async checkInactiveRooms(): Promise<void> {
const now = Date.now();
const rooms = await this.redis.smembers(REDIS_KEY.ACTIVE_ROOMS);
this.logger.verbose(`๋น„ํ™œ์„ฑ ๋ฐฉ ์ฒดํฌ์‹œ์ž‘ / ํ™œ์„ฑ ๋ฐฉ ๋ชฉ๋ก: ${rooms}`);

for (const roomId of rooms) {
const lastActivity = await this.redis.hget(REDIS_KEY.ROOM(roomId), 'lastActivityAt');

if (lastActivity && now - parseInt(lastActivity) > this.INACTIVE_THRESHOLD) {
await this.redis.publish('room:cleanup', roomId);
this.logger.verbose(`๋น„ํ™œ์„ฑ์œผ๋กœ ์ธํ•ด ๋ฐฉ ${roomId} ์ •๋ฆฌ ์‹œ์ž‘`);
}
}
}

/**
* ํ”Œ๋ ˆ์ด์–ด ๊ด€๋ จ ๋ชจ๋“  ๋ฐ์ดํ„ฐ์— TTL ์„ค์ •
*/
Expand Down

0 comments on commit 51c0ce2

Please sign in to comment.