Record Shardok client command arrivals in JFR (#8749)

This commit is contained in:
2026-07-21 09:18:34 -07:00
committed by GitHub
parent 68873cc219
commit e5be07b3b8
4 changed files with 112 additions and 2 deletions
@@ -224,6 +224,37 @@ class EaglePostActionPhaseEvent extends Event {
var succeeded: Boolean = false
}
@Name("net.eagle0.eagle.ShardokClientCommandReceived")
@Label("Eagle Shardok Client Command Received")
@Category(Array("Eagle0", "Shardok"))
@Enabled(true)
@StackTrace(false)
class EagleShardokClientCommandReceivedEvent 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("Command Index")
var commandIndex: Int = 0
@Label("Has Roll")
var hasRoll: Boolean = false
@Label("Roll")
var roll: Int = 0
@Label("Request Bytes")
var requestBytes: Int = 0
}
object JfrEvents {
private val usePostgresQueryEvents = new ThreadLocal[Boolean]()
@@ -429,4 +460,27 @@ object JfrEvents {
}
} else f(event)
}
def shardokClientCommandReceived(
eagleGameId: => String,
shardokGameId: => String,
playerId: Int,
token: Long,
commandIndex: Int,
roll: Option[Int],
requestBytes: => Int
): Unit = {
val event = new EagleShardokClientCommandReceivedEvent()
if event.isEnabled then {
event.eagleGameId = eagleGameId
event.shardokGameId = shardokGameId
event.playerId = playerId
event.token = token
event.commandIndex = commandIndex
event.hasRoll = roll.isDefined
event.roll = roll.getOrElse(0)
event.requestBytes = requestBytes
event.commit()
}
}
}
@@ -151,6 +151,7 @@ scala_library(
"//src/main/protobuf/net/eagle0/eagle/api:eagle_scala_grpc",
"//src/main/protobuf/net/eagle0/eagle/common:tutorial_phase_scala_proto",
"//src/main/scala/net/eagle0/common:functional_random",
"//src/main/scala/net/eagle0/common:jfr_events",
"//src/main/scala/net/eagle0/common:simple_timed_logger",
"//src/main/scala/net/eagle0/eagle:eagle_pkg",
"//src/main/scala/net/eagle0/eagle:user_id",
@@ -8,7 +8,7 @@ import scala.util.hashing.MurmurHash3
import io.grpc.{Context, Status, StatusRuntimeException}
import io.grpc.stub.StreamObserver
import io.sentry.Sentry
import net.eagle0.common.{FunctionalRandom, RandomState, SimpleTimedLogger}
import net.eagle0.common.{FunctionalRandom, JfrEvents, RandomState, SimpleTimedLogger}
import net.eagle0.eagle.{GameId, UserId}
import net.eagle0.eagle.api.eagle.*
import net.eagle0.eagle.api.eagle.EagleGrpc.Eagle
@@ -115,7 +115,21 @@ class EagleServiceImpl(
def postCommand(
request: PostCommandRequest
): Future[PostCommandResponse] =
): Future[PostCommandResponse] = {
request.command match {
case ShardokCommand(shardokGameId, playerId, shardokToken, index, roll, _) =>
JfrEvents.shardokClientCommandReceived(
eagleGameId = request.gameId.toHexString,
shardokGameId = shardokGameId,
playerId = playerId,
token = shardokToken,
commandIndex = index,
roll = roll,
requestBytes = request.serializedSize
)
case _ => ()
}
lockAndDoWithUserId { uid =>
request.command match {
case ShardokCommand(
@@ -194,6 +208,7 @@ class EagleServiceImpl(
)
PostCommandResponse(status = PostCommandResponse.Status.BAD_TOKEN, gameId = request.gameId)
}
}
private class SyncStreamObserver(
responseObserver: SyncResponseObserver
@@ -62,6 +62,46 @@ class JfrEventsTest extends AnyFlatSpec with Matchers {
metadataEvaluated shouldBe false
}
"shardokClientCommandReceived" should "record command arrival metadata" in {
val recordingPath = Files.createTempFile("eagle-shardok-client-command", ".jfr")
val recording = new Recording()
try {
recording.enable("net.eagle0.eagle.ShardokClientCommandReceived")
recording.start()
JfrEvents.shardokClientCommandReceived(
eagleGameId = "eagle-game-123",
shardokGameId = "shardok-game-456",
playerId = 7,
token = 42L,
commandIndex = 3,
roll = Some(19),
requestBytes = 12345
)
recording.stop()
recording.dump(recordingPath)
val event = RecordingFile
.readAllEvents(recordingPath)
.asScala
.find(_.getEventType.getName == "net.eagle0.eagle.ShardokClientCommandReceived")
.getOrElse(fail("ShardokClientCommandReceived 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("commandIndex") shouldBe 3
event.getBoolean("hasRoll") shouldBe true
event.getInt("roll") shouldBe 19
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()