I'm using gatling to run load tests. I'm not a gatling or scala expert, but it seems to me that this particular problem might be more related to scala (and Akka) than to gatling itself. I'll start at the end: When running at high load on a not-so-strong machine, I occasionally get this error:
07:04:42.071 [ERROR] i.g.c.a.b.SessionHookBuilder$$anonfun$build$1$$anon$1 - 'sessionHook-29' crashed on session Session(searchOrder,5348761459522421680-8295,Map(),1433329482056,0, OK,List(),), forwarding to the next one java.lang.UnsupportedOperationException: tail of empty list at scala.collection.immutable.Nil$.tail(List.scala:422) ~[scala-library-2.11.6.jar:na] at scala.collection.immutable.Nil$.tail(List.scala:417) ~[scala-library-2.11.6.jar:na] at scala.collection.LinearSeqLike$$anon$1.next(LinearSeqLike.scala:46) ~[scala-library-2.11.6.jar:na] at scala.collection.TraversableOnce$FlattenOps$$anon$1.next(TraversableOnce.scala:450) ~[scala-library-2.11.6.jar:na] at MyNamespace.Tenants$.next(Tenants.scala:21) ~[na:na] at MyNamespace.LoadTests$$anonfun$run$1.apply(LoadTests.scala:43) ~[na:na] at MyNamespace.LoadTests$$anonfun$run$1.apply(LoadTests.scala:41) ~[na:na] at io.gatling.core.action.SessionHook.executeOrFail(SessionHook.scala:35) ~[gatling-core-2.1.5.jar:2.1.5] at io.gatling.core.action.Failable$class.execute(Actions.scala:71) ~[gatling-core-2.1.5.jar:2.1.5] at io.gatling.core.action.SessionHook.execute(SessionHook.scala:28) ~[gatling-core-2.1.5.jar:2.1.5] at io.gatling.core.action.Action$$anonfun$receive$1.applyOrElse(Actions.scala:29) ~[gatling-core-2.1.5.jar:2.1.5] at scala.PartialFunction$OrElse.applyOrElse(PartialFunction.scala:171) ~[scala-library-2.11.6.jar:na] at akka.actor.Actor$class.aroundReceive(Actor.scala:465) ~[akka-actor_2.11-2.3.9.jar:na] at io.gatling.core.akka.BaseActor.aroundReceive(BaseActor.scala:22) ~[gatling-core-2.1.5.jar:2.1.5] at akka.actor.ActorCell.receiveMessage(ActorCell.scala:516) ~[akka-actor_2.11-2.3.9.jar:na] at akka.actor.ActorCell.invoke(ActorCell.scala:487) ~[akka-actor_2.11-2.3.9.jar:na] at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:254) ~[akka-actor_2.11-2.3.9.jar:na] at akka.dispatch.Mailbox.run(Mailbox.scala:221) ~[akka-actor_2.11-2.3.9.jar:na] at akka.dispatch.Mailbox.exec(Mailbox.scala:231) ~[akka-actor_2.11-2.3.9.jar:na] at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) ~[scala-library-2.11.6.jar:na] at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) ~[scala-library-2.11.6.jar:na] at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) [scala-library-2.11.6.jar:na] at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) [scala-library-2.11.6.jar:na]
I suspect it has to do with using
Iterator.continually([some collection]).flatten
where [some collection] is the collection that I'm trying to loop through circularly. Could it be that when lots of concurrent threads try to access an iterator defined in this way that it occasionally fails? Is there a better way to create a circular iterator that is also thread safe?
As in the tenants object created by the "save" method below (in practice there is more code here that fills the collection retrieved by the get method, so it is actually never empty):
import io.gatling.core.Predef._
object Tenants {
type TenantKeys = Map[String, Map[String, String]]
val key = "tenants"
val elementKey = "tenant"
var tenants: Iterator[Map[String, String]] = Iterator(Map[String, String]())
def get(session: Session): TenantKeys = {
session(key)
.asOption[TenantKeys]
.getOrElse(Map[String, Map[String, String]]())
}
def save(session: Session): Session = {
val mapped = get(session).map(_._2).toSeq
tenants = Iterator.continually(mapped).flatten
session
}
}