mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 20:55:44 +00:00
Avoid Postgres connection on cached state reads (#8719)
This commit is contained in:
@@ -23,6 +23,7 @@ import net.eagle0.shardok.storage.action_result.ActionResult as ShardokActionRes
|
||||
class PostgresHistory private[service] (
|
||||
private val gameId: GameId,
|
||||
initialCount: Int,
|
||||
initialStateCache: Map[Int, GameState] = Map.empty,
|
||||
initialShardokResultCounts: Map[ShardokGameId, Int] = Map.empty,
|
||||
initialShardokGameStates: Map[ShardokGameId, com.google.protobuf.ByteString] = Map.empty,
|
||||
initialShardokLiveBattleCaches: Map[ShardokGameId, ShardokLiveBattleCache] = Map.empty,
|
||||
@@ -36,7 +37,7 @@ class PostgresHistory private[service] (
|
||||
private var cachedCount: Int = initialCount
|
||||
private val dbLock = new Object
|
||||
private val actionCache = mutable.Map.empty[Int, ActionResultT]
|
||||
private val stateCache = mutable.Map.empty[Int, GameState]
|
||||
private val stateCache = mutable.Map.from(initialStateCache)
|
||||
// Shardok reconnects need these values while assembling a subscription. Keep them synchronized with accepted
|
||||
// write-behind updates so reconnect setup never waits for the shared Postgres connection pool.
|
||||
private val shardokResultCounts = mutable.Map.from(initialShardokResultCounts)
|
||||
@@ -338,7 +339,7 @@ class PostgresHistory private[service] (
|
||||
.getOrElse(Vector.empty)
|
||||
}
|
||||
|
||||
override def stateAfter(count: Int): GameState = withDbLock {
|
||||
override def stateAfter(count: Int): GameState = withLock {
|
||||
if count < 0 || count > cachedCount then
|
||||
throw new EagleInternalException(s"stateAfter($count) out of range; count is $cachedCount")
|
||||
withTutorialState(JfrEvents.postgresStateReplay("postgres-history", count) { event =>
|
||||
@@ -351,16 +352,18 @@ class PostgresHistory private[service] (
|
||||
state
|
||||
}
|
||||
.getOrElse {
|
||||
val (startBoundary, startState) =
|
||||
bestCachedStateAtOrBefore(count).getOrElse(readLatestSnapshotAtOrBefore(count))
|
||||
event.snapshotBoundary = startBoundary
|
||||
event.replayActionCount = count - startBoundary
|
||||
event.snapshotHit = startBoundary == count
|
||||
if startBoundary == count then startState
|
||||
else {
|
||||
val state = replayStateFrom(startBoundary, startState, endExclusiveSeq = count)
|
||||
stateCache(count) = state
|
||||
state
|
||||
withConnection {
|
||||
val (startBoundary, startState) =
|
||||
bestCachedStateAtOrBefore(count).getOrElse(readLatestSnapshotAtOrBefore(count))
|
||||
event.snapshotBoundary = startBoundary
|
||||
event.replayActionCount = count - startBoundary
|
||||
event.snapshotHit = startBoundary == count
|
||||
if startBoundary == count then startState
|
||||
else {
|
||||
val state = replayStateFrom(startBoundary, startState, endExclusiveSeq = count)
|
||||
stateCache(count) = state
|
||||
state
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -7,6 +7,10 @@ import java.util.concurrent.atomic.AtomicReference
|
||||
import scala.jdk.CollectionConverters.*
|
||||
|
||||
import com.google.protobuf.ByteString
|
||||
import net.eagle0.eagle.model.state.{GameType, RoundPhase}
|
||||
import net.eagle0.eagle.model.state.date.Date
|
||||
import net.eagle0.eagle.model.state.game_state.{EagleMapInfo, GameState}
|
||||
import net.eagle0.eagle.model.state.run_status.RunStatus
|
||||
import net.eagle0.shardok.api.action_result_view.ActionResultView as ShardokActionResultView
|
||||
import net.eagle0.shardok.api.command_descriptor.AvailableCommands as ShardokAvailableCommands
|
||||
import net.eagle0.shardok.storage.action_result.ActionResult as ShardokActionResult
|
||||
@@ -301,6 +305,41 @@ class PostgresHistorySchemaBatchTest extends AnyFlatSpec with Matchers with Mock
|
||||
history.shardokPlayerAvailableCommands("battle", 1) shouldBe Some(availableCommand)
|
||||
}
|
||||
|
||||
"PostgresHistory state cache" should "serve an exact hit without opening a database connection" in {
|
||||
val cachedState = GameState(
|
||||
gameId = 0x1234L,
|
||||
currentRoundId = 7,
|
||||
currentPhase = RoundPhase.PlayerCommands,
|
||||
currentDate = Date(1, Date.Month.January),
|
||||
actionResultCount = 1,
|
||||
provinces = Map.empty,
|
||||
heroes = Map.empty,
|
||||
battalions = Map.empty,
|
||||
destroyedBattalions = Map.empty,
|
||||
factions = Map.empty,
|
||||
factionCommandCounts = Map.empty,
|
||||
killedHeroes = Map.empty,
|
||||
destroyedFactions = Map.empty,
|
||||
outstandingBattles = Vector.empty,
|
||||
battleCounter = 0,
|
||||
deferredNotifications = Vector.empty,
|
||||
runStatus = RunStatus.Running,
|
||||
victor = None,
|
||||
battalionTypes = Vector.empty,
|
||||
randomSeed = 42L,
|
||||
chronicleEntries = Vector.empty,
|
||||
gameType = GameType.Normal,
|
||||
eagleMap = EagleMapInfo(0, 0, "", "")
|
||||
)
|
||||
val history = new PostgresHistory(
|
||||
gameId = cachedState.gameId,
|
||||
initialCount = 1,
|
||||
initialStateCache = Map(1 -> cachedState)
|
||||
)
|
||||
|
||||
history.stateAfter(1) shouldBe cachedState
|
||||
}
|
||||
|
||||
"PostgresHistory Shardok write-behind" should "publish accepted updates before a blocked database write completes" in {
|
||||
val writeStarted = new CountDownLatch(1)
|
||||
val allowWrite = new CountDownLatch(1)
|
||||
|
||||
Reference in New Issue
Block a user