From c85541befcd5cb0cc820a69d4a850c7008e7f431 Mon Sep 17 00:00:00 2001 From: Brutus5000 Date: Sat, 28 Oct 2023 13:06:14 +0200 Subject: [PATCH] Add scheduled session cleanup + error logging --- build.gradle.kts | 1 + .../icebreaker/config/FafProperties.kt | 2 + .../persistence/IceSessionEntity.kt | 7 +++- .../icebreaker/service/SessionService.kt | 26 +++++++++++- .../service/xirsys/XirsysSessionHandler.kt | 40 +++++++++++++------ .../icebreaker/web/SessionController.kt | 3 +- src/main/resources/application.yaml | 11 +++-- src/main/resources/logback.xml | 16 ++++++++ 8 files changed, 86 insertions(+), 20 deletions(-) create mode 100644 src/main/resources/logback.xml diff --git a/build.gradle.kts b/build.gradle.kts index a151361..cfa140a 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -21,6 +21,7 @@ val quarkusPlatformVersion: String by project dependencies { implementation(enforcedPlatform("$quarkusPlatformGroupId:$quarkusPlatformArtifactId:$quarkusPlatformVersion")) implementation("io.quarkus:quarkus-config-yaml") + implementation("io.quarkus:quarkus-scheduler") implementation("io.quarkus:quarkus-resteasy-reactive-jackson") implementation("io.quarkus:quarkus-container-image-docker") implementation("io.quarkus:quarkus-hibernate-orm-panache-kotlin") diff --git a/src/main/kotlin/com/faforever/icebreaker/config/FafProperties.kt b/src/main/kotlin/com/faforever/icebreaker/config/FafProperties.kt index ec3ce25..0929773 100644 --- a/src/main/kotlin/com/faforever/icebreaker/config/FafProperties.kt +++ b/src/main/kotlin/com/faforever/icebreaker/config/FafProperties.kt @@ -10,4 +10,6 @@ interface FafProperties { fun environment(): String fun tokenLifetimeSeconds(): Long + + fun maxSessionLifeTimeHours(): Long } diff --git a/src/main/kotlin/com/faforever/icebreaker/persistence/IceSessionEntity.kt b/src/main/kotlin/com/faforever/icebreaker/persistence/IceSessionEntity.kt index 49b7e56..35b7273 100644 --- a/src/main/kotlin/com/faforever/icebreaker/persistence/IceSessionEntity.kt +++ b/src/main/kotlin/com/faforever/icebreaker/persistence/IceSessionEntity.kt @@ -6,7 +6,7 @@ import jakarta.inject.Singleton import jakarta.persistence.Entity import jakarta.persistence.Id import jakarta.persistence.Table -import java.time.LocalDateTime +import java.time.Instant import java.util.UUID @Entity @@ -17,7 +17,7 @@ data class IceSessionEntity( val gameId: Long, - val createdAt: LocalDateTime, + val createdAt: Instant, ) : PanacheEntityBase @@ -25,4 +25,7 @@ data class IceSessionEntity( class IceSessionRepository : PanacheRepository { fun findByGameId(gameId: Long) = find("gameId = ?1", gameId).firstResult() + + fun findByCreatedAtLesserThan(instant: Instant) = + find("createdAt <= ?1", instant).list() } diff --git a/src/main/kotlin/com/faforever/icebreaker/service/SessionService.kt b/src/main/kotlin/com/faforever/icebreaker/service/SessionService.kt index 92643b7..89acce4 100644 --- a/src/main/kotlin/com/faforever/icebreaker/service/SessionService.kt +++ b/src/main/kotlin/com/faforever/icebreaker/service/SessionService.kt @@ -1,15 +1,23 @@ package com.faforever.icebreaker.service +import com.faforever.icebreaker.config.FafProperties import com.faforever.icebreaker.persistence.IceSessionEntity import com.faforever.icebreaker.persistence.IceSessionRepository +import io.quarkus.scheduler.Scheduled import jakarta.enterprise.inject.Instance import jakarta.inject.Singleton import jakarta.transaction.Transactional -import java.time.LocalDateTime +import org.slf4j.Logger +import org.slf4j.LoggerFactory +import java.time.Instant +import java.time.temporal.ChronoUnit + +private val LOG: Logger = LoggerFactory.getLogger(SessionService::class.java) @Singleton class SessionService( sessionHandlers: Instance, + private val fafProperties: FafProperties, private val iceSessionRepository: IceSessionRepository, ) { private val activeSessionHandlers = sessionHandlers.filter { it.active } @@ -17,7 +25,8 @@ class SessionService( @Transactional fun getSession(gameId: Long): Session { val session = iceSessionRepository.findByGameId(gameId) - ?: IceSessionEntity(gameId = gameId, createdAt = LocalDateTime.now()).also { + ?: IceSessionEntity(gameId = gameId, createdAt = Instant.now()).also { + LOG.debug("Creating session for gameId $gameId") iceSessionRepository.persist(it) } @@ -31,4 +40,17 @@ class SessionService( servers = servers, ) } + + @Transactional + @Scheduled(every = "10m") + fun cleanUpSessions() { + LOG.info("Cleaning up outdated sessions") + iceSessionRepository.findByCreatedAtLesserThan( + instant = Instant.now().plus(fafProperties.maxSessionLifeTimeHours(), ChronoUnit.HOURS), + ).forEach { iceSession -> + LOG.debug("Cleaning up session id ${iceSession.id}") + activeSessionHandlers.forEach { it.deleteSession(iceSession.id) } + iceSessionRepository.delete(iceSession) + } + } } diff --git a/src/main/kotlin/com/faforever/icebreaker/service/xirsys/XirsysSessionHandler.kt b/src/main/kotlin/com/faforever/icebreaker/service/xirsys/XirsysSessionHandler.kt index 42f1ff0..68151d2 100644 --- a/src/main/kotlin/com/faforever/icebreaker/service/xirsys/XirsysSessionHandler.kt +++ b/src/main/kotlin/com/faforever/icebreaker/service/xirsys/XirsysSessionHandler.kt @@ -5,9 +5,12 @@ import com.faforever.icebreaker.service.Session import com.faforever.icebreaker.service.SessionHandler import jakarta.inject.Singleton import org.eclipse.microprofile.rest.client.RestClientBuilder -import java.io.IOException +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.net.URI +private val LOG: Logger = LoggerFactory.getLogger(XirsysSessionHandler::class.java) + @Singleton class XirsysSessionHandler( private val fafProperties: FafProperties, @@ -26,7 +29,12 @@ class XirsysSessionHandler( override val active = xirsysProperties.enabled() override fun createSession(id: String) { - if (listSessions().contains(id)) return + if (listSessions().contains(id)) { + LOG.debug("Session id $id already exists") + return + } + + LOG.debug("Creating session id $id") val result = xirsysClient.createChannel( namespace = xirsysProperties.channelNamespace(), @@ -35,7 +43,7 @@ class XirsysSessionHandler( ) if (result is XirsysResponse.Error) { - throw IOException(result.code) + LOG.error("Creating session failed: ${result.code}") } } @@ -47,18 +55,21 @@ class XirsysSessionHandler( ) if (result is XirsysResponse.Error) { - throw IOException(result.code) + LOG.error("Deleting session failed: ${result.code}") } } - fun listSessions(): List = + private fun listSessions(): List = when ( val result = xirsysClient.listChannel( namespace = xirsysProperties.channelNamespace(), environment = fafProperties.environment(), ) ) { - is XirsysResponse.Error -> throw IOException(result.code) + is XirsysResponse.Error -> emptyList().also { + LOG.error("Listing sessions failed: ${result.code}") + } + is XirsysResponse.Success -> result.data } @@ -71,13 +82,18 @@ class XirsysSessionHandler( turnRequest = TurnRequest(), ) ) { - is XirsysResponse.Error -> throw IOException(result.code) + is XirsysResponse.Error -> emptyList().also { + LOG.error("Requesting ICE servers failed: ${result.code}") + } + is XirsysResponse.Success -> result.data.iceServers.let { - Session.Server( - userName = it.username, - secret = it.credential, - iceServerUrls = it.urls, + listOf( + Session.Server( + userName = it.username, + secret = it.credential, + iceServerUrls = it.urls, + ), ) } - }.let { listOf(it) } + } } diff --git a/src/main/kotlin/com/faforever/icebreaker/web/SessionController.kt b/src/main/kotlin/com/faforever/icebreaker/web/SessionController.kt index d1473d4..d6bde22 100644 --- a/src/main/kotlin/com/faforever/icebreaker/web/SessionController.kt +++ b/src/main/kotlin/com/faforever/icebreaker/web/SessionController.kt @@ -2,6 +2,7 @@ package com.faforever.icebreaker.web import com.faforever.icebreaker.service.Session import com.faforever.icebreaker.service.SessionService +import io.quarkus.security.PermissionsAllowed import jakarta.inject.Singleton import jakarta.ws.rs.GET import jakarta.ws.rs.Path @@ -11,9 +12,9 @@ import org.jboss.resteasy.reactive.RestPath @Singleton class SessionController(private val sessionService: SessionService) { - // @PermissionsAllowed("USER:lobby") @GET @Path("/game/{gameId}") + @PermissionsAllowed("USER:lobby") fun getSession(@RestPath gameId: Long): Session = sessionService.getSession(gameId) } diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 9ebd3b7..1c517eb 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -1,9 +1,6 @@ quarkus: log: level: INFO -# category: -# "org.flyway": -# level: DEBUG application: name: faf-icebreaker datasource: @@ -27,9 +24,17 @@ mp: faf: environment: ${ENVIRONMENT:dev} token-lifetime-seconds: 30 + max-session-life-time-hours: 24 xirsys: enabled: ${XIRSYS_ENABLED:true} base-url: "https://global.xirsys.net" ident: ${XIRSYS_IDENT} secret: ${XIRSYS_SECRET} channel-namespace: "faf" + +"%dev": + quarkus: + log: + category: + "org.hibernate.SQL": + level: DEBUG \ No newline at end of file diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml new file mode 100644 index 0000000..3a84501 --- /dev/null +++ b/src/main/resources/logback.xml @@ -0,0 +1,16 @@ + + + + true + + + %cyan(%d{HH:mm:ss.SSS}) %highlight(%-5level) [%-16X{traceId:-no trace}] %gray([%thread]) %magenta(%logger{36})- %msg%n + + + + + + + +