feat: NCS-82 add Swiss-system tournament module #55
+1
-1
@@ -53,4 +53,4 @@ modules/tournament/src/main/resources/keys/dev-public.pem
|
|||||||
modules/account/src/main/resources/keys/dev-private.pem
|
modules/account/src/main/resources/keys/dev-private.pem
|
||||||
modules/account/src/main/resources/keys/dev-public.pem
|
modules/account/src/main/resources/keys/dev-public.pem
|
||||||
modules/core/src/main/resources/keys/dev-public.pem
|
modules/core/src/main/resources/keys/dev-public.pem
|
||||||
|
|
|||||||
java_pid2736.hprof
|
*.hprof
|
||||||
|
|||||||
@@ -76,6 +76,6 @@ class OfficialBotAccount extends PanacheEntityBase:
|
|||||||
|
|
||||||
var createdAt: Instant = uninitialized
|
var createdAt: Instant = uninitialized
|
||||||
|
|
||||||
|
lq64
commented
Missing nullable = false. A just-constructed OfficialBotAccount starts with token = uninitialized, and AccountService does two separate persist() calls to work around this. Add nullable = false and generate the token before the first persist to collapse it to one round-trip and make the constraint explicit at the DB level. Missing nullable = false. A just-constructed OfficialBotAccount starts with token = uninitialized, and AccountService does two separate persist() calls to work around this. Add nullable = false and generate the token before the first persist to collapse it to one round-trip and make the constraint explicit at the DB level.
|
|||||||
@Column(length = 1024)
|
@Column(nullable = false, length = 1024)
|
||||||
var token: String = uninitialized
|
var token: String = uninitialized
|
||||||
// scalafix:on
|
// scalafix:on
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ class AccountResource:
|
|||||||
|
|
||||||
@POST
|
@POST
|
||||||
@Path("/official-bots")
|
@Path("/official-bots")
|
||||||
@RolesAllowed(Array("**"))
|
@RolesAllowed(Array("Admin"))
|
||||||
|
lq64
commented
Is it okay to leave it like that? Is it okay to leave it like that?
|
|||||||
def createOfficialBot(req: CreateBotAccountRequest): Response =
|
def createOfficialBot(req: CreateBotAccountRequest): Response =
|
||||||
accountService.createOfficialBotAccount(req.name) match
|
accountService.createOfficialBotAccount(req.name) match
|
||||||
case Right(bot) =>
|
case Right(bot) =>
|
||||||
|
|||||||
@@ -201,11 +201,12 @@ class AccountService:
|
|||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
def createOfficialBotAccount(botName: String): Either[AccountError, OfficialBotAccount] =
|
def createOfficialBotAccount(botName: String): Either[AccountError, OfficialBotAccount] =
|
||||||
|
val id = UUID.randomUUID()
|
||||||
val bot = new OfficialBotAccount()
|
val bot = new OfficialBotAccount()
|
||||||
|
bot.id = id
|
||||||
bot.name = botName
|
bot.name = botName
|
||||||
bot.createdAt = Instant.now()
|
bot.createdAt = Instant.now()
|
||||||
|
lq64
commented
Same two-persist pattern. The root cause is that bot.id is not available before the first persist(). Either use a pre-assigned UUID (@Id with UUID.randomUUID() before persist) or derive the token from a field that doesn't require a DB round-trip. Two flushes per creation is a correctness risk under failure: if the second persist fails, the entity exists without a valid token. Same two-persist pattern. The root cause is that bot.id is not available before the first persist(). Either use a pre-assigned UUID (@Id with UUID.randomUUID() before persist) or derive the token from a field that doesn't require a DB round-trip. Two flushes per creation is a correctness risk under failure: if the second persist fails, the entity exists without a valid token.
|
|||||||
officialBotAccountRepository.persist(bot)
|
bot.token = generateBotToken(id, botName)
|
||||||
bot.token = generateBotToken(bot.id, bot.name)
|
|
||||||
officialBotAccountRepository.persist(bot)
|
officialBotAccountRepository.persist(bot)
|
||||||
Right(bot)
|
Right(bot)
|
||||||
|
|
||||||
@@ -213,11 +214,12 @@ class AccountService:
|
|||||||
def syncOfficialBots(botNames: List[String]): Unit =
|
def syncOfficialBots(botNames: List[String]): Unit =
|
||||||
botNames.foreach { name =>
|
botNames.foreach { name =>
|
||||||
if officialBotAccountRepository.findByName(name).isEmpty then
|
if officialBotAccountRepository.findByName(name).isEmpty then
|
||||||
|
val id = UUID.randomUUID()
|
||||||
val bot = new OfficialBotAccount()
|
val bot = new OfficialBotAccount()
|
||||||
|
bot.id = id
|
||||||
bot.name = name
|
bot.name = name
|
||||||
bot.createdAt = Instant.now()
|
bot.createdAt = Instant.now()
|
||||||
officialBotAccountRepository.persist(bot)
|
bot.token = generateBotToken(id, name)
|
||||||
bot.token = generateBotToken(bot.id, bot.name)
|
|
||||||
officialBotAccountRepository.persist(bot)
|
officialBotAccountRepository.persist(bot)
|
||||||
log.infof("Auto-registered official bot: %s", name)
|
log.infof("Auto-registered official bot: %s", name)
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-4
@@ -1,5 +1,6 @@
|
|||||||
package de.nowchess.tournament.service
|
package de.nowchess.tournament.service
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper
|
||||||
import de.nowchess.tournament.client.{CoreCreateGameRequest, CoreGameClient, CorePlayerInfo, CoreTimeControl}
|
import de.nowchess.tournament.client.{CoreCreateGameRequest, CoreGameClient, CorePlayerInfo, CoreTimeControl}
|
||||||
import de.nowchess.tournament.config.RedisConfig
|
import de.nowchess.tournament.config.RedisConfig
|
||||||
import de.nowchess.tournament.domain.{Tournament, TournamentPairing, TournamentParticipant}
|
import de.nowchess.tournament.domain.{Tournament, TournamentPairing, TournamentParticipant}
|
||||||
@@ -40,8 +41,9 @@ class TournamentService:
|
|||||||
@RestClient
|
@RestClient
|
||||||
var coreGameClient: CoreGameClient = uninitialized
|
var coreGameClient: CoreGameClient = uninitialized
|
||||||
|
|
||||||
@Inject var redis: RedisDataSource = uninitialized
|
@Inject var redis: RedisDataSource = uninitialized
|
||||||
@Inject var redisConfig: RedisConfig = uninitialized
|
@Inject var redisConfig: RedisConfig = uninitialized
|
||||||
|
@Inject var objectMapper: ObjectMapper = uninitialized
|
||||||
// scalafix:on
|
// scalafix:on
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -187,8 +189,15 @@ class TournamentService:
|
|||||||
botAccountId: String,
|
botAccountId: String,
|
||||||
): Unit =
|
): Unit =
|
||||||
val channel = s"${redisConfig.prefix}:bot:$botName:events"
|
val channel = s"${redisConfig.prefix}:bot:$botName:events"
|
||||||
val payload =
|
val payload = objectMapper.writeValueAsString(
|
||||||
s"""{"type":"gameStart","gameId":"$gameId","playingAs":"$playingAs","difficulty":1500,"botAccountId":"$botAccountId"}"""
|
Map(
|
||||||
|
"type" -> "gameStart",
|
||||||
|
"gameId" -> gameId,
|
||||||
|
"playingAs" -> playingAs,
|
||||||
|
"difficulty" -> 1500,
|
||||||
|
"botAccountId" -> botAccountId,
|
||||||
|
),
|
||||||
|
)
|
||||||
Try(redis.pubsub(classOf[String]).publish(channel, payload)) match
|
Try(redis.pubsub(classOf[String]).publish(channel, payload)) match
|
||||||
case Failure(ex) => log.warnf(ex, "Failed to publish gameStart to bot channel %s", channel)
|
case Failure(ex) => log.warnf(ex, "Failed to publish gameStart to bot channel %s", channel)
|
||||||
case Success(_) => ()
|
case Success(_) => ()
|
||||||
|
|||||||
Reference in New Issue
Block a user
This is a heap dump filename with a specific PID, meaning a heap dump was taken during development. Confirm the dump file itself is not tracked in the repository (git ls-files | grep hprof). The gitignore entry should use a wildcard (*.hprof) to catch any future dump rather than a single named file.