Commit 702032f0 authored by Tom Käsler's avatar Tom Käsler

Merge branch 'stresstest' into 'master'

Stresstest

See merge request !14
parents 0d182b1b 4f18306b
Pipeline #7909 failed with stages
in 4 minutes and 7 seconds
......@@ -12,6 +12,7 @@ variables:
MYSQL_ROOT_PASSWORD: arsnova3_prototype
WAR_FILE: target/scala-2.11/arsnova-3-backend*.jar
MIRROR_REPO: git@github.com:thm-projects/arsnova-3-backend.git
REPORT_DIR: target/gatling-it/stresstest*
stylecheck:
stage: test
......@@ -44,6 +45,20 @@ sync_mirror:
- git update-ref -d refs/tags/staging
- git push --mirror "$MIRROR_REPO"
stresstest:
stage: test
only:
- master
- /^v[0-9]+/
tags:
- sbt
dependencies: []
script:
- sbt clean gatling-it:test
artifacts:
paths:
- $REPORT_DIR
package:
stage: build
only:
......
name := "arsnova-3-backend"
name := "arsnova-3-backend"
version := "0.0.1"
scalaVersion := "2.12.1"
scalaVersion := "2.11.8"
enablePlugins(GatlingPlugin)
libraryDependencies ++= {
val akkaVersion = "2.4.17"
......@@ -10,6 +14,7 @@ libraryDependencies ++= {
val scalaTestVersion = "3.0.0"
val scalaMockVersion = "3.2.2"
val slickVersion = "3.2.0"
val gatlingVersion = "2.2.4"
Seq(
"com.typesafe.akka" %% "akka-actor" % akkaVersion,
......@@ -23,9 +28,11 @@ libraryDependencies ++= {
"org.slf4j" % "slf4j-nop" % "1.7.21",
"mysql" % "mysql-connector-java" % "6.0.3",
"org.flywaydb" % "flyway-core" % "3.2.1",
"org.scalatest" %% "scalatest" % scalaTestVersion
"org.scalatest" %% "scalatest" % scalaTestVersion,
"io.gatling.highcharts" % "gatling-charts-highcharts" % gatlingVersion % "test,it",
"io.gatling" % "gatling-test-framework" % gatlingVersion % "test,it"
)
}
// skip Tests in assembly job
test in assembly := {}
test in assembly := {}
\ No newline at end of file
addSbtPlugin("io.spray" % "sbt-revolver" % "0.8.0")
addSbtPlugin("org.scalastyle" %% "scalastyle-sbt-plugin" % "0.8.0")
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.3")
addSbtPlugin("io.gatling" % "gatling-sbt" % "2.2.1")
package de.thm.arsnova
import de.thm.arsnova.auditor.BasicAuditorSimulation
import de.thm.arsnova.tutor.BasicTutorSimulation
import io.gatling.core.Predef._ // 2
import io.gatling.http.Predef._
import scala.concurrent.duration._
class Stresstest extends Simulation {
println("starting webserver")
WebServer.main(Array())
val httpProtocol = http
.baseURL("http://localhost:9000")
.inferHtmlResources(BlackList(""".*\.css""", """.*\.js""", """.*\.ico"""), WhiteList())
.acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
.acceptEncodingHeader("gzip, deflate")
.acceptLanguageHeader("en-US,en;q=0.5")
.doNotTrackHeader("1")
.userAgentHeader("Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:50.0) Gecko/20100101 Firefox/50.0")
val headers_0 = Map("Upgrade-Insecure-Requests" -> "1")
val uri1 = "http://localhost:9000/session/1"
val auditorScn = scenario("Test").exec(
BasicAuditorSimulation.joinSession.pause(3),
BasicAuditorSimulation.getAllPrepQuestions.pause(3),
BasicAuditorSimulation.answerToMCQuestion
)
val tutorScn = scenario("Basic Tutor").exec(
BasicTutorSimulation.createSession.pause(3),
BasicTutorSimulation.createQuestion
)
setUp(
auditorScn.inject(rampUsers(1000) over (5 seconds)),
tutorScn.inject(rampUsers(100) over (5 seconds))
).protocols(httpProtocol)
}
\ No newline at end of file
package de.thm.arsnova.auditor
import de.thm.arsnova.models.ChoiceAnswer
import io.gatling.core.Predef._ // 2
import io.gatling.http.Predef._
import scala.concurrent.duration._
import spray.json._
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
object BasicAuditorSimulation {
import de.thm.arsnova.mappings.ChoiceAnswerJsonProtocol._
val joinSession = exec(http("Auditor joins session")
.get("/session/1"))
val getAllPrepQuestions = exec(http("Auditor gets all preparation questions")
.get("/question/")
.queryParam("sessionid", "1")
.queryParam("variant", "preparation"))
val newAnswer = ChoiceAnswer(None, 5, 1, 2)
val answerToMCQuestion = exec(http("Auditor answers mc question")
.post("/question/5/choiceAnswer")
.header("Content-Type", "application/json")
.body(StringBody(newAnswer.toJson.toString)).asJSON)
}
\ No newline at end of file
package de.thm.arsnova.tutor
import de.thm.arsnova.models.{AnswerOption, Question, Session}
import io.gatling.core.Predef._
import io.gatling.http.Predef._
import scala.concurrent.duration._
import spray.json._
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import java.util.Calendar
object BasicTutorSimulation {
import de.thm.arsnova.mappings.QuestionJsonProtocol._
import de.thm.arsnova.mappings.SessionJsonProtocol._
val now = Calendar.getInstance.getTime.toString
val newSession = Session(None, "12312312", 1, "A new Session", "ans", now, now, true, false, false, None)
val mcAnswerOptions = Seq(
AnswerOption(None, None, false, "12", -10),
AnswerOption(None, None, true, "13", 10),
AnswerOption(None, None, false, "14", -10),
AnswerOption(None, None, true, "thirteen", 10)
)
val newMCQuestion = Question(None, 1, "new Question Subject", "This is an MC question for stress testing",
"preparation", "mc", Some("This is the hint!"), Some("The answer is 13"), true, false, true, true, false, None, Some(mcAnswerOptions))
val createSession = exec(http("Tutor creates session")
.post("/session/")
.header("Content-Type", "application/json")
.body(StringBody(newSession.toJson.toString)).asJSON)
val createQuestion = exec(http("Tutor creates mc question")
.post("/question/")
.header("Content-Type", "application/json")
.body(StringBody(newMCQuestion.toJson.toString)).asJSON
)
}
\ No newline at end of file
akka {
loglevel = WARNING
http {
server {
idle-timeout = 60s
request-timeout = 20s
bind-timeout = 1s
}
host-connection-pool {
max-connections = 4
idle-timeout = 30s
}
}
}
database = {
......@@ -9,10 +20,17 @@ database = {
user = ${?PSQL_USER}
password = "arsnova3_prototype"
password = ${?PSQL_PASSWORD}
driver = com.mysql.jdbc.Driver
numThreads = 1
connectionPool = disabled
driver = com.mysql.cj.jdbc.Driver
connectionPool = "HikariCP"
numThreads = 10
keepAliveConnection = true
connectionTimeout = 30 seconds
maxLifetime = 5 minutes
maximumPoolSize = 10
leakDetectionThreshold = 2000
properties.cachePrepStmts = true
properties.prepStmtCacheSize = 20000
properties.prepStmtCacheSqlLimit = 100000
}
http {
......@@ -20,3 +38,5 @@ http {
port = 9000
}
logger.scala.slick = DEBUG
logger.scala.slick.session = DEBUG
package de.thm.arsnova
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import scala.concurrent.ExecutionContext
object Context {
implicit val system = ActorSystem()
implicit val executor: ExecutionContext = system.dispatcher
implicit val materializer: ActorMaterializer = ActorMaterializer()
}
......@@ -4,16 +4,13 @@ import akka.actor.ActorSystem
import akka.event.{Logging, LoggingAdapter}
import akka.http.scaladsl.Http
import akka.http.scaladsl.server.Directives._
import akka.stream.ActorMaterializer
import de.thm.arsnova.utils.{MigrationConfig, Config}
import scala.concurrent.ExecutionContext
object WebServer extends App with Config with MigrationConfig with Routes {
private implicit val system = ActorSystem()
protected implicit val executor: ExecutionContext = system.dispatcher
import de.thm.arsnova.Context._
protected val log: LoggingAdapter = Logging(system, getClass)
protected implicit val materializer: ActorMaterializer = ActorMaterializer()
if (args.contains("migrate")) {
migrate
......
......@@ -4,7 +4,6 @@ import de.thm.arsnova.services.{ChoiceAnswerService, FreetextAnswerService}
import de.thm.arsnova.models._
import de.thm.arsnova.hateoas.{ApiRoutes, ResourceAdapter, Link}
import scala.concurrent.ExecutionContext.Implicits.global
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives._
import spray.json._
......@@ -13,6 +12,8 @@ import spray.json._
The API Interface regarding answers to choice questions (e.g. MC, SC).
*/
trait ChoiceAnswerApi {
import de.thm.arsnova.Context.executor
// protocol for serializing data
import de.thm.arsnova.mappings.ChoiceAnswerJsonProtocol._
......
......@@ -4,7 +4,6 @@ import de.thm.arsnova.services.CommentService
import de.thm.arsnova.models._
import de.thm.arsnova.hateoas.{ApiRoutes, ResourceAdapter, Link}
import scala.concurrent.ExecutionContext.Implicits.global
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives._
import spray.json._
......@@ -14,6 +13,8 @@ import spray.json._
The API Interface regarding comments (formerly known as "interposed question"), made by participants.
*/
trait CommentApi {
import de.thm.arsnova.Context.executor
// protocol for serializing data
import de.thm.arsnova.mappings.CommentJsonProtocol._
......
......@@ -4,7 +4,6 @@ import de.thm.arsnova.services.FeaturesService
import de.thm.arsnova.models._
import de.thm.arsnova.hateoas.{ApiRoutes, ResourceAdapter, Link}
import scala.concurrent.ExecutionContext.Implicits.global
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives._
import spray.json._
......@@ -14,6 +13,8 @@ import spray.json._
The API Interface regarding session features.
*/
trait FeaturesApi {
import de.thm.arsnova.Context.executor
// protocol for serializing data
import de.thm.arsnova.mappings.FeatureJsonProtocol._
......
......@@ -4,7 +4,6 @@ import de.thm.arsnova.services.FreetextAnswerService
import de.thm.arsnova.models._
import de.thm.arsnova.hateoas.{ApiRoutes, ResourceAdapter, Link}
import scala.concurrent.ExecutionContext.Implicits.global
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives._
import spray.json._
......@@ -14,6 +13,8 @@ import spray.json._
The API Interface regarding answers for the question type "freetext".
*/
trait FreetextAnswerApi {
import de.thm.arsnova.Context.executor
// protocol for serializing data
import de.thm.arsnova.mappings.FreetextAnswerJsonProtocol._
......
......@@ -4,7 +4,6 @@ import de.thm.arsnova.services.GlobalMotdService
import de.thm.arsnova.models._
import de.thm.arsnova.hateoas.{ApiRoutes, ResourceAdapter, Link}
import scala.concurrent.ExecutionContext.Implicits.global
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives._
import spray.json._
......@@ -14,6 +13,8 @@ The API Interface regarding global messages.
Only an admin should post these motd
*/
trait GlobalMotdApi {
import de.thm.arsnova.Context.executor
// protocol for serializing data
import de.thm.arsnova.mappings.GlobalMotdJsonProtocol._
......
......@@ -4,7 +4,6 @@ import de.thm.arsnova.services.QuestionService
import de.thm.arsnova.models._
import de.thm.arsnova.hateoas.{ApiRoutes, ResourceAdapter, Link}
import scala.concurrent.ExecutionContext.Implicits.global
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives._
import spray.json._
......@@ -14,6 +13,8 @@ import spray.json._
The API Interface regarding questions.
*/
trait QuestionApi {
import de.thm.arsnova.Context.executor
// protocol for serializing data
import de.thm.arsnova.mappings.QuestionJsonProtocol._
......
......@@ -4,7 +4,6 @@ import de.thm.arsnova.services.SessionService
import de.thm.arsnova.models._
import de.thm.arsnova.hateoas.{ApiRoutes, ResourceAdapter, Link}
import scala.concurrent.ExecutionContext.Implicits.global
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives._
import spray.json._
......@@ -14,6 +13,8 @@ import spray.json._
The API Interface regarding sessions, the core component for arsnova.voting.
*/
trait SessionApi {
import de.thm.arsnova.Context.executor
// protocol for serializing data
import de.thm.arsnova.mappings.SessionJsonProtocol._
......
......@@ -4,7 +4,6 @@ import de.thm.arsnova.services.SessionMotdService
import de.thm.arsnova.models._
import de.thm.arsnova.hateoas.{ApiRoutes, ResourceAdapter, Link}
import scala.concurrent.ExecutionContext.Implicits.global
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives._
import spray.json._
......@@ -13,6 +12,8 @@ import spray.json._
The API Interface regarding session messages.
*/
trait SessionMotdApi {
import de.thm.arsnova.Context.executor
// protocol for serializing data
import de.thm.arsnova.mappings.SessionMotdJsonProtocol._
......
......@@ -3,12 +3,13 @@ package de.thm.arsnova.api
import de.thm.arsnova.services.UserService
import de.thm.arsnova.models._
import scala.concurrent.ExecutionContext.Implicits.global
import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
import akka.http.scaladsl.server.Directives._
import spray.json._
trait UserApi {
import de.thm.arsnova.Context.executor
import de.thm.arsnova.mappings.UserJsonProtocol._
val userApi = pathPrefix("") {
......
......@@ -2,11 +2,12 @@ package de.thm.arsnova.services
import de.thm.arsnova.models._
import slick.driver.MySQLDriver.api._
import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import scala.util.{Failure, Success}
object FeaturesService extends BaseService {
import de.thm.arsnova.Context.executor
def getById(featuresId: FeaturesId): Future[Features] = {
featuresTable.filter(_.id === featuresId).result.head
}
......
......@@ -3,10 +3,11 @@ package de.thm.arsnova.services
import de.thm.arsnova.models._
import slick.driver.MySQLDriver.api._
import scala.concurrent.{ExecutionContext, Future}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
object QuestionService extends BaseService {
import de.thm.arsnova.Context.executor
def findQuestionsBySessionIdAndVariant(sessionId: SessionId, variant: String): Future[Seq[Question]] = {
db.run(questionsTable.filter(q => q.sessionId === sessionId && q.variant === variant).result).map(qSeq =>
Future.traverse(qSeq) { q: Question => q.format match {
......
......@@ -3,9 +3,10 @@ package de.thm.arsnova.services
import de.thm.arsnova.models.{UserId, Session, SessionId, Features}
import slick.driver.MySQLDriver.api._
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
object SessionService extends BaseService{
object SessionService extends BaseService {
import de.thm.arsnova.Context.executor
def findUserSessions(userId: UserId): Future[Seq[Session]] = {
val resultTupleQry = for {
sessions <- sessionsTable.filter(_.userId === userId)
......
......@@ -5,7 +5,5 @@ trait DatabaseConfig extends Config{
import driver.api._
def db: Database = Database.forConfig("database")
implicit val session: Session = db.createSession()
val db: Database = Database.forConfig("database")
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment