Gatling switch protocols during scenario - gatling

I'm trying to create a Gatling scenario which requires switching the protocol to a different host during the test. The user journey is
https://example.com/page1
https://example.com/page2
https://accounts.example.com/signin
https://example.com/page3
so as part of a single scenario, I need to ether switch the protocol defined in the scenario set up, or switch the baseUrl defined on the protocol but I can't figure out how to do that.
A basic scenario might look like
package protocolexample
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class Example extends Simulation {
val exampleHttp = http.baseURL("https://example.com/")
val exampleAccountsHttp = http.baseURL("https://accounts.example.com/")
val scn = scenario("Signin")
.exec(
http("Page 1").get("/page1")
)
.exec(
http("Page 2").get("/page2")
)
.exec(
// This needs to be done against accounts.example.com
http("Signin").get("/signin")
)
.exec(
// Back to example.com
http("Page 3").get("/page3")
)
setUp(
scn.inject(
atOnceUsers(3)
).protocols(exampleHttp)
)
}
I just need to figure out how to ether switch the host or protocol for the 3rd step. I know I can create multiple scenarios but this needs to be a single user flow across multiple hosts.
I've tried directly using the other protocol
exec(
// This needs to be done against accounts.example.com
exampleAccountsHttp("Signin").get("/signin")
)
which results in
protocolexample/example.scala:19: type mismatch;
found : String("Signin")
required: io.gatling.core.session.Session
exampleAccountsHttp("Signin").get("/signin")
and also changing the base URL on the request
exec(
// This needs to be done against accounts.example.com
http("Signin").baseUrl("https://accounts.example.com/").get("/signin")
)
which results in
protocolexample/example.scala:19: value baseUrl is not a member of io.gatling.http.request.builder.Http

You can use an absolute URI (inclusive protocol) as a parameter for Http.get, Http.post and so on.
class Example extends Simulation {
val exampleHttp = http.baseURL("https://example.com/")
val scn = scenario("Signin")
.exec(http("Page 1").get("/page1"))
.exec(http("Page 2").get("/page2"))
.exec(http("Signin").get("https://accounts.example.com/signin"))
.exec(http("Page 3").get("/page3"))
setUp(scn.inject(atOnceUsers(3))
.protocols(exampleHttp))
}
see: https://gatling.io/docs/current/cheat-sheet/#http-protocol-urls-baseUrl
baseURL: Sets the base URL of all relative URLs of the scenario on which the configuration is applied.

I am currently working in gatling.
SOLUTION:
=> WHAT WE USE IF WE HAVE ONE HTTPBASE:
var httpConf1= http.baseUrl("https://s1.com")
=>FOR MULTIPLE HTTPBASE:
var httpConf1=http.**baseUrls**("https://s1.com","https://s2.com","https://s3.com")
You can use baseUrls function which takes a list of httpBase urls.
ANOTHER METHOD:
Reading from csv file all httpbase and storing it in list buffer in scala language and then converting it into list while passing it in http.baseUrls
var listOfTenants:ListBuffer[String] = new ListBuffer[String] //buffer
var httpConf1=http.**baseUrls**(listOfTenants.toList) //converting buffer to list.
Hope it helps!.
Thank you.😊

Related

scala - Gatling - I can't seem to use Session Variables stored from a request in a subsequent Request

The code:
package simulations
import io.gatling.core.Predef._
import io.gatling.http.Predef._
class StarWarsBasicExample extends Simulation
{
// 1 Http Conf
val httpConf = http.baseUrl("https://swapi.dev/api/films/")
// 2 Scenario Definition
val scn = scenario("Star Wars API")
.exec(http("Get Number")
.get("4")
.check(jsonPath("$.episode_id")
.saveAs("episodeId"))
)
.exec(session => {
val movie = session("episodeId").as[String]
session.set("episode",movie)
}).pause(4)
.exec(http("$episode")
.get("$episode"))
// 3 Load Scenario
setUp(
scn.inject(atOnceUsers(1)))
.protocols(httpConf)
}
Trying to grab a variable from the first Get request, and inject that variable into a second request, but unable to do so despite using the documentation. There might be something I'm not understanding.
When I use breakpoints, and navigate through the process, it appears the session execution happens AFTER both of the other requests have been completed (by which time is too late). Can't seem to make that session execution happen between the two requests.
Already answered on Gatling's community mailing list.
"$episode" is not correct Gatling Expression Language syntax. "${episode}" is correct.

