Skip to content

Commit

Permalink
Loadtest: add me and single (group) topic loadtest scenarios.
Browse files Browse the repository at this point in the history
  • Loading branch information
aforge committed Mar 2, 2021
1 parent 92fc284 commit 995c706
Show file tree
Hide file tree
Showing 2 changed files with 229 additions and 125 deletions.
208 changes: 83 additions & 125 deletions loadtest/loadtest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,120 +10,16 @@ import scala.concurrent.duration._
import io.gatling.core.Predef._
import io.gatling.http.Predef._

class Loadtest extends Simulation {
val httpProtocol = http
.baseUrl("http://localhost:6060")
.wsBaseUrl("ws://localhost:6060")

class Loadtest extends TinodeBase {
// Input file can be set with the "accounts" java option.
// E.g. JAVA_OPTS="-Daccounts=/tmp/z.csv" gatling.sh -sf . -rsf . -rd "na" -s tinode.Loadtest
val feeder = csv(System.getProperty("accounts", "users.csv")).random

// Auth tokens to share between sessions.
val tokenCache : concurrent.Map[String, String] = new ConcurrentHashMap() asScala

val loginBasic = exitBlockOnFail {
exec { session =>
val uname = session("username").as[String]
val password = session("password").as[String]
val secret = new String(java.util.Base64.getEncoder.encode((uname + ":" + password).getBytes()))
session.set("secret", secret)
}
.exec {
ws("login").sendText(
"""{"login":{"id":"${id}-login","scheme":"basic","secret":"${secret}"}}"""
)
.await(5 seconds)(
ws.checkTextMessage("login-ctrl")
.matching(jsonPath("$.ctrl").find.exists)
.check(jsonPath("$.ctrl.params.token").saveAs("token"))
)
}
.exec { session =>
val uname = session("username").as[String]
val token = session("token").as[String]
tokenCache.put(uname, token)
session
}
}

val loginToken = exitBlockOnFail {
exec { session =>
val uname = session("username").as[String]
var token = session("token").asOption[String]
if (token == None) {
token = tokenCache.get(uname)
}
session.set("token", token.getOrElse(""))
}
.exec {
ws("login-token").sendText(
"""{"login":{"id":"${id}-login2","scheme":"token","secret":"${token}"}}"""
)
.await(5 seconds)(
ws.checkTextMessage("login-ctrl")
.matching(jsonPath("$.ctrl").find.exists)
)
}
}

val subMe = exitBlockOnFail {
exec {
ws("sub-me").sendText(
"""{"sub":{"id":"{id}-sub-me","topic":"me","get":{"what":"desc"}}}"""
)
.await(5 seconds)(
ws.checkTextMessage("sub-me-desc")
.matching(jsonPath("$.ctrl").find.exists)
.check(jsonPath("$.ctrl.code").ofType[Int].in(200 to 299))
)
}
}

val publish = exitBlockOnFail {
exec {
repeat(3, "i") {
exec {
ws("pub-topic").sendText(
"""{"pub":{"id":"${id}-pub-${sub}-${i}","topic":"${sub}","content":"This is a Tsung test ${i}"}}"""
)
.await(15 seconds)(
ws.checkTextMessage("pub-topic-ctrl")
.matching(jsonPath("$.ctrl").find.exists)
.check(jsonPath("$.ctrl.code").ofType[Int].in(200 to 299))
)
}
.pause(0, 3)
}
}
}

val getSubs = exitBlockOnFail {
exec {
ws("get-subs").sendText(
"""{"get":{"id":"${id}-get-subs","topic":"me","what":"sub"}}"""
)
.await(5 seconds)(
ws.checkTextMessage("save-subs")
.matching(jsonPath("$.meta.sub").find.exists)
.check(jsonPath("$.meta.sub[*].topic").findAll.saveAs("subs"))
)
}
}

val scn = scenario("WebSocket")
.exec(ws("Connect WS").connect("/v0/channels?apikey=AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K"))
.exec(session => session.set("id", "tn-" + session.userId))
.pause(1)
.exec {
ws("hi").sendText(
"""{"hi":{"id":"afabb3","ver":"0.16","ua":"Gatling-Loadtest/1.0; gatling/1.7.0"}}"""
)
.await(5 seconds)(
ws.checkTextMessage("hi")
.matching(jsonPath("$.ctrl").find.exists)
)
}
.exec(hello)
.pause(1)
.feed(feeder)
.doIfOrElse({session =>
Expand All @@ -149,31 +45,14 @@ class Loadtest extends Simulation {
session.set("subs", shuffled)
}
.foreach("${subs}", "sub") {
exec {
ws("sub-topic").sendText(
"""{"sub":{"id":"${id}-sub-${sub}","topic":"${sub}","get":{"what":"desc sub data del"}}}"""
)
.await(15 seconds)(
ws.checkTextMessage("sub-topic-ctrl")
.matching(jsonPath("$.ctrl").find.exists)
.check(jsonPath("$.ctrl.code").ofType[Int].in(200 to 299))
)
}
exec(subTopic)
.exitHereIfFailed
.pause(0, 2)
.doIfOrElse({session =>
val topic = session("sub").as[String]
!topic.startsWith("chn")
}) { publish } { pause(5) }
.exec {
ws("leave-topic").sendText(
"""{"leave":{"id":"${id}-leave-${sub}","topic":"${sub}"}}"""
)
.await(5 seconds)(
ws.checkTextMessage("sub-topic-ctrl")
.matching(jsonPath("$.ctrl").find.exists)
)
}
.exec(leaveTopic)
.pause(0, 3)
}
}
Expand All @@ -183,3 +62,82 @@ class Loadtest extends Simulation {
val rampPeriod = java.lang.Long.getLong("ramp", 300L)
setUp(scn.inject(rampUsers(numUsers) during (rampPeriod.seconds))).protocols(httpProtocol)
}

class MeLoadtest extends TinodeBase {
val username = System.getProperty("username", "user0")
val password = System.getProperty("password", "user0123")

val scn = scenario("WebSocket")
.exec(ws("Connect WS").connect("/v0/channels?apikey=AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K"))
.exec(session => session.set("id", "tn-" + session.userId))
.exec(session => session.set("username", username))
.exec(session => session.set("password", password))
.pause(1)
.exec(hello)
.pause(1)
.doIfOrElse({session =>
val uname = session("username").as[String]
val token = tokenCache.get(username)
token == None
}) { loginBasic } { loginToken }
.exitHereIfFailed
.exec(subMe)
.exitHereIfFailed
.exec(getSubs)
.exitHereIfFailed
.pause(1000)
.exec(ws("close-ws").close)

val numUsers = Integer.getInteger("num_users", 10000)
val rampPeriod = java.lang.Long.getLong("ramp", 300L)
setUp(scn.inject(rampUsers(numUsers) during (rampPeriod.seconds))).protocols(httpProtocol)
}

class SingleTopicLoadtest extends TinodeBase {
// Input file can be set with the "accounts" java option.
// E.g. JAVA_OPTS="-Daccounts=/tmp/z.csv" gatling.sh -sf . -rsf . -rd "na" -s tinode.Loadtest
val feeder = csv(System.getProperty("accounts", "users.csv")).random
val topic = System.getProperty("topic", "TOPIC_NAME")

val scn = scenario("WebSocket")
.exec(ws("Connect WS").connect("/v0/channels?apikey=AQEAAAABAAD_rAp4DJh05a1HAwFT3A6K"))
.exec(session => session.set("id", "tn-" + session.userId))
.pause(1)
.exec(hello)
.pause(1)
.feed(feeder)
.doIfOrElse({session =>
val uname = session("username").as[String]
var token = session("token").asOption[String]
if (token == None) {
token = tokenCache.get(uname)
}
token == None
}) { loginBasic } { loginToken }
.exitHereIfFailed
.exec(subMe)
.exitHereIfFailed
.exec(getSubs)
.exitHereIfFailed
.doIf({session =>
session.attributes.contains("subs")
}) {
exec(session => session.set("sub", topic))
.exec(subTopic)
.exitHereIfFailed
.pause(0, 2)
.exec(publish)
.pause(15)
.exec(publish)
.pause(15)
.exec(publish)
.pause(15)
.exec(leaveTopic)
.pause(0, 3)
}
.exec(ws("close-ws").close)

val numUsers = Integer.getInteger("num_users", 10000)
val rampPeriod = java.lang.Long.getLong("ramp", 300L)
setUp(scn.inject(rampUsers(numUsers) during (rampPeriod.seconds))).protocols(httpProtocol)
}
146 changes: 146 additions & 0 deletions loadtest/tinode.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
package tinode

import java.util.Base64
import java.util.concurrent.ConcurrentHashMap

import scala.collection.JavaConverters._
import scala.collection._
import scala.concurrent.duration._

import io.gatling.core.Predef._
import io.gatling.http.Predef._

class TinodeBase extends Simulation {
val httpProtocol = http
.baseUrl("http://localhost:6060")
.wsBaseUrl("ws://localhost:6060")

// Auth tokens to share between sessions.
val tokenCache : concurrent.Map[String, String] = new ConcurrentHashMap() asScala

val hello = exitBlockOnFail {
exec {
ws("hi").sendText(
"""{"hi":{"id":"afabb3","ver":"0.16","ua":"Gatling-Loadtest/1.0; gatling/1.7.0"}}"""
)
.await(15 seconds)(
ws.checkTextMessage("hi")
.matching(jsonPath("$.ctrl").find.exists)
)
}
}

val loginBasic = exitBlockOnFail {
exec { session =>
val uname = session("username").as[String]
val password = session("password").as[String]
val secret = new String(java.util.Base64.getEncoder.encode((uname + ":" + password).getBytes()))
session.set("secret", secret)
}
.exec {
ws("login").sendText(
"""{"login":{"id":"${id}-login","scheme":"basic","secret":"${secret}"}}"""
)
.await(15 seconds)(
ws.checkTextMessage("login-ctrl")
.matching(jsonPath("$.ctrl").find.exists)
.check(jsonPath("$.ctrl.params.token").saveAs("token"))
)
}
.exec { session =>
val uname = session("username").as[String]
val token = session("token").as[String]
tokenCache.put(uname, token)
session
}
}

val loginToken = exitBlockOnFail {
exec { session =>
val uname = session("username").as[String]
var token = session("token").asOption[String]
if (token == None) {
token = tokenCache.get(uname)
}
session.set("token", token.getOrElse(""))
}
.exec {
ws("login-token").sendText(
"""{"login":{"id":"${id}-login2","scheme":"token","secret":"${token}"}}"""
)
.await(15 seconds)(
ws.checkTextMessage("login-ctrl")
.matching(jsonPath("$.ctrl").find.exists)
)
}
}

val subMe = exitBlockOnFail {
exec {
ws("sub-me").sendText(
"""{"sub":{"id":"${id}-sub-me","topic":"me","get":{"what":"desc"}}}"""
)
.await(15 seconds)(
ws.checkTextMessage("sub-me-desc")
.matching(jsonPath("$.ctrl").find.exists)
.check(jsonPath("$.ctrl.code").ofType[Int].in(200 to 299))
)
}
}

val subTopic = exitBlockOnFail {
exec {
ws("sub-topic").sendText(
"""{"sub":{"id":"${id}-sub-${sub}","topic":"${sub}","get":{"what":"desc sub data del"}}}"""
)
.await(15 seconds)(
ws.checkTextMessage("sub-topic-ctrl")
.matching(jsonPath("$.ctrl").find.exists)
.check(jsonPath("$.ctrl.code").ofType[Int].in(200 to 299))
)
}
}

val publish = exitBlockOnFail {
exec {
repeat(3, "i") {
exec {
ws("pub-topic").sendText(
"""{"pub":{"id":"${id}-pub-${sub}-${i}","topic":"${sub}","content":"This is a Tsung test ${i}"}}"""
)
.await(15 seconds)(
ws.checkTextMessage("pub-topic-ctrl")
.matching(jsonPath("$.ctrl").find.exists)
.check(jsonPath("$.ctrl.code").ofType[Int].in(200 to 299))
)
}
.pause(0, 3)
}
}
}

val getSubs = exitBlockOnFail {
exec {
ws("get-subs").sendText(
"""{"get":{"id":"${id}-get-subs","topic":"me","what":"sub"}}"""
)
.await(15 seconds)(
ws.checkTextMessage("save-subs")
.matching(jsonPath("$.meta.sub").find.exists)
.check(jsonPath("$.meta.sub[*].topic").findAll.saveAs("subs"))
)
}
}

val leaveTopic = exitBlockOnFail {
exec {
ws("leave-topic").sendText(
"""{"leave":{"id":"${id}-leave-${sub}","topic":"${sub}"}}"""
)
.await(15 seconds)(
ws.checkTextMessage("sub-topic-ctrl")
.matching(jsonPath("$.ctrl").find.exists)
)
}
}
}

0 comments on commit 995c706

Please sign in to comment.