Instrument Shardok command latency in Eagle (#8763)

This commit is contained in:
2026-07-23 21:01:43 -07:00
committed by GitHub
parent e371368e0b
commit 912f53c42d
4 changed files with 166 additions and 41 deletions
@@ -255,6 +255,31 @@ class EagleShardokClientCommandReceivedEvent extends Event {
var requestBytes: Int = 0
}
@Name("net.eagle0.eagle.ShardokPlacementCommandsReceived")
@Label("Eagle Shardok Placement Commands Received")
@Category(Array("Eagle0", "Shardok"))
@Enabled(true)
@StackTrace(false)
class EagleShardokPlacementCommandsReceivedEvent extends Event {
@Label("Eagle Game ID")
var eagleGameId: String = ""
@Label("Shardok Game ID")
var shardokGameId: String = ""
@Label("Player ID")
var playerId: Int = 0
@Label("Token")
var token: Long = 0L
@Label("Placement Count")
var placementCount: Int = 0
@Label("Request Bytes")
var requestBytes: Int = 0
}
object JfrEvents {
private val usePostgresQueryEvents = new ThreadLocal[Boolean]()
@@ -483,4 +508,24 @@ object JfrEvents {
event.commit()
}
}
def shardokPlacementCommandsReceived(
eagleGameId: => String,
shardokGameId: => String,
playerId: Int,
token: Long,
placementCount: Int,
requestBytes: => Int
): Unit = {
val event = new EagleShardokPlacementCommandsReceivedEvent()
if event.isEnabled then {
event.eagleGameId = eagleGameId
event.shardokGameId = shardokGameId
event.playerId = playerId
event.token = token
event.placementCount = placementCount
event.requestBytes = requestBytes
event.commit()
}
}
}
@@ -117,7 +117,7 @@ class EagleServiceImpl(
request: PostCommandRequest
): Future[PostCommandResponse] = {
request.command match {
case ShardokCommand(shardokGameId, playerId, shardokToken, index, roll, _) =>
case ShardokCommand(shardokGameId, playerId, shardokToken, index, roll, _) =>
JfrEvents.shardokClientCommandReceived(
eagleGameId = request.gameId.toHexString,
shardokGameId = shardokGameId,
@@ -127,7 +127,16 @@ class EagleServiceImpl(
roll = roll,
requestBytes = request.serializedSize
)
case _ => ()
case ShardokPlacementCommands(shardokGameId, playerId, commands, shardokToken, _) =>
JfrEvents.shardokPlacementCommandsReceived(
eagleGameId = request.gameId.toHexString,
shardokGameId = shardokGameId,
playerId = playerId,
token = shardokToken,
placementCount = commands.size,
requestBytes = request.serializedSize
)
case _ => ()
}
lockAndDoWithUserId { uid =>
@@ -1942,29 +1942,46 @@ class GamesManager(
roll: Option[Int]
): Unit = {
requireLoadedGame(eagleGameId)
this.synchronized {
val eagleFactionId = requireShardokCommandAllowed(
userId = userId,
eagleGameId = eagleGameId,
shardokGameId = shardokGameId,
shardokPlayerId = shardokPlayerId
)
SimpleTimedLogger
.fileLogger(eagleGameId, "timings")
.withStopwatch("postShardokCommand") {
shardokClient.postCommand(
eagleGameId = eagleGameId,
shardokGameId = shardokGameId,
shardokPlayerId = shardokPlayerId,
token = shardokToken,
index = index,
roll = roll,
eagleFactionId = eagleFactionId
)
JfrEvents.postActionPhase(eagleGameId.toHexString, "gamesManager.postShardokCommand.total") { totalEvent =>
totalEvent.itemCount = 1
this.synchronized {
JfrEvents.postActionPhase(eagleGameId.toHexString, "gamesManager.postShardokCommand.criticalSection") {
criticalSectionEvent =>
criticalSectionEvent.itemCount = 1
val eagleFactionId = requireShardokCommandAllowed(
userId = userId,
eagleGameId = eagleGameId,
shardokGameId = shardokGameId,
shardokPlayerId = shardokPlayerId
)
criticalSectionEvent.factionId = eagleFactionId
SimpleTimedLogger
.fileLogger(eagleGameId, "timings")
.withStopwatch("postShardokCommand") {
JfrEvents.postActionPhase(
eagleGameId.toHexString,
"gamesManager.postShardokCommand.rpc",
factionId = eagleFactionId
) { rpcEvent =>
rpcEvent.itemCount = 1
shardokClient.postCommand(
eagleGameId = eagleGameId,
shardokGameId = shardokGameId,
shardokPlayerId = shardokPlayerId,
token = shardokToken,
index = index,
roll = roll,
eagleFactionId = eagleFactionId
)
}
}
// Record last played time for this user
updateLastPlayed(eagleGameId, userId)
save()
totalEvent.factionId = eagleFactionId
()
}
// Record last played time for this user
updateLastPlayed(eagleGameId, userId)
save()
}
}
}
@@ -1977,23 +1994,40 @@ class GamesManager(
shardokToken: Long
): Unit = {
requireLoadedGame(eagleGameId)
this.synchronized {
val eagleFactionId = requireShardokCommandAllowed(
userId = userId,
eagleGameId = eagleGameId,
shardokGameId = shardokGameId,
shardokPlayerId = shardokPlayerId
)
shardokClient.postPlacementCommands(
eagleGameId = eagleGameId,
shardokGameId = shardokGameId,
shardokPlayerId = shardokPlayerId,
placementCommands = placementCommands.map { pc =>
PlacementCommand(unitId = pc.unitId, row = pc.row, column = pc.column)
},
token = shardokToken,
eagleFactionId = eagleFactionId
)
JfrEvents.postActionPhase(eagleGameId.toHexString, "gamesManager.postPlacementCommands.total") { totalEvent =>
totalEvent.itemCount = placementCommands.size
this.synchronized {
JfrEvents.postActionPhase(eagleGameId.toHexString, "gamesManager.postPlacementCommands.criticalSection") {
criticalSectionEvent =>
criticalSectionEvent.itemCount = placementCommands.size
val eagleFactionId = requireShardokCommandAllowed(
userId = userId,
eagleGameId = eagleGameId,
shardokGameId = shardokGameId,
shardokPlayerId = shardokPlayerId
)
criticalSectionEvent.factionId = eagleFactionId
JfrEvents.postActionPhase(
eagleGameId.toHexString,
"gamesManager.postPlacementCommands.rpc",
factionId = eagleFactionId
) { rpcEvent =>
rpcEvent.itemCount = placementCommands.size
shardokClient.postPlacementCommands(
eagleGameId = eagleGameId,
shardokGameId = shardokGameId,
shardokPlayerId = shardokPlayerId,
placementCommands = placementCommands.map { pc =>
PlacementCommand(unitId = pc.unitId, row = pc.row, column = pc.column)
},
token = shardokToken,
eagleFactionId = eagleFactionId
)
}
totalEvent.factionId = eagleFactionId
()
}
}
}
}
@@ -102,6 +102,43 @@ class JfrEventsTest extends AnyFlatSpec with Matchers {
}
}
"shardokPlacementCommandsReceived" should "record placement command arrival metadata" in {
val recordingPath = Files.createTempFile("eagle-shardok-placement-commands", ".jfr")
val recording = new Recording()
try {
recording.enable("net.eagle0.eagle.ShardokPlacementCommandsReceived")
recording.start()
JfrEvents.shardokPlacementCommandsReceived(
eagleGameId = "eagle-game-123",
shardokGameId = "shardok-game-456",
playerId = 7,
token = 42L,
placementCount = 6,
requestBytes = 12345
)
recording.stop()
recording.dump(recordingPath)
val event = RecordingFile
.readAllEvents(recordingPath)
.asScala
.find(_.getEventType.getName == "net.eagle0.eagle.ShardokPlacementCommandsReceived")
.getOrElse(fail("ShardokPlacementCommandsReceived event was not recorded"))
event.getString("eagleGameId") shouldBe "eagle-game-123"
event.getString("shardokGameId") shouldBe "shardok-game-456"
event.getInt("playerId") shouldBe 7
event.getLong("token") shouldBe 42L
event.getInt("placementCount") shouldBe 6
event.getInt("requestBytes") shouldBe 12345
event.getStackTrace shouldBe null
} finally {
recording.close()
Files.deleteIfExists(recordingPath): Unit
}
}
"historyAppendPhase" should "record phase metadata and counts" in {
val recordingPath = Files.createTempFile("eagle-history-append-phase", ".jfr")
val recording = new Recording()