refactor: clean up code formatting and improve readability across multiple files
This commit is contained in:
@@ -12,8 +12,10 @@ import scala.jdk.CollectionConverters.*
|
||||
@GrpcService
|
||||
@Singleton
|
||||
class CoordinatorServiceHandler extends CoordinatorServiceGrpc.CoordinatorServiceImplBase:
|
||||
// scalafix:off DisableSyntax.var
|
||||
@Inject
|
||||
private var gameSubscriberManager: GameRedisSubscriberManager = uninitialized
|
||||
// scalafix:on DisableSyntax.var
|
||||
|
||||
override def batchResubscribeGames(
|
||||
request: BatchResubscribeRequest,
|
||||
|
||||
@@ -62,14 +62,14 @@ class GameRedisPublisher(
|
||||
case GameResult.Draw(_) => "draw"
|
||||
},
|
||||
terminationReason = entry.engine.context.result.map {
|
||||
case GameResult.Win(_, WinReason.Checkmate) => "checkmate"
|
||||
case GameResult.Win(_, WinReason.Resignation) => "resignation"
|
||||
case GameResult.Win(_, WinReason.TimeControl) => "timeout"
|
||||
case GameResult.Draw(DrawReason.Stalemate) => "stalemate"
|
||||
case GameResult.Draw(DrawReason.InsufficientMaterial) => "insufficient_material"
|
||||
case GameResult.Draw(DrawReason.FiftyMoveRule) => "fifty_move"
|
||||
case GameResult.Draw(DrawReason.ThreefoldRepetition) => "repetition"
|
||||
case GameResult.Draw(DrawReason.Agreement) => "agreement"
|
||||
case GameResult.Win(_, WinReason.Checkmate) => "checkmate"
|
||||
case GameResult.Win(_, WinReason.Resignation) => "resignation"
|
||||
case GameResult.Win(_, WinReason.TimeControl) => "timeout"
|
||||
case GameResult.Draw(DrawReason.Stalemate) => "stalemate"
|
||||
case GameResult.Draw(DrawReason.InsufficientMaterial) => "insufficient_material"
|
||||
case GameResult.Draw(DrawReason.FiftyMoveRule) => "fifty_move"
|
||||
case GameResult.Draw(DrawReason.ThreefoldRepetition) => "repetition"
|
||||
case GameResult.Draw(DrawReason.Agreement) => "agreement"
|
||||
},
|
||||
redoStack = entry.engine.redoStackMoves.map(GameDtoMapper.moveToUci),
|
||||
pendingTakebackRequest = entry.engine.pendingTakebackRequestBy.map(_.label.toLowerCase),
|
||||
|
||||
+20
-27
@@ -14,6 +14,7 @@ import io.quarkus.redis.datasource.RedisDataSource
|
||||
import io.quarkus.redis.datasource.pubsub.PubSubCommands
|
||||
import jakarta.annotation.PreDestroy
|
||||
import jakarta.enterprise.context.ApplicationScoped
|
||||
import jakarta.enterprise.inject.Instance
|
||||
import jakarta.inject.Inject
|
||||
import scala.compiletime.uninitialized
|
||||
import scala.util.Try
|
||||
@@ -24,14 +25,18 @@ import java.util.function.Consumer
|
||||
class GameRedisSubscriberManager:
|
||||
|
||||
// scalafix:off DisableSyntax.var
|
||||
@Inject var redis: RedisDataSource = uninitialized
|
||||
@Inject var registry: GameRegistry = uninitialized
|
||||
@Inject var objectMapper: ObjectMapper = uninitialized
|
||||
@Inject var redisConfig: RedisConfig = uninitialized
|
||||
@Inject var ioClient: IoGrpcClientWrapper = uninitialized
|
||||
@Inject var heartbeatService: InstanceHeartbeatService = null
|
||||
@Inject var redis: RedisDataSource = uninitialized
|
||||
@Inject var registry: GameRegistry = uninitialized
|
||||
@Inject var objectMapper: ObjectMapper = uninitialized
|
||||
@Inject var redisConfig: RedisConfig = uninitialized
|
||||
@Inject var ioClient: IoGrpcClientWrapper = uninitialized
|
||||
@Inject var heartbeatServiceInstance: Instance[InstanceHeartbeatService] = uninitialized
|
||||
// scalafix:on DisableSyntax.var
|
||||
|
||||
private def heartbeatServiceOpt: Option[InstanceHeartbeatService] =
|
||||
if heartbeatServiceInstance.isUnsatisfied then None
|
||||
else Some(heartbeatServiceInstance.get())
|
||||
|
||||
private val c2sListeners = new ConcurrentHashMap[String, PubSubCommands.RedisSubscriber]()
|
||||
private val s2cObservers = new ConcurrentHashMap[String, Observer]()
|
||||
|
||||
@@ -44,7 +49,7 @@ class GameRedisSubscriberManager:
|
||||
def subscribeGame(gameId: String): Unit =
|
||||
try
|
||||
val handler: Consumer[String] = msg => handleC2sMessage(gameId, msg)
|
||||
val subscriber = redis.pubsub(classOf[String]).subscribe(c2sTopic(gameId), handler)
|
||||
val subscriber = redis.pubsub(classOf[String]).subscribe(c2sTopic(gameId), handler)
|
||||
c2sListeners.put(gameId, subscriber)
|
||||
|
||||
val writebackFn: String => Unit = json => redis.pubsub(classOf[String]).publish("game-writeback", json)
|
||||
@@ -61,7 +66,7 @@ class GameRedisSubscriberManager:
|
||||
s2cObservers.put(gameId, obs)
|
||||
registry.get(gameId).foreach(_.engine.subscribe(obs))
|
||||
|
||||
if heartbeatService != null then heartbeatService.addGameSubscription(gameId)
|
||||
heartbeatServiceOpt.foreach(_.addGameSubscription(gameId))
|
||||
catch
|
||||
case e: Exception =>
|
||||
System.err.println(s"Warning: Redis subscription failed for game $gameId: ${e.getMessage}")
|
||||
@@ -75,7 +80,7 @@ class GameRedisSubscriberManager:
|
||||
registry.get(gameId).foreach(_.engine.unsubscribe(obs))
|
||||
}
|
||||
|
||||
if heartbeatService != null then heartbeatService.removeGameSubscription(gameId)
|
||||
heartbeatServiceOpt.foreach(_.removeGameSubscription(gameId))
|
||||
|
||||
private def handleC2sMessage(gameId: String, msg: String): Unit =
|
||||
parseC2sMessage(msg) match
|
||||
@@ -121,28 +126,16 @@ class GameRedisSubscriberManager:
|
||||
}
|
||||
|
||||
def batchResubscribeGames(gameIds: java.util.List[String]): Int =
|
||||
var count = 0
|
||||
gameIds.forEach { gameId =>
|
||||
subscribeGame(gameId)
|
||||
count += 1
|
||||
}
|
||||
count
|
||||
gameIds.forEach(subscribeGame)
|
||||
gameIds.size()
|
||||
|
||||
def unsubscribeGames(gameIds: java.util.List[String]): Int =
|
||||
var count = 0
|
||||
gameIds.forEach { gameId =>
|
||||
unsubscribeGame(gameId)
|
||||
count += 1
|
||||
}
|
||||
count
|
||||
gameIds.forEach(unsubscribeGame)
|
||||
gameIds.size()
|
||||
|
||||
def evictGames(gameIds: java.util.List[String]): Int =
|
||||
var count = 0
|
||||
gameIds.forEach { gameId =>
|
||||
unsubscribeGame(gameId)
|
||||
count += 1
|
||||
}
|
||||
count
|
||||
gameIds.forEach(unsubscribeGame)
|
||||
gameIds.size()
|
||||
|
||||
def drainInstance(): Int =
|
||||
val gameIds = new java.util.ArrayList(c2sListeners.keySet())
|
||||
|
||||
@@ -82,8 +82,7 @@ class GameResource:
|
||||
|
||||
private def assertIsNotBot(): Unit =
|
||||
val botType = Option(jwt.getClaim[AnyRef]("type")).map(_.toString).getOrElse("")
|
||||
if Set("bot", "official-bot").contains(botType) then
|
||||
throw ForbiddenException("Only bots can make moves")
|
||||
if Set("bot", "official-bot").contains(botType) then throw ForbiddenException("Only bots can make moves")
|
||||
|
||||
// scalafix:on DisableSyntax.throw
|
||||
|
||||
|
||||
+38
-46
@@ -20,6 +20,7 @@ import io.grpc.Channel
|
||||
|
||||
@ApplicationScoped
|
||||
class InstanceHeartbeatService:
|
||||
// scalafix:off DisableSyntax.var
|
||||
@Inject
|
||||
private var redis: RedisDataSource = uninitialized
|
||||
|
||||
@@ -36,51 +37,46 @@ class InstanceHeartbeatService:
|
||||
private var coordinatorEnabled: Boolean = true
|
||||
|
||||
private var coordinatorStub: CoordinatorServiceStub = uninitialized
|
||||
|
||||
private val log = Logger.getLogger(classOf[InstanceHeartbeatService])
|
||||
private val mapper = ObjectMapper()
|
||||
|
||||
private var instanceId = ""
|
||||
private var redisPrefix = "nowchess"
|
||||
private val log = Logger.getLogger(classOf[InstanceHeartbeatService])
|
||||
private val mapper = ObjectMapper()
|
||||
private var instanceId = ""
|
||||
private var redisPrefix = "nowchess"
|
||||
private var streamObserver: Option[StreamObserver[HeartbeatFrame]] = None
|
||||
private var heartbeatExecutor = Executors.newScheduledThreadPool(1)
|
||||
private var redisHeartbeatExecutor = Executors.newScheduledThreadPool(1)
|
||||
private var subscriptionCount = 0
|
||||
private var localCacheSize = 0
|
||||
private var serviceActive = false
|
||||
private var shuttingDown = false
|
||||
private var serviceActive = false
|
||||
private var shuttingDown = false
|
||||
// scalafix:on DisableSyntax.var
|
||||
|
||||
def onStart(@Observes event: StartupEvent): Unit =
|
||||
if !coordinatorEnabled then
|
||||
log.info("Coordinator support disabled via config; skipping heartbeat service startup")
|
||||
return
|
||||
|
||||
try
|
||||
shuttingDown = false
|
||||
generateInstanceId()
|
||||
initializeHeartbeatStream()
|
||||
scheduleHeartbeats()
|
||||
serviceActive = true
|
||||
log.infof("Instance heartbeat service started with ID: %s", instanceId)
|
||||
catch
|
||||
case ex: Exception =>
|
||||
serviceActive = false
|
||||
log.errorf(ex, "Failed to start instance heartbeat service")
|
||||
if coordinatorEnabled then
|
||||
try
|
||||
shuttingDown = false
|
||||
generateInstanceId()
|
||||
initializeHeartbeatStream()
|
||||
scheduleHeartbeats()
|
||||
serviceActive = true
|
||||
log.infof("Instance heartbeat service started with ID: %s", instanceId)
|
||||
catch
|
||||
case ex: Exception =>
|
||||
serviceActive = false
|
||||
log.errorf(ex, "Failed to start instance heartbeat service")
|
||||
else log.info("Coordinator support disabled via config; skipping heartbeat service startup")
|
||||
|
||||
def onShutdown(@Observes event: ShutdownEvent): Unit =
|
||||
shuttingDown = true
|
||||
|
||||
if !serviceActive then
|
||||
log.info("Instance heartbeat service stopped")
|
||||
return
|
||||
|
||||
try
|
||||
cleanup()
|
||||
serviceActive = false
|
||||
log.info("Instance heartbeat service stopped")
|
||||
catch
|
||||
case ex: Exception =>
|
||||
log.errorf(ex, "Error during heartbeat service shutdown")
|
||||
if serviceActive then
|
||||
try
|
||||
cleanup()
|
||||
serviceActive = false
|
||||
log.info("Instance heartbeat service stopped")
|
||||
catch
|
||||
case ex: Exception =>
|
||||
log.errorf(ex, "Error during heartbeat service shutdown")
|
||||
else log.info("Instance heartbeat service stopped")
|
||||
|
||||
def setRedisPrefix(prefix: String): Unit =
|
||||
redisPrefix = prefix
|
||||
@@ -92,18 +88,16 @@ class InstanceHeartbeatService:
|
||||
localCacheSize = count
|
||||
|
||||
def addGameSubscription(gameId: String): Unit =
|
||||
if !coordinatorEnabled then return
|
||||
|
||||
val setKey = s"$redisPrefix:instance:$instanceId:games"
|
||||
redis.set(classOf[String]).sadd(setKey, gameId)
|
||||
subscriptionCount += 1
|
||||
if coordinatorEnabled then
|
||||
val setKey = s"$redisPrefix:instance:$instanceId:games"
|
||||
redis.set(classOf[String]).sadd(setKey, gameId)
|
||||
subscriptionCount += 1
|
||||
|
||||
def removeGameSubscription(gameId: String): Unit =
|
||||
if !coordinatorEnabled then return
|
||||
|
||||
val setKey = s"$redisPrefix:instance:$instanceId:games"
|
||||
redis.set(classOf[String]).srem(setKey, gameId)
|
||||
subscriptionCount = Math.max(0, subscriptionCount - 1)
|
||||
if coordinatorEnabled then
|
||||
val setKey = s"$redisPrefix:instance:$instanceId:games"
|
||||
redis.set(classOf[String]).srem(setKey, gameId)
|
||||
subscriptionCount = Math.max(0, subscriptionCount - 1)
|
||||
|
||||
private def generateInstanceId(): Unit =
|
||||
val hostname =
|
||||
@@ -137,7 +131,6 @@ class InstanceHeartbeatService:
|
||||
streamObserver = None
|
||||
|
||||
private def scheduleHeartbeats(): Unit =
|
||||
// Send heartbeat every 200ms
|
||||
heartbeatExecutor.scheduleAtFixedRate(
|
||||
() => sendHeartbeat(),
|
||||
0,
|
||||
@@ -145,7 +138,6 @@ class InstanceHeartbeatService:
|
||||
TimeUnit.MILLISECONDS,
|
||||
)
|
||||
|
||||
// Refresh Redis TTL every 2s
|
||||
redisHeartbeatExecutor.scheduleAtFixedRate(
|
||||
() => refreshRedisHeartbeat(),
|
||||
0,
|
||||
|
||||
Reference in New Issue
Block a user