Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRAW-287 MafiaGameInfo 메모리 -> Redis 저장소 이관 #101

Merged
merged 16 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
package com.xorker.draw.timer

import com.xorker.draw.mafia.event.JobWithStartTime
import com.xorker.draw.room.RoomId
import java.time.Duration
import java.time.LocalDateTime
import java.util.concurrent.ConcurrentHashMap
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.springframework.context.ApplicationEventPublisher
import org.springframework.stereotype.Component

@Component
internal class TimerAdapter(
private val eventPublisher: ApplicationEventPublisher,
) : TimerRepository {
internal class TimerAdapter : TimerRepository {
private val jobs = ConcurrentHashMap<RoomId, JobWithStartTime>()

override fun <T : Any> startTimer(interval: Duration, event: T): JobWithStartTime {
override fun startTimer(roomId: RoomId, interval: Duration, callback: () -> Unit): JobWithStartTime {
val job = JobWithStartTime()
CoroutineScope(Dispatchers.IO + job).launch {
delay(interval.toMillis())
eventPublisher.publishEvent(event)
}
return job
}

override fun startTimer(interval: Duration, callback: () -> Unit): JobWithStartTime {
val job = JobWithStartTime()
saveJob(roomId, job)

CoroutineScope(Dispatchers.IO + job).launch {
delay(interval.toMillis())
callback.invoke()
}
return job
}

override fun cancelTimer(roomId: RoomId) {
jobs[roomId]?.cancel()
jobs.remove(roomId)
}

override fun getTimerStartTime(roomId: RoomId): LocalDateTime? {
return jobs[roomId]?.startTime
}

private fun saveJob(roomId: RoomId, job: JobWithStartTime) {
jobs[roomId] = job
}
}
17 changes: 17 additions & 0 deletions adapter/redis/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import org.springframework.boot.gradle.tasks.bundling.BootJar

plugins {
kotlin("plugin.spring")
}

dependencies {
implementation(project(":domain"))
implementation(project(":support:metric"))

implementation("org.springframework.boot:spring-boot-starter-data-redis:${Versions.SPRING_BOOT}")
}

tasks {
withType<Jar> { enabled = true }
withType<BootJar> { enabled = false }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.xorker.draw.config

import com.xorker.draw.mafia.dto.RedisMafiaGameInfo
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.data.redis.connection.RedisConnectionFactory
import org.springframework.data.redis.connection.RedisStandaloneConfiguration
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer
import org.springframework.data.redis.serializer.StringRedisSerializer

@Configuration
internal class RedisConfig {

@Value("\${spring.data.redis.host}")
lateinit var host: String

@Value("\${spring.data.redis.password}")
lateinit var password: String

@Bean
fun redisConnectionFactory(): RedisConnectionFactory {
val config = RedisStandaloneConfiguration(host, 6379)
comforest marked this conversation as resolved.
Show resolved Hide resolved
config.setPassword(password)

return LettuceConnectionFactory(config)
}

@Bean
fun redisTemplateWithObject(connectionFactory: RedisConnectionFactory): RedisTemplate<String, RedisMafiaGameInfo> {
val template = RedisTemplate<String, RedisMafiaGameInfo>()

template.connectionFactory = connectionFactory

template.keySerializer = StringRedisSerializer()
template.valueSerializer = Jackson2JsonRedisSerializer(RedisMafiaGameInfo::class.java)

return template
}

@Bean
fun redisTemplate(connectionFactory: RedisConnectionFactory): RedisTemplate<String, String> {
val template = RedisTemplate<String, String>()

template.connectionFactory = connectionFactory

template.keySerializer = StringRedisSerializer()
template.valueSerializer = StringRedisSerializer()

return template
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package com.xorker.draw.mafia

import com.xorker.draw.mafia.dto.RedisMafiaGameInfo
import com.xorker.draw.mafia.dto.toRedisMafiaGameInfo
import com.xorker.draw.room.Room
import com.xorker.draw.room.RoomId
import com.xorker.draw.room.RoomRepository
import com.xorker.draw.support.metric.MetricManager
import com.xorker.draw.timer.TimerRepository
import com.xorker.draw.user.UserId
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.stereotype.Component

@Component
internal class MafiaGameAdapter(
private val metricManager: MetricManager,
private val redisTemplateWithObject: RedisTemplate<String, RedisMafiaGameInfo>,
private val redisTemplate: RedisTemplate<String, String>,
private val timerRepository: TimerRepository,
) : MafiaGameRepository, RoomRepository {

override fun saveGameInfo(gameInfo: MafiaGameInfo) {
val room = gameInfo.room
if (room.isEmpty()) {
removeGameInfo(gameInfo)
} else {
val findGameInfo = redisTemplateWithObject
.opsForValue()
.get(room.id.value)

if (findGameInfo == null) {
metricManager.increaseGameCount()
}

redisTemplateWithObject
.opsForValue()
.set(room.id.value, gameInfo.toRedisMafiaGameInfo())

room.players.forEach {
redisTemplate
.opsForValue()
.set(it.userId.value.toString(), room.id.value)
}
}
}

override fun removeGameInfo(gameInfo: MafiaGameInfo) {
metricManager.decreaseGameCount()

val room = gameInfo.room

room.players
.map { it.userId }
.forEach { removePlayer(it) }

val phase = gameInfo.phase

if (phase is MafiaPhaseWithTimer) {
timerRepository.cancelTimer(room.id)
}

redisTemplateWithObject.delete(room.id.value)
}

override fun getGameInfo(roomId: RoomId): MafiaGameInfo? {
return redisTemplateWithObject
.opsForValue()
.get(roomId.value)
?.toMafiaGameInfo()
}

override fun getGameInfo(userId: UserId): MafiaGameInfo? {
val roomId = redisTemplate
.opsForValue()
.get(userId.value.toString()) ?: return null

return redisTemplateWithObject
.opsForValue()
.get(roomId)
?.toMafiaGameInfo()
}

override fun removePlayer(userId: UserId) {
redisTemplate.delete(userId.value.toString())
}

override fun getRoom(roomId: RoomId): Room<MafiaPlayer>? {
return redisTemplateWithObject
.opsForValue()
.get(roomId.value)
?.toMafiaGameInfo()
?.room
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.xorker.draw.mafia.dto

import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
import com.xorker.draw.mafia.MafiaGameInfo

data class RedisMafiaGameInfo @JsonCreator constructor(
@JsonProperty("room") val room: RedisMafiaRoom,
@JsonProperty("phase") val phase: RedisMafiaPhase,
@JsonProperty("gameOption") val gameOption: RedisMafiaGameOption,
) {
fun toMafiaGameInfo(): MafiaGameInfo = MafiaGameInfo(
room = room.toRoom(),
phase = phase.toMafiaPhase(),
gameOption = gameOption.toGameOption(),
)
}

fun MafiaGameInfo.toRedisMafiaGameInfo(): RedisMafiaGameInfo = RedisMafiaGameInfo(
room = RedisMafiaRoom(
id = room.id.value,
locale = room.locale,
owner = RedisMafiaPlayer(
id = room.owner.userId.value,
nickname = room.owner.nickname,
color = room.owner.color,
isConnect = room.owner.isConnect(),
),
maxMemberNum = room.maxMemberNum,
players = room.players.map { player ->
RedisMafiaPlayer(
id = player.userId.value,
nickname = player.nickname,
color = player.color,
isConnect = player.isConnect(),
)
},
isRandomMatching = room.isRandomMatching,
),
phase = phase.toRedisMafiaPhase(),
gameOption = RedisMafiaGameOption(
minimum = gameOption.minimum,
maximum = gameOption.maximum,
readyTime = gameOption.readyTime.toMillis(),
introAnimationTime = gameOption.introAnimationTime.toMillis(),
roundAnimationTime = gameOption.roundAnimationTime.toMillis(),
round = gameOption.round,
turnTime = gameOption.turnTime.toMillis(),
turnCount = gameOption.turnCount,
voteTime = gameOption.voteTime.toMillis(),
answerTime = gameOption.answerTime.toMillis(),
endTime = gameOption.endTime.toMillis(),
),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.xorker.draw.mafia.dto

import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
import com.xorker.draw.mafia.MafiaGameOption
import java.time.Duration

data class RedisMafiaGameOption @JsonCreator constructor(
@JsonProperty("minimum") val minimum: Int,
@JsonProperty("maximum") val maximum: Int,
@JsonProperty("readyTime") val readyTime: Long,
@JsonProperty("introAnimationTime") val introAnimationTime: Long,
@JsonProperty("roundAnimationTime") val roundAnimationTime: Long,
@JsonProperty("round") val round: Int,
@JsonProperty("turnTime") val turnTime: Long,
@JsonProperty("turnCount") val turnCount: Int,
@JsonProperty("voteTime") val voteTime: Long,
@JsonProperty("answerTime") val answerTime: Long,
@JsonProperty("endTime") val endTime: Long,
)

fun RedisMafiaGameOption.toGameOption(): MafiaGameOption = MafiaGameOption(
minimum = this.minimum,
maximum = this.maximum,
readyTime = Duration.ofMillis(this.readyTime),
introAnimationTime = Duration.ofMillis(this.introAnimationTime),
roundAnimationTime = Duration.ofMillis(this.roundAnimationTime),
round = this.round,
turnTime = Duration.ofMillis(this.turnTime),
turnCount = this.turnCount,
voteTime = Duration.ofMillis(this.voteTime),
answerTime = Duration.ofMillis(this.answerTime),
endTime = Duration.ofMillis(this.endTime),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.xorker.draw.mafia.dto

import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty
import com.xorker.draw.mafia.MafiaKeyword

data class RedisMafiaKeyword @JsonCreator constructor(
@JsonProperty("category") val category: String,
@JsonProperty("answer") val answer: String,
)

fun RedisMafiaKeyword.toMafiaKeyword(): MafiaKeyword = MafiaKeyword(
answer = this.answer,
category = this.category,
)
Loading
Loading