Retry failed request with pause in gatling - scala

I'm trying to implement retry logic so that if my request fails due to some technical problem (timeout, connection reset etc.) or error code 4xx|5xx, script tries to re-submit it couple of times with some pause
My code looks like this
scenario("my_simulation")
.repeat(2) {
tryMax(5, "retryLoopIndex") {
//pause(session => computePause(session("retryLoopIndex").as[Int]))
println(LocalDateTime.now() + """Before sleep""")
pause(5.seconds)
println(LocalDateTime.now() + """After sleep""")
exec(http("get_eps_internal")
.get("/500")
.headers(headers_0)
.requestTimeout(GlobalRequestTimeout)
.check(status.is(200))
)
}
}
I have 2 problems here:
it seems like pause "happens" only once when scenario gets initialized
it pauses for couple milliseconds, not 5 seconds as I would expect
From the logs
2022-02-04T08:23:06.404 Before sleep
2022-02-04T08:23:06.408 After sleep
Ho can I add pause before each retry?

The way you are calling the println() is not correct. In fact, println() called once and before starting the load test. If you want to call during the test you need to do this:
.exec { session =>
println(LocalDateTime.now() + """Before sleep""")
session
}
Another point is that you call actions without a "dot" and it isn't a right way
... {
pause(5.seconds)
exec(http("get_eps_internal")
... }
You need do this:
... {
pause(5.seconds)
.exec(http("get_eps_internal")
... }

Related

Gatling: how to execute the first request only once?

I'm trying to run a scenario which has 2 requests
The first one authenticates the users through an API and gets a token. I want each user to do the first request only once.
The second one executes a GET throught another API.
Here's the code of the scenario:
val slot: ScenarioBuilder = scenario("Get slot")
.doIf(_("connected").asOption[String].isEmpty) {
exec(
http("Get_token")
.post(my_endpoint)
.headers(specific_headers)
.formParam("username", "my_username")
.formParam("password", "my_password")
.check(regex("""id_token":"(.*?)"""").saveAs("my_token"))
)
.exec { session =>
println("Before: " + session("connected").as[String]) //prints nothing as expected
session
}
.exec(_.set("connected", "true"))
.exec { session =>
println("After: " + session("connected").as[String]) //prints "true" as expected
session
}
}
.feed(fsid)
.exec(
http("Get_slot")
.get("/slots")
.headers(other_specific_headers) //including "my_token" extraced above
.queryParam("specific_id", "${specific_id}") //from feeder fsid
.check(regex("""id":"(.*?)"""").findRandom.saveAs("slot_id"))
)
Here's the code of the simulation:
setUp(
slot.inject(
constantUsersPerSec(1).during(15.seconds)
).protocols(httpConfig)
)
When I execute the above simulation, I can see in the logs that each of the 2 requests are both executed 15 times. I'm expecting to have less "Get_token" executed than "Get_slot".
I've read some documentation avout Gatling's forever element, and I think it could do the work.
But I would like to understand why this does not behave as I expect.
If both requests are executed 15 times, does it mean that:
15 users are launched, and thus their session is empty at the beginning, so it's normal they do the get_token ?
My "Doif" is not well written ?
Something else I'm missing ?
Any help would be great, thank you in advance.
EDIT:
Just to give more informations, according to simpleApp's comment, I only see 2 actives users during the test (in average)
This is why I expect to see 4 "Get_token", and not more.

Play framework Scala run job in background

Is there any way I can trigger a job from the controller (to not to wait for its completion) and display the message to the user that job will be running in the background?
I have one controller method which takes quite long time to run. So I want to make that run offline and display the message to the user that it will be running in the background.
I tried Action.async as shown below. But the processing of the Future object is still taking more time and getting timed out.
def submit(id: Int) = Action.async(parse.multipartFormData) { implicit request =>
val result = Future {
//process the data
}
result map {
res =>
Redirect(routes.testController.list()).flashing(("success", s"Job(s) will be ruuning in background."))
}
}
You can also return a result without waiting for the result of the future in a "fire and forget" way
def submit(id: Int) = Action(parse.multipartFormData) { implicit request =>
Future {
//process the data
}
Redirect(routes.testController.list()).flashing(("success", s"Job(s) will be running in background."))
}
The docs state:
By giving a Future[Result] instead of a normal Result, we are able to quickly generate the result without blocking. Play will then serve the result as soon as the promise is redeemed.
The web client will be blocked while waiting for the response, but nothing will be blocked on the server, and server resources can be used to serve other clients.
You can configure your client code to use ajax request and display a Waiting for data message for some part of the page without blocking the rest of the web page from loading.
I also tried the "Futures.timeout" option. It seems to work fine. But I'm not sure its correct way to do it or not.
result.withTimeout(20.seconds)(futures).map { res =>
Redirect(routes.testController.list()).flashing(("success", s"Job(s) will be updated in background."))
}.recover {
case e: scala.concurrent.TimeoutException =>
Redirect(routes.testController.list()).flashing(("success", s"Job(s) will be updated in background."))
}

Strange timeout with ScalaTest's Selenium DSL

I'm writing Selenium tests with ScalaTest's Selenium DSL and I'm running into timeouts I can't explain. To make matters more complicated, they only seem to happen some of the time.
The problem occurs whenever I access an Element after a page load or some Javascript rendering. It looks like this:
click on "editEmployee"
eventually {
textField(name("firstName")).value = "Steve"
}
My PatienceConfig is configured like this:
override implicit val patienceConfig: PatienceConfig =
PatienceConfig(timeout = Span(5, Seconds), interval = Span(50, Millis))
The test fails with the following error:
- should not display the old data after an employee was edited *** FAILED ***
The code passed to eventually never returned normally. Attempted 1 times over 10.023253653000001 seconds.
Last failure message: WebElement 'firstName' not found.. (EditOwnerTest.scala:24)
It makes sense that it doesn't succeed immediately, because the click causes some rendering, and the textfield may not be available right away. However, it shouldn't take 10 seconds to make an attempt to find it, right?
Also, I find it very interesting that the eventually block tried it only once, and that it took almost precisely 10 seconds. This smells like a timeout occurred somewhere, and it's not my PatienceConfig, because that was set to time out after 5 seconds.
With this workaround, it does work:
click on "editEmployee"
eventually {
find(name("firstName")).value // from ScalaTest's `OptionValues`
}
textField(name("firstName")).value = "Steve"
I did some digging in the ScalaTest source, and I've noticed that all calls that have this problem (it's not just textField), eventually call webElement at some point. The reason why the workaround works, is because it doesn't call webElement. webElement is defined like this:
def webElement(implicit driver: WebDriver, pos: source.Position = implicitly[source.Position]): WebElement = {
try {
driver.findElement(by)
}
catch {
case e: org.openqa.selenium.NoSuchElementException =>
// the following is avoid the suite instance to be bound/dragged into the messageFun, which can cause serialization problem.
val queryStringValue = queryString
throw new TestFailedException(
(_: StackDepthException) => Some("WebElement '" + queryStringValue + "' not found."),
Some(e),
pos
)
}
}
I've copied that code into my project and played around with it, and it looks like constructing and/or throwing the exception is where most of the 10 seconds are spent.
(EDIT Clarification: I've actually seen the code actually spend its 10 seconds inside the catch block. The implicit wait is set to 0, and besides, if I remove the catch block everything simply works as expected.)
So my question is, what can I do to avoid this strange behaviour? I don't want to have to insert superfluous calls to find all the time, because it's easily forgotten, especially since, as I said, the error occurs only some of the time. (I haven't been able to determine when the behaviour occurs and when it doesn't.)
It is clear that the textField(name("firstName")).value = "Steve" ends up calling the WebElement as you have found out.
Since the issue in the op is happening where ever web elements are involved (which in turn implies that webdriver is involved), I think it is safe to assume that the issue is related to the implicit wait on the Web driver.
implicitlyWait(Span(0, Seconds))
The above should ideally fix the issue. Also, making implicit wait to be 0 is a bad practice. Any web page might have some loading issues. The page load is handled by Selenium outside its wait conditions. But slow element load (may be due to ajax calls) could result in failure. I usually keep 10 seconds as my standard implicit wait. For scenarios which require more wait, explicit waits can be used.
def implicitlyWait(timeout: Span)(implicit driver: WebDriver): Unit = {
driver.manage.timeouts.implicitlyWait(timeout.totalNanos, TimeUnit.NANOSECONDS)
}
Execution Flow:
name("firstName") ends up having value as Query {Val by = By.className("firstName") }.
def name(elementName: String): NameQuery = new NameQuery(elementName)
case class NameQuery(queryString: String) extends Query { val by = By.name(queryString) }
Query is fed to the textField method which calls the Query.webElement as below.
def textField(query: Query)(implicit driver: WebDriver, pos: source.Position): TextField = new TextField(query.webElement)(pos)
sealed trait Query extends Product with Serializable {
val by: By
val queryString: String
def webElement(implicit driver: WebDriver, pos: source.Position = implicitly[source.Position]): WebElement = {
try {
driver.findElement(by)
}
catch {
case e: org.openqa.selenium.NoSuchElementException =>
// the following is avoid the suite instance to be bound/dragged into the messageFun, which can cause serialization problem.
val queryStringValue = queryString
throw new TestFailedException(
(_: StackDepthException) => Some("WebElement '" + queryStringValue + "' not found."),
Some(e),
pos
)
}
}
}
I don't know ScalaTest's specifics, but such strange timeouts usually occur when you're mixing up implicit and explicit waits together.
driver.findElement uses implicit waits internally. And depending on specified explicit waits timeout, you may face with summing both together.
Ideally, implicit waits should be set to 0 to avoid such issues.

How can I know my Stopwatch has run?

I use several stopwatches in my application. They are all created together, but only some of them have actually run (due to exceptions earlier in the code or other things).
After my application has run, I'm creating my report using those stopwatches. For instance, I'm doing the following:
Stopwatch subStopwatch = Stopwatch.createUnstarted();
Stopwatch mainStopwatch = Stopwatch.createStarted();
try {
// do something 1
subStopwatch.start();
// do something 2
subStopwatch.stop();
} finally {
mainStopwatch.stop();
System.out.printf("Total run time: %s%n", mainStopwatch);
if (!subStopwatch.isRunning()) {
System.out.printf(" including sub run time: %s%n", subStopwatch);
}
}
The problem in this code is that if something happens in "do something 1" (return, exception), subStopwatch will be printed anyways.
The following solutions work:
- Using a boolean to indicate I started the stopwatch.
- Using a stopwatch more locally and using a report mechanism that contains the information I'm looking for.
But the main question remains: can I know that a stopwatch has run using Stopwatch only.
You can check the elapsed time on the stopwatch:
if (subStopwatch.elapsed(TimeUnit.NANOSECONDS) > 0) {
// it ran
}

shutdown hook won't start upon ^C (scala)

i'm trying to get a clean and gracefull shutdown, and for some reason, it wont execute. iv'e tried:
sys addShutdownHook{
logger.warn("SHUTTING DOWN...")
// irrelevant logic here...
}
and also:
Runtime.getRuntime.addShutdownHook(ThreadOperations.delayOnThread{
logger.warn("SHUTTING DOWN...")
// irrelevant logic here...
}
)
where ThreadOperations.delayOnThread definition is:
object ThreadOperations {
def startOnThread(body: =>Unit) : Thread = {
onThread(true, body)
}
def delayOnThread(body: =>Unit) : Thread = {
onThread(false, body)
}
private def onThread(runNow : Boolean, body: =>Unit) : Thread = {
val t=new Thread {
override def run=body
}
if(runNow){t.start}
t
}
// more irrelevant operations...
}
but when i run my program (executable jar, double activation), the hook does not start. so what am i doing wrong? what is the right way to add a shutdown hook in scala? is it in any way related to the fact i'm using double activation?
double activation is done like that:
object Gate extends App {
val givenArgs = if(args.isEmpty){
Array("run")
}else{
args
}
val jar = Main.getClass.getProtectionDomain().getCodeSource().getLocation().getFile;
val dir = jar.dropRight(jar.split(System.getProperty("file.separator")).last.length + 1)
val arguments = Seq("java", "-cp", jar, "boot.Main") ++ givenArgs.toSeq
Process(arguments, new java.io.File(dir)).run();
}
(scala version: 2.9.2 )
thanks.
In your second attempt, your shutdown hook you seems to just create a thread and never start it (so it just gets garbage collected and does nothing). Did I miss something? (EDIT: yes I did, see comment. My bad).
In the first attempt, the problem might just be that the underlying log has some caching, and the application exits before the log is flushed.
Solved it.
For some reason, I thought that run as opposed to ! would detach the process. It actually hangs on because there are open streams left to the Process, which is returned from run (or maybe it just hangs for another reason, 'cause exec doesn't hang, but returns a Process with open streams to and from the child process, much like run). For this reason, the original process was still alive, and I accidentally sent the signals to it. Of course, it did not contain a handler, or a shutdown hook, so nothing happened.
The solution was to use Runtime.getRuntime.exec(arguments.toArray) instead of Process(arguments, new java.io.File(dir)).run();, close the streams in the Gate object, and send the ^C signal to the right process.