This is an automated email from the ASF dual-hosted git repository. vladimirsitnikov pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/jmeter.git
commit d0bdbafd7bbedb5e9fc32855740b52cda4282638 Author: Vladimir Sitnikov <[email protected]> AuthorDate: Thu Mar 2 11:14:47 2023 +0300 fix: disable FunctionProperty caching Previously it cached the values based on iteration number only which triggered wrong results on concurrent executions. The previous behavior can be temporary restored with function.cache.per.iteration=true property. --- bin/jmeter.properties | 7 +++++++ .../jmeter/testelement/property/FunctionProperty.java | 19 +++++++++++++++++-- xdocs/changes.xml | 4 ++++ xdocs/usermanual/properties_reference.xml | 9 +++++++++ 4 files changed, 37 insertions(+), 2 deletions(-) diff --git a/bin/jmeter.properties b/bin/jmeter.properties index 69b77d46bd..410ae16819 100644 --- a/bin/jmeter.properties +++ b/bin/jmeter.properties @@ -1055,6 +1055,13 @@ csvdataset.file.encoding_list=UTF-8|UTF-16|ISO-8859-15|US-ASCII # ORO PatternCacheLRU size #oro.patterncache.size=1000 +# Cache function execution during test execution +# By default, JMeter caches function properties, however, it might cause unexpected results +# when the component is shared across threads and the expression depends on the thread variables. +# The caching behaviour would likely change in the upcoming versions +# Deprecation notice: the setting will likely disappear, so if you need it, consider raising an issue with the use-case. +#function.cache.per.iteration=false + #TestBeanGui # #propertyEditorSearchPath=null diff --git a/src/core/src/main/java/org/apache/jmeter/testelement/property/FunctionProperty.java b/src/core/src/main/java/org/apache/jmeter/testelement/property/FunctionProperty.java index a4adf02774..238ec0184f 100644 --- a/src/core/src/main/java/org/apache/jmeter/testelement/property/FunctionProperty.java +++ b/src/core/src/main/java/org/apache/jmeter/testelement/property/FunctionProperty.java @@ -21,19 +21,28 @@ import org.apache.jmeter.engine.util.CompoundVariable; import org.apache.jmeter.testelement.TestElement; import org.apache.jmeter.threads.JMeterContext; import org.apache.jmeter.threads.JMeterContextService; +import org.apache.jmeter.util.JMeterUtils; /** * Class that implements the Function property */ public class FunctionProperty extends AbstractProperty { private static final long serialVersionUID = 233L; + private static final boolean FUNCTION_CACHE_PER_ITERATION = + JMeterUtils.getPropDefault("function.cache.per.iteration", false); private transient CompoundVariable function; private int testIteration = -1; + /** + * The cache will be removed in the subsequent releases. + * For now, it is kept for backward compatibility. + */ private String cacheValue; + private String overrideValue; + public FunctionProperty(String name, CompoundVariable func) { super(name); function = func; @@ -48,7 +57,7 @@ public class FunctionProperty extends AbstractProperty { if (v instanceof CompoundVariable && !isRunningVersion()) { function = (CompoundVariable) v; } else { - cacheValue = v.toString(); + overrideValue = v.toString(); } } @@ -87,7 +96,11 @@ public class FunctionProperty extends AbstractProperty { log.debug("Not running version, return raw function string"); return function.getRawParameters(); } - if(!ctx.isSamplingStarted()) { + String overrideValue = this.overrideValue; + if (overrideValue != null) { + return overrideValue; + } + if (!FUNCTION_CACHE_PER_ITERATION || !ctx.isSamplingStarted()) { return function.execute(); } log.debug("Running version, executing function"); @@ -115,6 +128,7 @@ public class FunctionProperty extends AbstractProperty { public FunctionProperty clone() { FunctionProperty prop = (FunctionProperty) super.clone(); prop.cacheValue = cacheValue; + prop.overrideValue = overrideValue; prop.testIteration = testIteration; prop.function = function; return prop; @@ -126,5 +140,6 @@ public class FunctionProperty extends AbstractProperty { @Override public void recoverRunningVersion(TestElement owner) { cacheValue = null; + overrideValue = null; } } diff --git a/xdocs/changes.xml b/xdocs/changes.xml index f212ecfbf5..449fa8f481 100644 --- a/xdocs/changes.xml +++ b/xdocs/changes.xml @@ -104,6 +104,10 @@ Summary <li><pr>5899</pr>Speed up CPU-bound tests by skipping <code>recoverRunningVersion</code> for elements that are shared between threads (the ones that implement <code>NoThreadClone</code>)</li> <li><pr>5914</pr>Use <code>Locale.ROOT</code> instead of default locale for <code>toUpperCase</code>, and <code>toLowerCase</code> to avoid surprises with dotless I in <code>tr_TR</code> locale</li> <li><pr>5885</pr>Use Java's <code>ServiceLoader</code> for loading plugins instead of classpath scanning. It enables faster startup</li> + <li><pr>5788</pr><code>FunctionProperty</code> no longer caches the value. + Previously it cached the values based on iteration number only which triggered wrong results on concurrent executions. + The previous behavior can be temporary restored with <code>function.cache.per.iteration</code> property. + </li> </ul> <ch_section>Non-functional changes</ch_section> diff --git a/xdocs/usermanual/properties_reference.xml b/xdocs/usermanual/properties_reference.xml index 68441b1bb8..cb32afcefa 100644 --- a/xdocs/usermanual/properties_reference.xml +++ b/xdocs/usermanual/properties_reference.xml @@ -1339,6 +1339,15 @@ JMETER-SERVER</source> ORO PatternCacheLRU size.<br/> Defaults to: <code>1000</code> </property> +<property name="function.cache.per.iteration"> + <p>Cache function execution during test execution.</p> + <p>By default, JMeter caches function properties during a test iteration, however, + it might cause unexpected results when a component is shared across threads and the expression depends on + the thread variables.</p> + <note>The property will likely be removed in an upcoming version, so if you need it consider raising + an issue with your use-case.</note> + Defaults to: <code>false</code> +</property> <property name="propertyEditorSearchPath"> TestBeanGui<br/> Defaults to: <code>null</code>
