From 65bc6a759937543df2d29905688bfa9e68d0c9d4 Mon Sep 17 00:00:00 2001 From: Janis Date: Wed, 10 Jun 2026 09:00:24 +0200 Subject: [PATCH] feat(reflection): add native reflection configuration for tournament classes fix(ws): improve WebSocket connection cleanup on close chore(stream): simplify group name generation in GameCreationStreamClient --- .../client/GameCreationStreamClient.scala | 2 +- .../reachability-metadata.json | 27 +++++++++++++++ .../config/NativeReflectionConfig.scala | 34 +++++++++++++++++++ .../ws/resource/UserWebSocketResource.scala | 7 +++- 4 files changed, 68 insertions(+), 2 deletions(-) create mode 100644 modules/tournament/src/main/resources/META-INF.native-image.de.nowchess.tournament/reachability-metadata.json create mode 100644 modules/tournament/src/main/scala/de/nowchess/tournament/config/NativeReflectionConfig.scala diff --git a/modules/account/src/main/scala/de/nowchess/account/client/GameCreationStreamClient.scala b/modules/account/src/main/scala/de/nowchess/account/client/GameCreationStreamClient.scala index 137ee57..5347f28 100644 --- a/modules/account/src/main/scala/de/nowchess/account/client/GameCreationStreamClient.scala +++ b/modules/account/src/main/scala/de/nowchess/account/client/GameCreationStreamClient.scala @@ -36,7 +36,7 @@ class GameCreationStreamClient: private val log = Logger.getLogger(classOf[GameCreationStreamClient]) private val instanceId = UUID.randomUUID().toString - private val groupName = s"account-game-creation-$instanceId" + private val groupName = "account-game-creation" private val consumerId = instanceId private val maxStreamLen = 1000L private val timeout = Duration.ofSeconds(10) diff --git a/modules/tournament/src/main/resources/META-INF.native-image.de.nowchess.tournament/reachability-metadata.json b/modules/tournament/src/main/resources/META-INF.native-image.de.nowchess.tournament/reachability-metadata.json new file mode 100644 index 0000000..7245246 --- /dev/null +++ b/modules/tournament/src/main/resources/META-INF.native-image.de.nowchess.tournament/reachability-metadata.json @@ -0,0 +1,27 @@ +{ + "reflection": [ + { "type": "scala.Tuple1[]" }, + { "type": "scala.Tuple2[]" }, + { "type": "scala.Tuple3[]" }, + { "type": "scala.Tuple4[]" }, + { "type": "scala.Tuple5[]" }, + { "type": "scala.Tuple6[]" }, + { "type": "scala.Tuple7[]" }, + { "type": "scala.Tuple8[]" }, + { "type": "scala.Tuple9[]" }, + { "type": "scala.Tuple10[]" }, + { "type": "scala.Tuple11[]" }, + { "type": "scala.Tuple12[]" }, + { "type": "scala.Tuple13[]" }, + { "type": "scala.Tuple14[]" }, + { "type": "scala.Tuple15[]" }, + { "type": "scala.Tuple16[]" }, + { "type": "scala.Tuple17[]" }, + { "type": "scala.Tuple18[]" }, + { "type": "scala.Tuple19[]" }, + { "type": "scala.Tuple20[]" }, + { "type": "scala.Tuple21[]" }, + { "type": "scala.Tuple22[]" }, + { "type": "com.fasterxml.jackson.module.scala.introspect.PropertyDescriptor[]" } + ] +} diff --git a/modules/tournament/src/main/scala/de/nowchess/tournament/config/NativeReflectionConfig.scala b/modules/tournament/src/main/scala/de/nowchess/tournament/config/NativeReflectionConfig.scala new file mode 100644 index 0000000..a77a32d --- /dev/null +++ b/modules/tournament/src/main/scala/de/nowchess/tournament/config/NativeReflectionConfig.scala @@ -0,0 +1,34 @@ +package de.nowchess.tournament.config + +import de.nowchess.tournament.client.{CoreCreateGameRequest, CoreGameResponse, CorePlayerInfo, CoreTimeControl} +import de.nowchess.tournament.domain.{Tournament, TournamentPairing, TournamentParticipant} +import de.nowchess.tournament.dto.* +import de.nowchess.tournament.error.TournamentError +import io.quarkus.runtime.annotations.RegisterForReflection + +@RegisterForReflection( + targets = Array( + classOf[Tournament], + classOf[TournamentPairing], + classOf[TournamentParticipant], + classOf[TournamentError], + classOf[BotRef], + classOf[Clock], + classOf[Variant], + classOf[CreateTournamentForm], + classOf[ResultDto], + classOf[Standing], + classOf[TournamentDto], + classOf[TournamentListDto], + classOf[PairingDto], + classOf[GameExportDto], + classOf[RoundPairingsDto], + classOf[ErrorDto], + classOf[OkDto], + classOf[CorePlayerInfo], + classOf[CoreTimeControl], + classOf[CoreCreateGameRequest], + classOf[CoreGameResponse], + ), +) +class NativeReflectionConfig diff --git a/modules/ws/src/main/scala/de/nowchess/ws/resource/UserWebSocketResource.scala b/modules/ws/src/main/scala/de/nowchess/ws/resource/UserWebSocketResource.scala index 13c30ec..52da4e9 100644 --- a/modules/ws/src/main/scala/de/nowchess/ws/resource/UserWebSocketResource.scala +++ b/modules/ws/src/main/scala/de/nowchess/ws/resource/UserWebSocketResource.scala @@ -63,7 +63,12 @@ class UserWebSocketResource: @OnClose def onClose(connection: WebSocketConnection): Unit = log.infof("User WebSocket closed — connectionId=%s", connection.id()) - connections.remove(connection.id()) + val userIdOpt = Option(connections.remove(connection.id())).map(_._1) + userIdOpt.foreach { userId => + Try(redis.stream(classOf[String]).xgroupDestroy(userStreamKey(userId), connection.id())) match + case Failure(ex) => log.warnf(ex, "Failed to destroy consumer group for connectionId=%s", connection.id()) + case Success(_) => () + } private def createGroupIfAbsent(userId: String, groupName: String): Unit = Try(