Skip to content

Instantly share code, notes, and snippets.

@imavroukakis
Last active June 25, 2020 08:38
Show Gist options
  • Save imavroukakis/99289df78baa7f27e93af8d5e5fca0d4 to your computer and use it in GitHub Desktop.
Save imavroukakis/99289df78baa7f27e93af8d5e5fca0d4 to your computer and use it in GitHub Desktop.
enablePlugins(PackPlugin)
organization := "dev.to"
name := "gatling-scale-out"
version := "1.0"
scalaVersion := "2.12.10"
val gatlingVersion = "3.3.1"
resolvers += Resolver.sonatypeRepo("releases")
resolvers += Resolver.jcenterRepo
libraryDependencies ++= Seq(
"io.gatling" % "gatling-app" % gatlingVersion,
"io.gatling.highcharts" % "gatling-charts-highcharts" % gatlingVersion exclude("io.gatling", "gatling-recorder"),
"org.rogach" %% "scallop" % "3.4.0",
)
packMain := Map("load-test" -> "dev.to.gatling.GatlingRunner")
packJvmOpts := Map("load-test" -> Seq("-Xms2G -Xmx2G -XX:+HeapDumpOnOutOfMemoryError -XX:+UseG1GC -XX:MaxGCPauseMillis=30 -XX:G1HeapRegionSize=16m -XX:InitiatingHeapOccupancyPercent=75 -XX:+ParallelRefProcEnabled -XX:+PerfDisableSharedMem -XX:+OptimizeStringConcat -Djava.net.preferIPv4Stack=true -Djava.net.preferIPv6Addresses=false"))
Compile / run / fork := true
javaOptions ++= {
val props = sys.props.toList
props.map {
case (key, value) => s"-D$key=$value"
}
}
package dev.to.gatling
import java.text.SimpleDateFormat
import java.util.Calendar
import io.gatling.app.Gatling
import io.gatling.core.config.GatlingPropertiesBuilder
import org.rogach.scallop.{ScallopConf, ScallopOption}
class Conf(arguments: Seq[String]) extends ScallopConf(arguments) {
val usersPerSecond: ScallopOption[Int] = opt[Int](default = Some(5))
val reportOnly: ScallopOption[String] = opt[String]()
val testDuration: ScallopOption[String] = opt[String](default = Some("60_seconds"))
verify()
}
object GatlingRunner {
var conf: Option[Conf] = None
def main(args: Array[String]) {
conf = Some(new Conf(args))
conf match {
case Some(conf) => {
val simClass = classOf[LoadSimulation].getName
val props = new GatlingPropertiesBuilder
props.simulationClass(simClass)
props.runDescription("Gatling Load Test")
if (conf.reportOnly.isDefined) {
props.reportsOnly(conf.reportOnly())
} else {
val now = Calendar.getInstance().getTime
val dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH_mm_ss")
props.resultsDirectory(s"results/${dateFormat.format(now)}")
}
Gatling.fromMap(props.build)
}
case None => throw new IllegalArgumentException
}
}
}
def gitUrl = 'https://github.com/your_repo/gatling-scale-out'
def gitCredentials = 'Github'
def testGroups = [:]
def numberOfTestNodes = 5
def splitTestsAbove = 50.0
def jdkTool = 'openjdk-11'
def sbtTool = '1.3.8'
pipeline {
agent any
tools {
jdk jdkTool
}
environment {
SBT_HOME = tool name: sbtTool, type: 'org.jvnet.hudson.plugins.SbtPluginBuilder$SbtInstallation'
PATH = "${env.SBT_HOME}/bin:${env.PATH}"
}
parameters {
choice(choices: ['5', '10', '15', '20', '30', '40', '50', '60', '70', '80', '90', '100'], description: 'The amount of users per second to generate', name: 'usersPerSecond')
choice(choices: ['1_minute', '2_minutes', '5_minutes', '10_minutes', '15_minutes', '20_minutes'], description: 'The amount of time to run the simulation for', name: 'duration')
}
stages {
stage('Checkout') {
steps {
deleteDir()
git branch: 'main', credentialsId: "$gitCredentials", poll: false, url: "$gitUrl"
}
}
stage('Build') {
steps {
sh "sbt clean compile packArchiveTgz"
stash name: 'load-test', includes: 'target/gatling-scale-out-1.0.tar.gz'
}
}
stage('Load Test') {
steps {
script {
currentBuild.description = "Users/sec:${params.usersPerSecond}/Duration:${params.duration}"
def userPerSecond = "${params.usersPerSecond}" as Double
int usersPerNodeCount
if (userPerSecond >= splitTestsAbove) {
usersPerNodeCount = Math.round(userPerSecond / numberOfTestNodes)
} else {
usersPerNodeCount = userPerSecond
numberOfTestNodes = 1
}
for (int i = 0; i < numberOfTestNodes; i++) {
def num = i
testGroups["node $num"] = {
node {
def javaHome = tool name: jdkTool
deleteDir()
unstash 'load-test'
sh 'mv target/gatling-scale-out-1.0.tar.gz ./'
sh 'tar xf gatling-scale-out-1.0.tar.gz'
sh "JAVA_HOME=$javaHome gatling-scale-out-1.0/bin/load-test --users-per-second=$usersPerNodeCount --test-duration=${params.duration}"
stash name: "node $num", includes: '**/simulation.log'
}
}
}
parallel testGroups
}
}
}
stage('Collect results') {
steps {
script {
for (int i = 0; i < numberOfTestNodes; i++) {
def num = i
unstash "node $i"
}
}
sh 'mv target/gatling-scale-out-1.0.tar.gz ./'
sh 'tar xf gatling-scale-out-1.0.tar.gz'
sh "gatling-scale-out-1.0/bin/load-test --report-only \"${env.WORKSPACE}/results\""
sh "mv results results-test-${env.BUILD_NUMBER}"
sh "tar zcf results-test-${env.BUILD_NUMBER}.tar.gz results-test-${env.BUILD_NUMBER}"
archiveArtifacts artifacts: "results-test-${env.BUILD_NUMBER}.tar.gz", caseSensitive: false, onlyIfSuccessful: true
}
}
}
}
package dev.to.gatling
import com.typesafe.scalalogging.StrictLogging
import io.gatling.core.Predef._
import io.gatling.core.scenario.Simulation
import io.gatling.http.Predef._
import scala.concurrent.duration._
import scala.language.postfixOps
import scala.util.{Failure, Success, Try}
class LoadSimulation extends Simulation with StrictLogging {
GatlingRunner.conf match {
case Some(conf) => {
val duration: FiniteDuration = Try(Duration(conf.testDuration().replace('_', ' '))) match {
case Success(duration) => duration.asInstanceOf[FiniteDuration]
case Failure(exception) => throw exception
}
val usersPerSecond = conf.usersPerSecond().toDouble
val postsScenario =
scenario("Posts")
.exec(http("Get 100 posts").get("http://jsonplaceholder.typicode.com/todos"))
setUp(
postsScenario.inject(
constantUsersPerSec(usersPerSecond) during duration
).protocols(http)
)
}
case None => throw new IllegalStateException
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment