Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Exclude the right border of the injection time interval #1885

Merged
merged 1 commit into from
May 21, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ case class RampInjection(users: Int, duration: FiniteDuration) extends Injection
require(users > 0, "The number of users must be a strictly positive value")

override def chain(iterator: Iterator[FiniteDuration]): Iterator[FiniteDuration] = {
val interval = duration / (users - 1).max(1)
val interval = duration / users.max(1)
Iterator.iterate(0 milliseconds)(_ + interval).take(users) ++ iterator.map(_ + duration)
}
}
Expand Down Expand Up @@ -75,9 +75,9 @@ case class AtOnceInjection(users: Int) extends InjectionStep {
* The injection scheduling follows this equation
* u = r1*t + (r2-r1)/(2*duration)*t²
*
* @param r1 : initial injection rate in users/seconds
* @param r2 : final injection rate in users/seconds
* @param duration : injection duration
* @param r1 Initial injection rate in users/seconds
* @param r2 Final injection rate in users/seconds
* @param duration Injection duration
*/
case class RampRateInjection(r1: Double, r2: Double, duration: FiniteDuration) extends InjectionStep {
require(r1 > 0 && r2 > 0, "injection rates must be strictly positive values")
Expand Down Expand Up @@ -106,20 +106,24 @@ case class RampRateInjection(r1: Double, r2: Double, duration: FiniteDuration) e
}

/**
* Inject users thru separated steps until reaching the total amount of users
* Inject users through separated steps until reaching the closest possible amount of total users.
*
* @param possibleUsers The maximum possible of total users.
* @param step The step that will be repeated.
* @param separator Will be injected in between the regular injection steps.
*/
case class SplitInjection(possibleUsers: Int, step: InjectionStep, separator: InjectionStep) extends InjectionStep {
private val stepUsers = step.users
private lazy val separatorUsers = separator.users

val users = {
if (possibleUsers > stepUsers)
possibleUsers - (possibleUsers - stepUsers) % (stepUsers + separator.users)
possibleUsers - (possibleUsers - stepUsers) % (stepUsers + separatorUsers)
else 0
}

override def chain(iterator: Iterator[FiniteDuration]) = {
if (possibleUsers > stepUsers) {
val separatorUsers = separator.users
val n = (possibleUsers - stepUsers) / (stepUsers + separatorUsers)
val lastScheduling = step.chain(iterator)
(1 to n).foldRight(lastScheduling)((_, iterator) => step.chain(separator.chain(iterator)))
Expand All @@ -144,7 +148,7 @@ case class HeavisideInjection(users: Int, duration: FiniteDuration) extends Inje

override def chain(iterator: Iterator[FiniteDuration]) = {
def heavisideInv(u: Int) = {
val x = u.toDouble / (users + 1)
val x = u.toDouble / (users + 2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

func([0, d]) => [1, user+1]
func([0, d[) => [1, user+2]

erfinv(2 * x - 1)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,14 @@
package io.gatling.core.scenario

import akka.actor.ActorRef

import io.gatling.core.akka.AkkaDefaults
import io.gatling.core.controller.Controller
import io.gatling.core.controller.inject.InjectionProfile
import io.gatling.core.result.message.Start
import io.gatling.core.result.writer.UserMessage
import io.gatling.core.session.Session
import io.gatling.core.util.TimeHelper.zeroMs
import io.gatling.core.util.TimeHelper._

case class Scenario(name: String, entryPoint: ActorRef, injectionProfile: InjectionProfile) extends AkkaDefaults {

Expand All @@ -39,7 +40,8 @@ case class Scenario(name: String, entryPoint: ActorRef, injectionProfile: Inject
if (startingTime == zeroMs)
startUser(index)
else
scheduler.scheduleOnce(startingTime) {
// Reduce the starting time to the millisecond precision to avoid flooding the scheduler
scheduler.scheduleOnce(toMillisPrecision(startingTime)) {
startUser(index)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ package io.gatling.core.util

import java.lang.System.{ currentTimeMillis, nanoTime }

import scala.concurrent.duration.DurationInt
import scala.concurrent.duration._

object TimeHelper {

Expand All @@ -31,4 +31,10 @@ object TimeHelper {
def computeTimeMillisFromNanos(nanos: Long) = (nanos - nanoTimeReference) / 1000000 + currentTimeMillisReference
def nowMillis = computeTimeMillisFromNanos(nanoTime)
def nowSeconds = computeTimeMillisFromNanos(nanoTime) / 1000

def toMillisPrecision(t: FiniteDuration): FiniteDuration =
t.unit match {
case MICROSECONDS | NANOSECONDS => t.toMillis.milliseconds
case _ => t
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,23 @@ class InjectionStepSpec extends Specification {
"schedule with a correct interval" in {
val interval0 = scheduling(1) - scheduling(0)
val interval1 = scheduling(2) - scheduling(1)
scheduling.length must beEqualTo(ramp.users) and (interval0 must beEqualTo(interval1)) and (interval0 must beEqualTo(250 milliseconds))
scheduling.length must beEqualTo(ramp.users) and (interval0 must beEqualTo(interval1)) and (interval0 must beEqualTo(200 milliseconds))
}

"the first and the last users should be correctly scheduled" in {
val first = scheduling.head
val last = scheduling.last
first must beEqualTo(0 second) and (last must beEqualTo(1 second)) and (scheduling must beSorted)
first must beEqualTo(0 second) and (last must be_<(1 second)) and (scheduling must beSorted)
}
}

"ConstantRateInjection" should {
val injec = ConstantRateInjection(1.0, 5 seconds)
val injec2 = ConstantRateInjection(0.4978, 100 seconds)

"return the correct number of users" in {
injec.users must beEqualTo(5) and
(injec2.users must beEqualTo(49))
}
}

Expand Down Expand Up @@ -124,20 +134,29 @@ class InjectionStepSpec extends Specification {
case (i1, i2) => i2 - i1
}.toSet

constantRampScheduling must beSorted and (steps.size must beEqualTo(1))
constantRampScheduling must beSorted and
(steps.size must beEqualTo(1)) and
(constantRampScheduling.last must be_<(10 seconds))
}
}

"SplitInjection" should {

"provide an appropriate injection scheduling and ignore extra users" in {
val scheduling = SplitInjection(10, RampInjection(3, 2 seconds), NothingForInjection(5 seconds)).chain(Iterator.empty).toList
scheduling must beEqualTo(List(0 second, 1 second, 2 seconds, 7 seconds, 8 seconds, 9 seconds, 14 seconds, 15 seconds, 16 seconds))
val scheduling = SplitInjection(6, RampInjection(2, 2 seconds), NothingForInjection(5 seconds)).chain(Iterator.empty).toList
scheduling must beEqualTo(List(
0 second, 1 second, // 1st ramp
7 seconds, 8 seconds, // 2nd ramp after a pause
14 seconds, 15 seconds)) // 3rd ramp after a pause
}

"should schedule the first and last user thru the 'into' injection step" in {
val scheduling = SplitInjection(5, RampInjection(3, 2 seconds), AtOnceInjection(1)).chain(AtOnceInjection(1).chain(Iterator.empty)).toList
scheduling must beEqualTo(List(0 second, 1 second, 2 seconds, 2 seconds))
"should schedule the first and last user through the 'into' injection step" in {
val scheduling = SplitInjection(5, RampInjection(2, 2 seconds), AtOnceInjection(1)).chain(AtOnceInjection(1).chain(Iterator.empty)).toList
scheduling must beEqualTo(List(
0 second, 1 second, // 1st ramp
2 seconds, // at once in between
2 seconds, 3 seconds, // 2nd ramp until reaching 5 users
4 seconds)) // at once from the chained injection
}
}

Expand All @@ -148,17 +167,15 @@ class InjectionStepSpec extends Specification {
scheduling.length must beEqualTo(100)
}

"be of an appropriate duration" in {
scheduling.last must beEqualTo(5 seconds)
}

"provide correct values" in {
scheduling(1) must beEqualTo(292 milliseconds) and (scheduling must beSorted)
scheduling(1) must beEqualTo(291 milliseconds) and
(scheduling must beSorted) and
(scheduling.last must be_<(5 seconds))
}

"have most of the scheduling values close to half of the duration" in {
val l = scheduling.filter((t) => (t > (1.5 seconds)) && (t < (3.5 seconds))).length
l must beEqualTo(66)
l must beEqualTo(67) // two thirds
}
}

Expand Down