Configuring additional properties within SnowflakeBasicDataSource

I'm using following class in Java. But this class has limited implementation of properties.
SnowflakeBasicDataSource basicDataSource = new SnowflakeBasicDataSource();
basicDataSource.setSsl(true);
basicDataSource.setUser(dbSnowFlakeUsername);
basicDataSource.setPassword(dbSnowFlakePassword);
basicDataSource.setUrl(dbSnowFlakeUrl);
basicDataSource.setLoginTimeout(dbSnowFlakeLoginTimeoutSeconds);
For example i want to indicate networkTimeout and queryTimeout. But this is not implemented
How do i pass it to SnowflakeBasicDataSource?
Tried to pass withing url like this
jdbc:snowflake://ni31094.eu-central-1.snowflakecomputing.com/?db=TEST_DB&schema=PUBLIC&role=SYSADMIN&warehouse=TEST_WAREHOUSE&tracing=FINE&networkTimeout=10&queryTimeout=10
But don't think it works.
Please need assistance.
The two parameters for Network and Query Timeout are:
Network Timeout: networkTimeout
Query Timeout: queryTimeout=<number>
Looking at your string: Yes, you are using the two parameters correctly but I see your user and password-parameters are missing.
You can also try to:
ensure your properties are set correctly
adjust your connection string and accountinformation-values
You can find some more detail information about troubleshooting here: https://docs.snowflake.com/en/user-guide/jdbc-configure.html#troubleshooting-tips
Setting the properties in the URL for the SnowflakeBasicDataSource does work. I tested the queryTimeout with a long-running query and the query correctly get cancelled after the specified time in the parameter.
Testing networkTimeout is a little more difficult since it's hard to tell how it is actually used. It is used by net.snowflake.client.jdbc.RestRequest and I've tested that the correct parameter gets passed through, and it does. The reason you're getting a timeout of 60 seconds is that the HTTP request for the initial login request gets set to that by default. The initial login request seems to ignore the networkTimeout. The request which contains the query to run gets correctly set to the networkTimeout parameter passed in through the query string. Since my java skills aren't great I was unable to test a situation where the networkTimeout causes an error, unfortunately.
Here is some scala code which shows you that the two params get correctly set in the session:
import net.snowflake.client.jdbc.{SnowflakeBasicDataSource, SnowflakeConnectionV1}
import java.sql.Statement
import java.io.FileReader
import java.util.Properties
object BasicConnector extends App{
val prop = new Properties
prop.load(new FileReader("~/snowflake_conn.properties"))
val username = prop.getProperty("username")
val password = prop.getProperty("password")
val url = prop.getProperty("url") + "?networkTimeout=54321&queryTimeout=1234"
val basicDataSource = new SnowflakeBasicDataSource()
basicDataSource.setSsl(true)
basicDataSource.setUser(username)
basicDataSource.setPassword(password)
basicDataSource.setUrl(url)
basicDataSource.setWarehouse("DEMO_WH")
val conn: SnowflakeConnectionV1 = basicDataSource.getConnection().asInstanceOf[SnowflakeConnectionV1]
val statement: Statement = conn.createStatement()
val queryTimeout = conn.getSfSession.getQueryTimeout
val networkTimeout = conn.getSfSession.getNetworkTimeoutInMilli
println(s"query timeout: $queryTimeout")
println(s"network timeout: $networkTimeout")
statement.close()
conn.close()
The above prints out:
query timeout: 1234
network timeout: 54321
As you can see, I had to cast the Connection object to a SnowflakeConnectionV1 and use the getSfSession method to inspect the params with .asInstanceOf[SnowflakeConnectionV1]. This is because the JDBC Connection type doesn't have this method. You shouldn't have to do this though if you don't care about inspecting the parameter, it'll still use them correctly.

Is there a way to source stream from query parameters with akka-http?

I know how to source stream from an entity via a POST request, but I want to be able to also create a source stream from the query parameters of a GET request.
I know i can get query parameters to a case class via a as[] directive, but it seems like a miss to have to wrap that in a source in order to source stream it.
The query parameters that are part of the URL are not "streamed" from the client, rather they are part of the request line. Therefore, when you have an HttpRequest object in your memory you have already consumed enough space to hold the query parameters. This means that you lose any back-pressure benefits from using a Source. I recommend analyzing why you want to create a Source in the first place...
If you absolutely have to create a Source out of the parameters then you can use the parameterSeq Directive:
val route =
parameterSeq { params : Seq[(String, String)] =>
val parameterSource : Source[(String, String), _] = Source(params)
}

akka-http: complete request with flow

Assume I have set up an arbitrarily complex Flow[HttpRequest, HttpResponse, Unit].
I can already use said flow to handle incoming requests with
Http().bindAndHandle(flow, "0.0.0.0", 8080)
Now I would like to add logging, leveraging some existing directive, like logRequestResult("my-service"){...}
Is there a way to combine this directive with my flow? I guess I am looking for another directive, something along the lines of
def completeWithFlow(flow: Flow): Route
Is this possible at all?
N.B.: logRequestResult is an example, my question applies to any Directive one might find useful.
Turns out one (and maybe the only) way is to wire and materialize a new flow, and feed the extracted request to it. Example below
val myFlow: Flow[HttpRequest, HttpResponse, NotUsed] = ???
val route =
get {
logRequestResult("my-service") {
extract(_.request) { req ⇒
val futureResponse = Source.single(req).via(myFlow).runWith(Sink.head)
complete(futureResponse)
}
}
}
Http().bindAndHandle(route, "127.0.0.1", 9000)
http://doc.akka.io/docs/akka/2.4.2/scala/http/routing-dsl/overview.html
Are you looking for route2HandlerFlow or Route.handlerFlow ?
I believe Route.handlerFlow will work based on implicits.
eg /
val serverBinding = Http().bindAndHandle(interface = "0.0.0.0", port = 8080,
handler = route2HandlerFlow(mainFlow()))

Client session storage in an akka-http server

In an akka-http service, how does one cache some information, per client session? This is not quite obvious in the docs. I would for example like to create an actor for each connection.
Where should I create the actor, and how do I get reference to it from inside my stages?
My service is bound something like this:
val serverSource: Source[Http.IncomingConnection, Future[Http.ServerBinding]] =
Http().bind(interface = bindAddress, port = bindPort)
val bindingFuture: Future[Http.ServerBinding] =
serverSource
.to(Sink.foreach { connection =>
connection.handleWithSyncHandler (requestHandler)
// seems like I should set up some session state storage here,
// such as my actor
})
.run()
...
and later on:
val packetProcessor: Flow[A, B, Unit] = Flow[A]
.map {
case Something =>
// can i use the actor here, or access my session state?
}
I suspect I'm probably misinterpreting the whole paradigm in trying to make this fit. I can't tell if there is anything built in or how much I need to implement manually.
I have found Agent to be a very convenient mechanism for concurrent caching.
Say, for example, you want to keep a running Set of all the remote addresses that you have been connected to. You can setup an Agent to store the values and a Flow to write to the cache:
import scala.concurrent.ExecutionContext.Implicits.global
import akka.agent.Agent
import scala.collection.immutable
val addressCache = Agent(immutable.Set.empty[java.net.InetSocketAddress])
import akka.stream.scaladsl.Flow
val cacheAddressFlow = Flow[IncomingConnection] map { conn =>
addressCache send (_ + conn.remoteAddress) //updates the cache
conn //forwards the connection to the rest of the stream
}
This Flow can then be made part of your Stream:
val bindingFuture: Future[Http.ServerBinding] =
serverSource.via(cacheAddressFlow)
.to(Sink.foreach { connection =>
connection.handleWithSyncHandler (requestHandler)
})
.run()
You can then "query" the cache completely outside of the binding logic:
def somewhereElseInTheCode = {
val currentAddressSet = addressCache.get
println(s"address count so far: ${currentAddressSet.size}")
}
If your goal is to send all IncomingConnection values to an Actor for processing then this can be accomplished with Sink.actorRef:
object ConnectionStreamTerminated
class ConnectionActor extends Actor {
override def receive = {
case conn : IncomingConnection => ???
case ConnectionStreamTerminated => ???
}
}
val actorRef = actorSystem actorOf Props[ConnectionActor]
val actorSink =
Sink.actorRef[IncomingConnection](actorRef, ConnectionStreamTerminated)
val bindingFuture: Future[Http.ServerBinding] =
serverSource.runWith(actorSink)
For the reason that the suggested Agents have been deprecated. I would suggests to use akka-http-session. It makes sure session data is secure and cannot be tampered with.

Resources