kg.apc.jmeter.timers.functions.TSTFeedback Maven / Gradle / Ivy
The newest version!
package kg.apc.jmeter.timers.functions;
import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.functions.AbstractFunction;
import org.apache.jmeter.functions.InvalidVariableException;
import org.apache.jmeter.gui.MainFrame;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
import org.apache.jmeter.testelement.TestStateListener;
import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.LoggerFactory;
import org.slf4j.Logger;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
public class TSTFeedback extends AbstractFunction implements TestStateListener {
private static final Logger log = LoggerFactory.getLogger(TSTFeedback.class);
private static final List desc = new LinkedList<>();
private static final String KEY = "__tstFeedback";
// Number of parameters expected - used to reject invalid calls
private static final int MIN_PARAMETER_COUNT = 2;
static {
desc.add("Name of Throughput Shaping Timer to integrate with");
desc.add("Starting concurrency");
desc.add("Max concurrency");
desc.add("Spare threads ratio");
}
private CompoundVariable[] values;
private boolean justStarted = true;
@Override
public synchronized String execute(SampleResult previousResult, Sampler currentSampler) {
String tstName = values[0].execute();
String concName = tstName + "_concurrency";
if (justStarted) {
JMeterUtils.setProperty(concName, String.valueOf(values[1].execute()));
justStarted = false;
}
int limit = Integer.MAX_VALUE;
if (values.length > 2) {
try {
limit = Integer.parseInt(values[2].execute());
} catch (NumberFormatException exc) {
log.debug("Failed to parse value for limit, defaulting to infinity", exc);
}
}
double spare = 0.1; //TODO: parameterize it somehow?
if (values.length > 3) { // We have a 3rd parameter
try {
spare = Double.parseDouble(values[3].execute());
} catch (NumberFormatException exc) {
log.debug("Failed to parse value for spare ratio, defaulting to 0", exc);
spare = 1;
}
}
int con = Integer.parseInt(JMeterUtils.getPropDefault(concName, "1"));
int delayed = Integer.parseInt(JMeterUtils.getPropDefault(tstName + "_cntDelayed", "0"));
int sent = Integer.parseInt(JMeterUtils.getPropDefault(tstName + "_cntSent", "0"));
float rps = Float.parseFloat(JMeterUtils.getPropDefault(tstName + "_rps", "0"));
int needed = con;
if (rps <= 0) {
// no action needed
} else if (delayed > 0) {
needed = decreaseNeeded(spare, con, delayed, needed);
} else if (sent < rps) {
needed = (int) Math.ceil(con * (2 - sent / rps));
}
if (needed != con && log.isDebugEnabled()) {
log.debug("Need to change " + concName + ": " + con + "=>" + needed + " (" + sent + "/" + rps + "/" + delayed + ")");
}
if (needed <= 0) {
log.warn("Got concurrency less than zero: " + needed);
needed = 1;
}
if (needed > limit) {
log.warn("Got concurrency more than limit: " + needed);
needed = limit;
}
JMeterUtils.setProperty(concName, String.valueOf(needed));
JMeterUtils.setProperty(tstName + "_rps", "0");
return String.valueOf(needed);
}
private int decreaseNeeded(double spare, int con, int delayed, int needed) {
if (spare >= 1) { // absolute count
needed -= delayed - spare;
if (con > spare) {
return (int) Math.max(spare, needed);
}
} else {
if (delayed > Math.ceil(con * spare)) {
needed = (int) (con * (1 - spare));
}
}
return needed;
}
@Override
public synchronized void setParameters(Collection parameters) throws InvalidVariableException {
checkParameterCount(parameters, MIN_PARAMETER_COUNT, desc.size());
values = parameters.toArray(new CompoundVariable[0]);
}
@Override
public String getReferenceKey() {
return KEY;
}
@Override
public List getArgumentDesc() {
return desc;
}
@Override
public void testStarted() {
testStarted(MainFrame.LOCAL);
}
@Override
public void testStarted(String s) {
justStarted = true;
}
@Override
public void testEnded() {
testEnded(MainFrame.LOCAL);
}
@Override
public void testEnded(String s) {
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy