Author: pmouawad Date: Tue Dec 26 20:40:32 2017 New Revision: 1819293 URL: http://svn.apache.org/viewvc?rev=1819293&view=rev Log: Bug 61931 - Exponential Timer : timer that produces poisson arrivals with given constant throughput This closes #231 Bugzilla Id: 61931
Added: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ConstantPoissonProcessGenerator.java (with props) jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/DurationProvider.java (with props) jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/EventProducer.java (with props) jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimer.java (with props) jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerBeanInfo.java (with props) jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerResources.properties (with props) jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerResources_fr.properties (with props) jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ThroughputProvider.java (with props) jmeter/trunk/test/src/org/apache/jmeter/timers/poissonarrivals/ jmeter/trunk/test/src/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerTest.java (with props) Modified: jmeter/trunk/bin/saveservice.properties jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java jmeter/trunk/xdocs/changes.xml jmeter/trunk/xdocs/usermanual/component_reference.xml Modified: jmeter/trunk/bin/saveservice.properties URL: http://svn.apache.org/viewvc/jmeter/trunk/bin/saveservice.properties?rev=1819293&r1=1819292&r2=1819293&view=diff ============================================================================== --- jmeter/trunk/bin/saveservice.properties (original) +++ jmeter/trunk/bin/saveservice.properties Tue Dec 26 20:40:32 2017 @@ -65,7 +65,7 @@ # 3.1 = 3.1 # 3.2 = 3.2 # 3.4 = 3.4 -_version=3.4 +_version=4.0 # # # Character set encoding used to read and write JMeter XML files and CSV results @@ -133,6 +133,7 @@ DNSCacheManager=org.apache.jmeter.protoc DNSCachePanel=org.apache.jmeter.protocol.http.gui.DNSCachePanel DurationAssertion=org.apache.jmeter.assertions.DurationAssertion DurationAssertionGui=org.apache.jmeter.assertions.gui.DurationAssertionGui +ExponentialTimer=org.apache.jmeter.timers.poissonarrivals.ExponentialTimer # Should really have been defined as floatProp to agree with other properties # No point changing this now FloatProperty=org.apache.jmeter.testelement.property.FloatProperty Added: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ConstantPoissonProcessGenerator.java URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ConstantPoissonProcessGenerator.java?rev=1819293&view=auto ============================================================================== --- jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ConstantPoissonProcessGenerator.java (added) +++ jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ConstantPoissonProcessGenerator.java Tue Dec 26 20:40:32 2017 @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.jmeter.timers.poissonarrivals; + +import java.nio.DoubleBuffer; +import java.util.Random; + +import org.apache.jmeter.testelement.AbstractTestElement; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Generates events for poisson processes, ensuring throughput*duration events will be present in each "duration" + * @since 4.0 + */ +public class ConstantPoissonProcessGenerator implements EventProducer { + private static final Logger log = LoggerFactory.getLogger(ConstantPoissonProcessGenerator.class); + + private Random rnd = new Random(); + public ThroughputProvider throughput; + private int batchSize; + private int batchThreadDelay; + public DurationProvider duration; + private double lastThroughput; + private int exactLimit; + private double allowedThroughputSurplus; + private DoubleBuffer events; + private double lastEvent; + private final boolean logFirstSamples; + + public ConstantPoissonProcessGenerator( + ThroughputProvider throughput, int batchSize, int batchThreadDelay, + DurationProvider duration, int exactLimit, double allowedThroughputSurplus, + Long seed, boolean logFirstSamples) { + this.throughput = throughput; + this.batchSize = batchSize; + this.batchThreadDelay = batchThreadDelay; + this.duration = duration; + this.exactLimit = exactLimit; + this.allowedThroughputSurplus = allowedThroughputSurplus; + this.logFirstSamples = logFirstSamples; + if (seed != null && seed.intValue() != 0) { + rnd.setSeed(seed); + } + ensureCapacity(); + } + + private void ensureCapacity() { + int size = (int) Math.round((throughput.getThroughput() * duration.getDuration() + 1) * 3); + if (events != null && events.capacity() >= size) { + return; + } + events = DoubleBuffer.allocate(size); + } + + public void generateNext() { + double throughput = this.throughput.getThroughput(); + lastThroughput = throughput; + if (batchSize > 1) { + throughput /= batchSize; + } + long duration = this.duration.getDuration(); + ensureCapacity(); + int samples = (int) Math.ceil(throughput * duration); + double time; + int i = 0; + long t = System.currentTimeMillis(); + int loops = 0; + double allowedThroughputSurplus = samples < exactLimit ? 0.0d : this.allowedThroughputSurplus / 100; + do { + time = 0; + events.clear(); + if (throughput < 1e-5) { + log.info("Throughput should exceed zero"); + break; + } + if (duration < 5) { + log.info("Duration should exceed 5 seconds"); + break; + } + for (i = 0; time < duration; i++) { + double u = rnd.nextDouble(); + // https://en.wikipedia.org/wiki/Exponential_distribution#Generating_exponential_variates + double delay = -Math.log(1 - u) / throughput; + time += delay; + events.put(time + lastEvent); + } + loops++; + } while (System.currentTimeMillis() - t < 5000 && + (i < samples + 1 // not enough samples + || (i - 1 - samples) * 1.0f / samples > allowedThroughputSurplus)); + t = System.currentTimeMillis() - t; + if (t > 1000) { + log.warn("Spent {} ms while generating sequence of delays for {} samples, {} throughput, {} duration", + t, samples, throughput, duration); + } + if (logFirstSamples) { + if (log.isDebugEnabled()) { + log.debug("Generated {} events ({} required, rate {}) in {} ms, restart was issued {} times", + events.position(), samples, throughput, t, loops); + } + if(log.isInfoEnabled()) { + StringBuilder sb = new StringBuilder(); + sb.append("Generated ").append(events.position()).append(" timings ("); + if (this.duration instanceof AbstractTestElement) { + sb.append(((AbstractTestElement) this.duration).getName()); + } + sb.append(" ").append(samples).append(" required, rate ").append(throughput).append(", duration ").append(duration); + sb.append(", exact lim ").append(exactLimit).append(", i").append(i); + sb.append(") in ").append(t).append(" ms, restart was issued ").append(loops).append(" times. "); + sb.append("First 15 events will be fired at: "); + double prev = 0; + for (i = 0; i < events.position() && i < 15; i++) { + if (i > 0) { + sb.append(", "); + } + double ev = events.get(i); + sb.append(ev); + sb.append(" (+").append(ev - prev).append(")"); + prev = ev; + } + log.info(sb.toString()); + } + + } + events.flip(); + if (batchSize > 1) { + // If required to generate "pairs" of events, then just duplicate events in the buffer + // TODO: for large batchSizes it makes sense to use counting instead + DoubleBuffer tmpBuffer = DoubleBuffer.allocate(batchSize * events.remaining()); + while (events.hasRemaining()) { + double curTime = events.get(); + for (int j = 0; j < batchSize; j++) { + tmpBuffer.put(curTime + j * batchThreadDelay); + } + } + tmpBuffer.flip(); + events = tmpBuffer; + } + } + + @Override + public double next() { + if (!events.hasRemaining() || throughput.getThroughput() != lastThroughput) { + generateNext(); + } + lastEvent = events.get(); + return lastEvent; + } +} Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ConstantPoissonProcessGenerator.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ConstantPoissonProcessGenerator.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/DurationProvider.java URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/DurationProvider.java?rev=1819293&view=auto ============================================================================== --- jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/DurationProvider.java (added) +++ jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/DurationProvider.java Tue Dec 26 20:40:32 2017 @@ -0,0 +1,27 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.jmeter.timers.poissonarrivals; + +/** + * @since 4.0 + */ +@FunctionalInterface +interface DurationProvider { + long getDuration(); +} Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/DurationProvider.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/DurationProvider.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/EventProducer.java URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/EventProducer.java?rev=1819293&view=auto ============================================================================== --- jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/EventProducer.java (added) +++ jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/EventProducer.java Tue Dec 26 20:40:32 2017 @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.jmeter.timers.poissonarrivals; + +/** + * @since 4.0 + * + */ +@FunctionalInterface +interface EventProducer { + double next(); +} Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/EventProducer.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/EventProducer.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimer.java URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimer.java?rev=1819293&view=auto ============================================================================== --- jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimer.java (added) +++ jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimer.java Tue Dec 26 20:40:32 2017 @@ -0,0 +1,215 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.jmeter.timers.poissonarrivals; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; + +import org.apache.jmeter.testbeans.TestBean; +import org.apache.jmeter.testelement.AbstractTestElement; +import org.apache.jmeter.testelement.TestStateListener; +import org.apache.jmeter.threads.AbstractThreadGroup; +import org.apache.jmeter.timers.Timer; +import org.apache.jorphan.util.JMeterStopThreadException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This timer generates poisson arrivals with constant throughput. + * On top of that, it tries to maintain the exact amount of arrivals for a given timeframe ({@link #throughputPeriod}. + * @since 4.0 + */ +public class ExponentialTimer extends AbstractTestElement implements Cloneable, Timer, TestStateListener, TestBean, ThroughputProvider, DurationProvider { + private static final Logger log = LoggerFactory.getLogger(ExponentialTimer.class); + + private static final long serialVersionUID = 3; + private static final ConcurrentMap<AbstractThreadGroup, EventProducer> groupEvents = new ConcurrentHashMap<>(); + + /** + * Desired throughput configured as {@code throughput/throughputPeriod} per second. + */ + private double throughput; + private int throughputPeriod; + + /** + * This is used to ensure you'll get {@code duration*throughput/throughputPeriod} samples during "test duration" timeframe. + * Even though arrivals are random, business users want to see round numbers in reports like "100 samples per hour", + * so the timer picks only those random arrivals that end up with round total numbers. + */ + private long duration; + + private long testStarted; + + /** + * When number of required samples exceeds {@code exactLimit}, random generator would resort to approximate match of + * number of generated samples. + */ + private int exactLimit; + private double allowedThroughputSurplus; + + /** + * This enables to reproduce exactly the same sequence of delays by reusing the same seed. + */ + private Long randomSeed; + + /** + * This enables to generate events in batches (e.g. pairs of events with {@link #batchThreadDelay} sec in between) + * TODO: this should be either rewritten to double / ms, or dropped in favour of other approach + */ + private int batchSize; + private int batchThreadDelay; + + @Override + public Object clone() { + final ExponentialTimer newTimer = (ExponentialTimer) super.clone(); + newTimer.testStarted = testStarted; // JMeter cloning does not clone fields + return newTimer; + } + + @Override + public void testStarted() { + testStarted(null); + } + + @Override + public void testStarted(String host) { + groupEvents.clear(); + testStarted = System.currentTimeMillis(); + } + + @Override + public void testEnded() { + // NOOP + } + + @Override + public void testEnded(String s) { + // NOOP + } + + @Override + public long delay() { + double nextEvent; + EventProducer events = getEventProducer(); + synchronized (events) { + nextEvent = events.next(); + } + long delay = (long) (nextEvent * TimeUnit.SECONDS.toMillis(1) + testStarted - System.currentTimeMillis()); + if (log.isDebugEnabled()) { + log.debug("Calculated delay is {}", delay); + } + delay = Math.max(0, delay); + long endTime = getThreadContext().getThread().getEndTime(); + if (endTime > 0 && System.currentTimeMillis() + delay > endTime) { + throw new JMeterStopThreadException("The thread is scheduled to stop in " + + (System.currentTimeMillis() - endTime) + " ms" + + " and the throughput timer generates a delay of " + delay + "." + + " JMeter (as of 4.0) does not support interrupting of sleeping threads, thus terminating the thread manually." + ); + } + return delay; + } + + private EventProducer getEventProducer() { + AbstractThreadGroup tg = getThreadContext().getThreadGroup(); + Long seed = randomSeed == null || randomSeed == 0 ? null : randomSeed; + return + groupEvents.computeIfAbsent(tg, x -> new ConstantPoissonProcessGenerator( + () -> ExponentialTimer.this.getThroughput() / throughputPeriod, + batchSize, batchThreadDelay, this, exactLimit, allowedThroughputSurplus, seed, true)); + } + + /** + * Returns number of generated samples per {@link #getThroughputPeriod} + * @return number of samples per {@link #getThroughputPeriod} + */ + public double getThroughput() { + return throughput; + } + + /** + * Sets number of generated samples per {@link #getThroughputPeriod} + * @param throughput number of samples per {@link #getThroughputPeriod} + */ + public void setThroughput(double throughput) { + this.throughput = throughput; + } + + /** + * Allows to use business values for throughput configuration. + * For instance, 100 samples per hour vs 100 samples per minute. + * @return + */ + public int getThroughputPeriod() { + return throughputPeriod; + } + + public void setThroughputPeriod(int throughputPeriod) { + this.throughputPeriod = throughputPeriod; + } + + public long getDuration() { + return duration; + } + + public void setDuration(long duration) { + this.duration = duration; + } + + public int getExactLimit() { + return exactLimit; + } + + public void setExactLimit(int exactLimit) { + this.exactLimit = exactLimit; + } + + public double getAllowedThroughputSurplus() { + return allowedThroughputSurplus; + } + + public void setAllowedThroughputSurplus(double allowedThroughputSurplus) { + this.allowedThroughputSurplus = allowedThroughputSurplus; + } + + public Long getRandomSeed() { + return randomSeed; + } + + public void setRandomSeed(Long randomSeed) { + this.randomSeed = randomSeed; + } + + public int getBatchSize() { + return batchSize; + } + + public void setBatchSize(int batchSize) { + this.batchSize = batchSize; + } + + public int getBatchThreadDelay() { + return batchThreadDelay; + } + + public void setBatchThreadDelay(int batchThreadDelay) { + this.batchThreadDelay = batchThreadDelay; + } +} Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimer.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimer.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerBeanInfo.java URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerBeanInfo.java?rev=1819293&view=auto ============================================================================== --- jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerBeanInfo.java (added) +++ jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerBeanInfo.java Tue Dec 26 20:40:32 2017 @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.jmeter.timers.poissonarrivals; + +import java.beans.PropertyDescriptor; + +import org.apache.jmeter.testbeans.BeanInfoSupport; + +/** + * @since 4.0 + */ +public class ExponentialTimerBeanInfo extends BeanInfoSupport { + public ExponentialTimerBeanInfo() { + super(ExponentialTimer.class); + createPropertyGroup( + "delay", //$NON-NLS-1$ + new String[]{ + "throughput", //$NON-NLS-1$ + "throughputPeriod", //$NON-NLS-1$ + "duration", //$NON-NLS-1$ + } + ); + + PropertyDescriptor p; + p = property("throughput"); //$NON-NLS-1$ + p.setValue(NOT_UNDEFINED, Boolean.TRUE); + p.setValue(DEFAULT, Double.valueOf(100)); + + p = property("throughputPeriod"); //$NON-NLS-1$ + p.setValue(NOT_UNDEFINED, Boolean.TRUE); + p.setValue(DEFAULT, 3600); + + p = property("duration"); //$NON-NLS-1$ + p.setValue(NOT_UNDEFINED, Boolean.TRUE); + p.setValue(DEFAULT, Long.valueOf(3600)); + + createPropertyGroup( + "batching", //$NON-NLS-1$ + new String[] { + "batchSize" + , "batchThreadDelay" + } + ); + + p = property("batchSize"); //$NON-NLS-1$ + p.setValue(NOT_UNDEFINED, Boolean.TRUE); + p.setValue(DEFAULT, 1); + + p = property("batchThreadDelay"); //$NON-NLS-1$ + p.setValue(NOT_UNDEFINED, Boolean.TRUE); + p.setValue(DEFAULT, 0); + + + createPropertyGroup( + "accuracy", //$NON-NLS-1$ + new String[]{ + "exactLimit", //$NON-NLS-1$ + "allowedThroughputSurplus" //$NON-NLS-1$ + } + ); + + p = property("exactLimit"); //$NON-NLS-1$ + p.setValue(NOT_UNDEFINED, Boolean.TRUE); + p.setValue(DEFAULT, Integer.valueOf(10000)); + + p = property("allowedThroughputSurplus"); //$NON-NLS-1$ + p.setValue(NOT_UNDEFINED, Boolean.TRUE); + p.setValue(DEFAULT, Double.valueOf(1.0d)); + + createPropertyGroup( + "repeatability", //$NON-NLS-1$ + new String[]{ + "randomSeed" //$NON-NLS-1$ + } + ); + + p = property("randomSeed"); //$NON-NLS-1$ + p.setValue(NOT_UNDEFINED, Boolean.TRUE); + p.setValue(DEFAULT, Long.valueOf(0)); + } +} Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerBeanInfo.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerBeanInfo.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerResources.properties URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerResources.properties?rev=1819293&view=auto ============================================================================== --- jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerResources.properties (added) +++ jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerResources.properties Tue Dec 26 20:40:32 2017 @@ -0,0 +1,36 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +displayName=Exponential Timer +delay.displayName=Delay threads to ensure target throughput +throughput.displayName=Target throughput (in samples per "throughput period") +throughput.shortDescription=Maximum number of samples you want to obtain per "throughput period", including all threads in group, from all affected samplers +throughputPeriod.displayName=Throughput period (seconds) +throughputPeriod.shortDescription=Throughput period. For example, if "throughput" is set to 42 and "throughput period" to 21 sec, then you'll get 2 samples per second. +duration.displayName=Test duration (seconds) +duration.shortDescription=This is used to ensure you'll get throughput*duration samples during "test duration" timeframe +accuracy.displayName=Accuracy of generated delays +exactLimit.displayName=Use approximate throughput when sequence length exceeds (samples) +exactLimit.shortDescription=When the required number of samples is less than this limit, timer will generate exact number of samples +allowedThroughputSurplus.displayName=Allowed throughput surplus (percents) +allowedThroughputSurplus.shortDescription=When more than "max exact samples" samples is required, timer might generate slightly more events than specified by throughput +repeatability.displayName=Setting to ensure repeatable sequence +randomSeed.displayName=Random seed (change from 0 to random) +randomSeed.shortDescription=Note: different timers should better have different seed values. Constant seed ensures timer generates the same delays each test start. The value of "0" means the timer is truly random (non-repeatable from one execution to another). +batching.displayName=Batched departures +batchSize.displayName=Number of threads in the batch (threads) +batchSize.shortDescription=If the value exceeds 1, then multiple threads depart from the timer simultaneously. Average throughput still meets "throughput" value +batchThreadDelay.displayName=Delay between threads in the batch (ms) +batchThreadDelay.shortDescription=For instance, if set to 42, and the batch size is 3, then threads will depart at x, x+42ms, x+84ms Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerResources.properties ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerResources.properties ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerResources_fr.properties URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerResources_fr.properties?rev=1819293&view=auto ============================================================================== --- jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerResources_fr.properties (added) +++ jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerResources_fr.properties Tue Dec 26 20:40:32 2017 @@ -0,0 +1,36 @@ +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +displayName=Exponential Timer +delay.displayName=Delay threads to ensure target throughput +throughput.displayName=Target throughput (in samples per "throughput period") +throughput.shortDescription=Maximum number of samples you want to obtain per "throughput period", including all threads in group, from all affected samplers +throughputPeriod.displayName=Throughput period (seconds) +throughputPeriod.shortDescription=Throughput period. For example, if "throughput" is set to 42 and "throughput period" to 21 sec, then you'll get 2 samples per second. +duration.displayName=Test duration (seconds) +duration.shortDescription=This is used to ensure you'll get throughput*duration samples during "test duration" timeframe +accuracy.displayName=Accuracy of generated delays +exactLimit.displayName=Use approximate throughput when sequence length exceeds (samples) +exactLimit.shortDescription=When the required number of samples is less than this limit, timer will generate exact number of samples +allowedThroughputSurplus.displayName=Allowed throughput surplus (percents) +allowedThroughputSurplus.shortDescription=When more than "max exact samples" samples is required, timer might generate slightly more events than specified by throughput +repeatability.displayName=Setting to ensure repeatable sequence +randomSeed.displayName=Random seed (change from 0 to random) +randomSeed.shortDescription=Note: different timers should better have different seed values. Constant seed ensures timer generates the same delays each test start. The value of "0" means the timer is truly random (non-repeatable from one execution to another). +batching.displayName=Batched departures +batchSize.displayName=Number of threads in the batch (threads) +batchSize.shortDescription=If the value exceeds 1, then multiple threads depart from the timer simultaneously. Average throughput still meets "throughput" value +batchThreadDelay.displayName=Delay between threads in the batch (ms) +batchThreadDelay.shortDescription=For instance, if set to 42, and the batch size is 3, then threads will depart at x, x+42ms, x+84ms Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerResources_fr.properties ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerResources_fr.properties ------------------------------------------------------------------------------ svn:mime-type = text/plain Added: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ThroughputProvider.java URL: http://svn.apache.org/viewvc/jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ThroughputProvider.java?rev=1819293&view=auto ============================================================================== --- jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ThroughputProvider.java (added) +++ jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ThroughputProvider.java Tue Dec 26 20:40:32 2017 @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.jmeter.timers.poissonarrivals; + +/** + * @since 4.0 + * + */ +@FunctionalInterface +interface ThroughputProvider { + double getThroughput(); +} Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ThroughputProvider.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jmeter/trunk/src/components/org/apache/jmeter/timers/poissonarrivals/ThroughputProvider.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java URL: http://svn.apache.org/viewvc/jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java?rev=1819293&r1=1819292&r2=1819293&view=diff ============================================================================== --- jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java (original) +++ jmeter/trunk/src/core/org/apache/jmeter/save/SaveService.java Tue Dec 26 20:40:32 2017 @@ -149,13 +149,13 @@ public class SaveService { // Must match _version property value in saveservice.properties // used to ensure saveservice.properties and SaveService are updated simultaneously - static final String PROPVERSION = "3.4";// Expected version $NON-NLS-1$ + static final String PROPVERSION = "4.0";// Expected version $NON-NLS-1$ // Internal information only private static String fileVersion = ""; // computed from saveservice.properties file// $NON-NLS-1$ // Must match the sha1 checksum of the file saveservice.properties (without newline character), // used to ensure saveservice.properties and SaveService are updated simultaneously - static final String FILEVERSION = "0acd8200bf252acf41d5eeca6aa56b0eaee06f0f"; // Expected value $NON-NLS-1$ + static final String FILEVERSION = "c616712e26251d15588fbe615d1c31a34dbbf854"; // Expected value $NON-NLS-1$ private static String fileEncoding = ""; // read from properties file// $NON-NLS-1$ Added: jmeter/trunk/test/src/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerTest.java URL: http://svn.apache.org/viewvc/jmeter/trunk/test/src/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerTest.java?rev=1819293&view=auto ============================================================================== --- jmeter/trunk/test/src/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerTest.java (added) +++ jmeter/trunk/test/src/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerTest.java Tue Dec 26 20:40:32 2017 @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.jmeter.timers.poissonarrivals; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ExponentialTimerTest { + private static final Logger LOG = LoggerFactory.getLogger(ExponentialTimerTest.class); + + @Test + public void testTimer1() throws Exception { + ConstantPoissonProcessGenerator gen = getConstantPoissonProcessGenerator(2, 5, 42L); + gen.generateNext(); + assertEquals(0.6501751901910952, gen.next(), 0.01); + assertEquals(1.2249545461599474, gen.next(), 0.01); + assertEquals(1.409559315928937, gen.next(), 0.01); + assertEquals(1.5717866281130652, gen.next(), 0.01); + assertEquals(2.1194190047658874, gen.next(), 0.01); + assertEquals(3.2878637366551384, gen.next(), 0.01); + assertEquals(3.517916456559849, gen.next(), 0.01); + assertEquals(3.679224444929692, gen.next(), 0.01); + assertEquals(3.9907119513763165, gen.next(), 0.01); + assertEquals(4.754414649148714, gen.next(), 0.01); + // ^^ 10 samples for 5 seconds + assertEquals(6.013095167372755, gen.next(), 0.01); + } + + @Test + public void testExactNumberOfSamples() throws Exception { + java.util.Random rnd = new java.util.Random(); + long seed = rnd.nextLong(); + // Log seed, so the test can be reproduced in case of failure + LOG.info("testExactNumberOfSamples is using seed " + seed); + rnd.setSeed(seed); + + int testDuration = 5; + for (int i = 0; i < 1000; i++) { + ConstantPoissonProcessGenerator gen = getConstantPoissonProcessGenerator(2, testDuration, rnd.nextLong()); + gen.generateNext(); + for (int j = 0; j < 10; j++) { + double next = gen.next(); + assertTrue("Delay #" + j + " (0-based) exceeds " + testDuration + " seconds", next < 5.0); + } + } + } + + protected ConstantPoissonProcessGenerator getConstantPoissonProcessGenerator( + final double throughput, final int duration, long seed) { + return new ConstantPoissonProcessGenerator( + new ThroughputProvider() { + @Override + public double getThroughput() { + return throughput; // samples per second + } + }, + 1, + 0, + new DurationProvider() { + @Override + public long getDuration() { + return duration; // "expected" test duration: 3 seconds + } + }, + 10000, + 0.1, + seed, // Seed + false + ); + } + +} Propchange: jmeter/trunk/test/src/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerTest.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: jmeter/trunk/test/src/org/apache/jmeter/timers/poissonarrivals/ExponentialTimerTest.java ------------------------------------------------------------------------------ svn:mime-type = text/plain Modified: jmeter/trunk/xdocs/changes.xml URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/changes.xml?rev=1819293&r1=1819292&r2=1819293&view=diff ============================================================================== --- jmeter/trunk/xdocs/changes.xml [utf-8] (original) +++ jmeter/trunk/xdocs/changes.xml [utf-8] Tue Dec 26 20:40:32 2017 @@ -128,7 +128,8 @@ Summary <h3>Timers, Assertions, Config, Pre- & Post-Processors</h3> <ul> - <li><bug>60213</bug>Boundary based extractor</li> + <li><bug>60213</bug>New component : Boundary based extractor</li> + <li><bug>61931</bug>New Component : Exponential Timer, timer that produces poisson arrivals with given constant throughput </li> <li><bug>61644</bug>HTTP Cache Manager: "Use Cache-Control/Expires header when processing GET requests" should be checked by default</li> <li><bug>61645</bug>Response Assertion: Add ability to assert on Request Data</li> <li><bug>61534</bug>Convert AssertionError to a failed assertion in the JSR223Assertion allowing users to use assert in their code</li> @@ -136,6 +137,7 @@ Summary <li><bug>61758</bug><code>Apply to:</code> field in Extractors, Assertions : When entering a value in <code>JMeter Variable Name</code>, the radio box <code>JMeter Variable Name</code> should be selected by default. Contributed by Ubik Load Pack (support at ubikloadpack.com)</li> <li><bug>61845</bug>New Component JSON Assertion based on AtlanBH JSON Path Assertion donated to JMeter-Plugins and migrated into JMeter core by Artem Fedorov (artem at blazemeter.com)</li> <li><bug>61846</bug>Scoped Assertion should follow same order of evaluation as Post Processors</li> + </ul> <h3>Functions</h3> Modified: jmeter/trunk/xdocs/usermanual/component_reference.xml URL: http://svn.apache.org/viewvc/jmeter/trunk/xdocs/usermanual/component_reference.xml?rev=1819293&r1=1819292&r2=1819293&view=diff ============================================================================== --- jmeter/trunk/xdocs/usermanual/component_reference.xml (original) +++ jmeter/trunk/xdocs/usermanual/component_reference.xml Tue Dec 26 20:40:32 2017 @@ -5310,6 +5310,39 @@ to the random delay.</property> </component> +<component name="Exponential Timer" index="§-num;.6.10" width="341" height="182" screenshot="timers/exponential_timer.png"> +<description><p>This timer produces poisson arrivals with given constant throughput. It provides the following features: +<ul> + <li><i>Specific number of iterations per hour</i>. + The very basic requirement is to ensure you end up exactly 50 + iterations per hour.</li> + <li><i>Bursty load</i> simulation. There is no easy way to test "50 + iterations per hour as 10 bursts of 5 items".</li> + <li><i>Repeatable test profile</i>. All the random timers produce different + pattern on each test run. This is not good for low-level analysis + (e.g. compare of CPU% charts, etc).</li> + <li> + <li>Avoid all thread groups</li> to fire at 00:00:00. By default, all + thread groups would fire at 0, so there would be noticeable spike at + the start of the test.</li> +</ul> +</p></description> + + +<properties> + <property name="Name" required="No">Descriptive name for this timer that is shown in the tree</property> + <property name="Target throughput (in samples per 'throughput period')" required="Yes">Maximum number of samples you want to obtain per "throughput period", including all threads in group, from all affected samplers.</property> + <property name="Throughput period (seconds)" required="Yes">Throughput period. For example, if "throughput" is set to 42 and "throughput period" to 21 sec, then you'll get 2 samples per second.</property> + <property name="Test duration (seconds)" required="Yes">This is used to ensure you'll get throughput*duration samples during "test duration" timeframe.</property> + <property name="Number of threads in the batch (threads)" required="Yes">If the value exceeds 1, then multiple threads depart from the timer simultaneously. Average throughput still meets "throughput" value.</property> + <property name="Delay between threads in the batch (ms)" required="Yes">For instance, if set to 42, and the batch size is 3, then threads will depart at x, x+42ms, x+84ms.</property> + <property name="Use approximate throughput when sequence length exceeds (samples)" required="Yes">When the required number of samples is less than this limit, timer will generate exact number of samples.</property> + <property name="Allowed throughput surplus (percents)" required="Yes">When more than "max exact samples" samples is required, timer might generate slightly more events than specified by throughput.</property> + <property name="Random seed (change from 0 to random)" required="Yes">Note: different timers should better have different seed values. Constant seed ensures timer generates the same delays each test start. The value of "0" means the timer is truly random (non-repeatable from one execution to another)..</property> +</properties> + +</component> + <a href="#">^</a> </section>