Hi,
I’m trying to implement a scenario where a user represents a replay of a number of messages on a websocket. The messages that the user sends to the websocket are stored in a number of files, one message per line, e.g.
foo.log
{"foo":"bar"}
...
{"foo":"box"}
There are five of such files, each is a different seed template for a given gatling session.
So I wrote some extensions to websocket classes.
The first adds a sendReplay
method to the Ws
class.
object WsOps {
implicit class WsSendReplay(ws: Ws) {
def sendReplay(replayId: Expression[String],
replacementId: Expression[String]):WsSendBuilder = {
new EventReplaySendBuilder(
replacementId,
"replay",
_ => Success(TextMessage("")), // unused
None, // unused
replayId,
replacementId
)
}
}
}
The replayId is the filename for the template (foo for the example shown above). The replacementId is a random int that will replace the replayId when replaying the messages. Now calling ws("").sendReplay will give back a EventReplaySendBuilder…
class EventReplaySendBuilder(
requestName: Expression[String],
wsName: String,
message: Expression[WsMessage],
checkBuilder: Option[AsyncCheckBuilder],
replayEventId: Expression[String],
replacementId: Expression[String]
) extends WsSendBuilder(requestName, wsName, message, checkBuilder) {
private val log = LoggerFactory.getLogger(getClass)
override def check(checkBuilder: AsyncCheckBuilder): WsSendBuilder = {
new EventReplaySendBuilder(requestName, wsName, message, Some(checkBuilder), replayEventId, replacementId)
}
override def build(ctx: ScenarioContext, next: Action): Action = {
new EventReplaySend(requestName, wsName, message, checkBuilder,
ctx.coreComponents.statsEngine, next, replayEventId, replacementId)
}
}
The next class extends WsSendBuilder
class EventReplaySend(
override val requestName: Expression[String],
wsName: String,
message: Expression[WsMessage],
checkBuilder: Option[AsyncCheckBuilder],
statsEngine: StatsEngine,
override val next: Action,
replayEventId: Expression[String],
replacementId: Expression[String]
) extends WsSend(requestName, wsName, message, checkBuilder, statsEngine, next) {
private val log = LoggerFactory.getLogger(getClass)
def eventReplay(sourceId: String, newId: String): List[(String, Long)] = {
val sourceFile = Paths.get(GatlingFiles.dataDirectory.toAbsolutePath.toString, "itf", sourceId)
log.warn(s"event replay $sourceId $newId ${GatlingFiles.dataDirectory.toAbsolutePath.toString}")
val replayLines = Source.fromFile(sourceFile.toUri).getLines().toList
val first = replayLines.head
val second = replayLines.tail.head
replayLines.drop(1).zip(replayLines.drop(2)).foldLeft(
List((first, timeDifference(first, second)))
) { case (messageChain, (last, nextMessage)) =>
(nextMessage.replaceAllLiterally(sourceId, newId), timeDifference(last, nextMessage)) :: messageChain
}.reverse
}
override def sendRequest(requestName: String, session: Session): Validation[Unit] = {
log.debug(s"Send request called $requestName")
for {
wsActor <- fetchActor(wsName, session)
messageSource <- replayEventId(session)
replacementId <- replacementId(session)
} yield {