Add support for Poisson injector. Allows randomized load profiles.

This is a rewrite of #1987. Injection now runs for a fixed duration. As a consequence, the number of users can vary from run-to run, depending on the seed, but is deterministic for a given seed.

Also, some of the maths in the original PR was a little suspect, so I've switched to Lewis and Shedler's algorithm, which should be more accurate.
Interesting - thanks for the details.


On Tue, Jul 1, 2014 at 4:18 PM, James Pickering <notifications@github.com>
wrote:

> In
> gatling-core/src/main/scala/io/gatling/core/controller/inject/InjectionStep.scala:
>
> > +    val maxLambda = startRate max endRate
> > +      def shouldKeep(d: Double) = {
> > +        val actualLambda = startRate + (endRate - startRate) * d / durationSecs
> > +        rand.nextDouble() < actualLambda / maxLambda
> > +      }
> > +
> > +    val rawIntervals = Iterator.continually {
> > +      val u = rand.nextDouble()
> > +      -math.log(u) / maxLambda
> > +    }
> > +
> > +    rawIntervals
> > +      .scanLeft(0.0)(_ + _) // Rolling sum
> > +      .drop(1) // Throw away first value of 0.0. It is not random, but a quirk of using scanLeft
> > +      .takeWhile(_ < durationSecs)
> > +      .flatMap { d =>
>
> Three times, actually. Try:
>
> def debugTrue = {
>   new Exception().printStackTrace()
>   true
> }
> Iterator(1).collect{case x if debugTrue => x}.toList
>
> It gets called once by hasNext, and twice by next. Once to check whether
> it should skip the current element, and once to actually get the element.
>
> I think it's kind of a hangover from Scala's early days. PartialFunction
> is often used with the if (pf.isDefined(x)) pf.apply(x) else ??? idiom,
> rather than applyOrElse(x, ???).
>
> —
> Reply to this email directly or view it on GitHub
> <https://github.com/excilys/gatling/pull/1990/files#r14406123>.
>