feat: implement clock expiry scanning and handling for game records (#53)
Build & Test (NowChessSystems) TeamCity build finished
Build & Test (NowChessSystems) TeamCity build finished
Reviewed-on: #53
This commit was merged in pull request #53.
This commit is contained in:
@@ -60,6 +60,7 @@ dependencies {
|
||||
implementation("io.quarkus:quarkus-opentelemetry")
|
||||
implementation("com.fasterxml.jackson.module:jackson-module-scala_3:${versions["JACKSON_SCALA"]!!}")
|
||||
implementation("io.quarkus:quarkus-redis-client")
|
||||
implementation("io.quarkus:quarkus-scheduler")
|
||||
|
||||
testImplementation(platform("org.junit:junit-bom:5.13.4"))
|
||||
testImplementation("org.junit.jupiter:junit-jupiter")
|
||||
|
||||
@@ -34,6 +34,19 @@ class GameRecordRepository:
|
||||
.asScala
|
||||
.toList
|
||||
|
||||
def findExpiredLiveClockGames(nowMs: Long): List[GameRecord] =
|
||||
em.createQuery(
|
||||
"SELECT g FROM GameRecord g WHERE g.result IS NULL AND g.clockLastTickAt IS NOT NULL AND g.whiteRemainingMs IS NOT NULL",
|
||||
classOf[GameRecord],
|
||||
).getResultList
|
||||
.asScala
|
||||
.toList
|
||||
.filter { g =>
|
||||
val remaining =
|
||||
if g.clockActiveColor == "white" then g.whiteRemainingMs.longValue else g.blackRemainingMs.longValue
|
||||
g.clockLastTickAt.longValue + remaining < nowMs
|
||||
}
|
||||
|
||||
def findByPlayerIdRunning(playerId: String, offset: Int, limit: Int): List[GameRecord] =
|
||||
em.createQuery(
|
||||
"SELECT g FROM GameRecord g WHERE g.whiteId = :id OR g.blackId = :id AND g.result = null ORDER BY g.updatedAt DESC",
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
package de.nowchess.store.service
|
||||
|
||||
import de.nowchess.store.config.RedisConfig
|
||||
import de.nowchess.store.repository.GameRecordRepository
|
||||
import io.quarkus.redis.datasource.RedisDataSource
|
||||
import io.quarkus.scheduler.Scheduled
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.inject.Inject
|
||||
import org.jboss.logging.Logger
|
||||
import scala.compiletime.uninitialized
|
||||
|
||||
@ApplicationScoped
|
||||
class ClockExpiryScanner:
|
||||
@Inject
|
||||
// scalafix:off DisableSyntax.var
|
||||
var repository: GameRecordRepository = uninitialized
|
||||
@Inject var redis: RedisDataSource = uninitialized
|
||||
@Inject var redisConfig: RedisConfig = uninitialized
|
||||
// scalafix:on
|
||||
|
||||
private val log = Logger.getLogger(classOf[ClockExpiryScanner])
|
||||
|
||||
private def clockExpireChannel: String = s"${redisConfig.prefix}:game:clock:expire"
|
||||
|
||||
@Scheduled(every = "30s")
|
||||
def scan(): Unit =
|
||||
try
|
||||
val nowMs = System.currentTimeMillis()
|
||||
val expired = repository.findExpiredLiveClockGames(nowMs)
|
||||
if expired.nonEmpty then
|
||||
log.infof("Found %d games with expired clocks", expired.size)
|
||||
expired.foreach { record =>
|
||||
log.infof("Publishing clock expiry for game %s", record.gameId)
|
||||
redis.pubsub(classOf[String]).publish(clockExpireChannel, record.gameId)
|
||||
}
|
||||
catch case ex: Exception => log.warnf(ex, "Clock expiry scan failed")
|
||||
Reference in New Issue
Block a user