Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add streams support in Generic Journey #279

Merged
merged 5 commits into from
Aug 11, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import io.gatling.core.structure.{
}
import io.gatling.http.Predef._
import io.gatling.http.protocol.HttpProtocolBuilder
import io.gatling.http.request.builder.HttpRequestBuilder
import org.kibanaLoadTest.KibanaConfiguration
import org.kibanaLoadTest.helpers.Helper
import org.kibanaLoadTest.helpers.HttpHelper
Expand All @@ -25,22 +26,40 @@ import scala.util.Using

object ApiCall {
def execute(
request: mapping.Request,
requests: List[mapping.Request],
config: KibanaConfiguration
): ChainBuilder = {
val defaultHeaders = request.headers.-(
// Workaround for https://github.com/gatling/gatling/issues/3783
val httpParentRequest = httpRequest(requests(0).http, config)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using array indices we could also use .head and .tail which would improve readability IMHO.

val children = requests.drop(1);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: You can use .tail here.

if (children.isEmpty) {
exec(httpParentRequest)
} else {
val childHttpRequests: Seq[HttpRequestBuilder] =
(children.map(request => httpRequest(request.http, config))).toSeq
exec(httpParentRequest.resources(childHttpRequests: _*))
}
}

def httpRequest(
request: mapping.Http,
config: KibanaConfiguration
): HttpRequestBuilder = {
val excludeHeaders = List(
"Content-Length",
"Kbn-Version",
"Traceparent",
"Authorization",
"Cookie",
"X-Kbn-Context"
)
val defaultHeaders = request.headers.--(excludeHeaders.iterator)

val headers =
if (request.headers.contains("Kbn-Version"))
defaultHeaders + ("Kbn-Version" -> config.version)
else defaultHeaders
val httpRequestBuilder = request.method match {
request.method match {
case "GET" =>
http(requestName = s"${request.method} ${request.path}")
.get(request.path)
Expand All @@ -66,7 +85,6 @@ object ApiCall {
case _ =>
throw new IllegalArgumentException(s"Invalid method ${request.method}")
}
exec(httpRequestBuilder)
}
}

Expand All @@ -87,20 +105,19 @@ class GenericJourney extends Simulation {
config: KibanaConfiguration
): ChainBuilder = {
var steps: ChainBuilder = exec()
var priorRequest: Option[mapping.Request] =
var priorStream: Option[mapping.RequestStream] =
Option.empty
for (trace <- journey.requests) {
val request = trace.request
val priorTimestamp =
priorRequest.map(_.timestamp).getOrElse(request.timestamp)
for (stream <- journey.streams) {
val priorDate =
priorStream.map(_.endTime).getOrElse(stream.endTime)
val pauseDuration =
Math.max(0L, request.timestamp.getTime - priorTimestamp.getTime)
Math.max(0L, stream.startTime.getTime - priorDate.getTime)
if (pauseDuration > 0L) {
steps = steps.pause(pauseDuration.toString, TimeUnit.MILLISECONDS)
}
Comment on lines 114 to 118
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope that using previous stream end time (point in time when the last request was finished) and the next stream start time (point in time when the first request was started) difference is valid for pause between requests?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable to me.


steps = steps.exec(ApiCall.execute(request, config))
priorRequest = Option(request)
steps = steps.exec(ApiCall.execute(stream.requests, config))
priorStream = Option(stream)
}
steps
}
Expand Down Expand Up @@ -162,7 +179,11 @@ class GenericJourney extends Simulation {
private val httpProtocol: HttpProtocolBuilder =
new HttpHelper(config).getProtocol
.baseUrl(config.baseUrl)
// Gatling automatically follow redirects in case of 301, 302, 303, 307 or 308 response status code
// Disabling this behavior since we run the defined sequence of requests
.disableFollowRedirect
// Mimic real Chrome browser
.maxConnectionsPerHostLikeChrome
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gatling should allow up to 6 connections per host name by default, but I thought it is better to use Chrome value for visibility. But it basically changes nothing https://github.com/gatling/gatling/blob/045603bc5c9d4466cb97d1e54902451f9b18dadd/gatling-http/src/main/scala/io/gatling/http/protocol/HttpProtocolBuilder.scala#L108

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a good idea but I wonder whether we should use it as it's a deprecated property?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Funny I missed that comment. I will just remove it, let's use the default value

private val steps = scenarioSteps(journey, config)
private val warmupScenario = scenarioForStage(
steps,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package org.kibanaLoadTest.simulation.generic.mapping

import spray.json.DefaultJsonProtocol

case class Http(
path: String,
headers: Map[String, String],
method: String,
body: Option[String],
statusCode: Int
)

object HttpJsonProtocol extends DefaultJsonProtocol {
implicit val httpFormat = jsonFormat5(Http)
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
package org.kibanaLoadTest.simulation.generic.mapping

import ScalabilitySetupJsonProtocol._
import TraceJsonProtocol._
import org.kibanaLoadTest.simulation.generic.mapping.RequestStreamJsonProtocol._
import spray.json.DefaultJsonProtocol

case class Journey(
journeyName: String,
kibanaVersion: String,
scalabilitySetup: ScalabilitySetup,
requests: List[Trace]
streams: List[RequestStream]
)

object JourneyJsonProtocol extends DefaultJsonProtocol {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,14 @@
package org.kibanaLoadTest.simulation.generic.mapping

import org.kibanaLoadTest.helpers.Helper
import spray.json.{
DefaultJsonProtocol,
JsNumber,
JsString,
JsValue,
JsonFormat,
deserializationError
}
import spray.json.{DefaultJsonProtocol, JsNumber, JsString, JsValue, JsonFormat, RootJsonFormat, deserializationError}

import java.util.Date
import org.kibanaLoadTest.simulation.generic.mapping.HttpJsonProtocol._

case class Request(
path: String,
headers: Map[String, String],
method: String,
body: Option[String],
statusCode: Int,
timestamp: Date
http: Http,
date: Date
)

object DateJsonProtocol extends DefaultJsonProtocol {
Expand All @@ -36,5 +26,5 @@ object DateJsonProtocol extends DefaultJsonProtocol {
import org.kibanaLoadTest.simulation.generic.mapping.DateJsonProtocol._

object RequestJsonProtocol extends DefaultJsonProtocol {
implicit val requestFormat = jsonFormat6(Request)
implicit val requestFormat: RootJsonFormat[Request] = jsonFormat2(Request)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.kibanaLoadTest.simulation.generic.mapping

import spray.json.DefaultJsonProtocol
import org.kibanaLoadTest.simulation.generic.mapping.RequestJsonProtocol._
import org.kibanaLoadTest.simulation.generic.mapping.DateJsonProtocol._

import java.util.Date

case class RequestStream(
startTime: Date,
endTime: Date,
requests: List[Request]
)

object RequestStreamJsonProtocol extends DefaultJsonProtocol {
implicit val requestStreamFormat = jsonFormat3(RequestStream)
}

This file was deleted.