You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jmeter.apache.org by vl...@apache.org on 2023/05/19 12:59:05 UTC
[jmeter] branch master updated: perf: cache datetime formatter in __time function
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
The following commit(s) were added to refs/heads/master by this push:
new 6e637e9f7a perf: cache datetime formatter in __time function
6e637e9f7a is described below
commit 6e637e9f7a3de4234fe411348a7b46dfa3c88a0d
Author: Vladimir Sitnikov <si...@gmail.com>
AuthorDate: Sun Mar 26 12:31:08 2023 +0200
perf: cache datetime formatter in __time function
Previous JMeter version used SimpleDateFormat for the time format,
and u had a different meaning.
We log a warning only at the first encounter of a problematic pattern.
This implementation has a few loopholes/problems:
* 'u' could still be used for something different and the year
could be coming from a 'YYYY' format string
Co-authored-by: Felix Schumacher <fe...@internetallee.de>
Part of #5694
Closes https://github.com/apache/jmeter/pull/5796
---
.../apache/jmeter/resources/messages.properties | 2 +-
.../apache/jmeter/resources/messages_fr.properties | 2 +-
.../org/apache/jmeter/functions/TimeFunction.java | 71 ++++++++++++++++++----
xdocs/changes.xml | 1 +
4 files changed, 61 insertions(+), 15 deletions(-)
diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages.properties
index 535e175cd2..851b4a63ed 100644
--- a/src/core/src/main/resources/org/apache/jmeter/resources/messages.properties
+++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages.properties
@@ -1333,7 +1333,7 @@ throughput_control_perthread_label=Per User
throughput_control_title=Throughput Controller
throughput_control_tplabel=Throughput
time_format=Format string for SimpleDateFormat (optional)
-time_format_changed=Formatters for time function has been changed from SimpleDateFormat to DateTimeFormatter. Especially the meaning of 'u' has changed from day-of-week to year. Please check and update your format strings accordingly
+time_format_changed=Formatters for time function has been changed from SimpleDateFormat to DateTimeFormatter. Especially the meaning of ''u'' has changed from day-of-week to year. Please check and update your format strings accordingly: {0}
time_format_random=Format string for DateTimeFormatter (optional) (default yyyy-MM-dd)
time_format_shift=Format string for DateTimeFormatter (optional) (default unix timestamp in millisecond)
timelim=Time limit
diff --git a/src/core/src/main/resources/org/apache/jmeter/resources/messages_fr.properties b/src/core/src/main/resources/org/apache/jmeter/resources/messages_fr.properties
index fe279153c6..c904d122cb 100644
--- a/src/core/src/main/resources/org/apache/jmeter/resources/messages_fr.properties
+++ b/src/core/src/main/resources/org/apache/jmeter/resources/messages_fr.properties
@@ -1322,7 +1322,7 @@ throughput_control_perthread_label=Par utilisateur
throughput_control_title=Contrôleur Débit
throughput_control_tplabel=Débit \:
time_format=Chaîne de formatage sur le modèle SimpleDateFormat (optionnel)
-time_format_changed=Les formateurs pour la fonction de temps ont été modifiés de SimpleDateFormat à DateTimeFormatter. En particulier, la signification de 'u' a changé du jour de la semaine à l'année. Veuillez vérifier et mettre à jour vos chaînes de format en conséquence
+time_format_changed=Le format de la fonction __time a été modifié de SimpleDateFormat à DateTimeFormatter. En particulier, la signification de ''u'' a changé de jour de la semaine à l''année. Veuillez vérifier et mettre à jour vos chaînes de formatage en conséquence: {0}
time_format_random=Chaîne de formatage sur le modèle DateTimeFormatter (optionnel) ( défaut \: yyyy-MM-dd )
time_format_shift=Chaîne de formatage sur le modèle DateTimeFormatter (optionnel) ( défaut \: unix timestamp en millisecondes )
timelim=Limiter le temps de réponses à (ms)
diff --git a/src/functions/src/main/java/org/apache/jmeter/functions/TimeFunction.java b/src/functions/src/main/java/org/apache/jmeter/functions/TimeFunction.java
index 1dd087b7ed..f704985286 100644
--- a/src/functions/src/main/java/org/apache/jmeter/functions/TimeFunction.java
+++ b/src/functions/src/main/java/org/apache/jmeter/functions/TimeFunction.java
@@ -17,24 +17,30 @@
package org.apache.jmeter.functions;
+import java.text.MessageFormat;
import java.time.Instant;
import java.time.ZoneId;
+import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Supplier;
import java.util.regex.Pattern;
import org.apache.jmeter.engine.util.CompoundVariable;
import org.apache.jmeter.samplers.SampleResult;
import org.apache.jmeter.samplers.Sampler;
+import org.apache.jmeter.testelement.TestStateListener;
import org.apache.jmeter.threads.JMeterVariables;
import org.apache.jmeter.util.JMeterUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import com.github.benmanes.caffeine.cache.Caffeine;
+import com.github.benmanes.caffeine.cache.LoadingCache;
import com.google.auto.service.AutoService;
// See org.apache.jmeter.functions.TestTimeFunction for unit tests
@@ -44,7 +50,7 @@ import com.google.auto.service.AutoService;
* @since 2.2
*/
@AutoService(Function.class)
-public class TimeFunction extends AbstractFunction {
+public class TimeFunction extends AbstractFunction implements TestStateListener {
private static final String KEY = "__time"; // $NON-NLS-1$
@@ -57,6 +63,34 @@ public class TimeFunction extends AbstractFunction {
private static final Logger log = LoggerFactory.getLogger(TimeFunction.class);
+ private static final LoadingCache<String, Supplier<String>> DATE_TIME_FORMATTER_CACHE =
+ Caffeine.newBuilder()
+ .maximumSize(1000)
+ .build((fmt) -> {
+ if (DIVISOR_PATTERN.matcher(fmt).matches()) {
+ long div = Long.parseLong(fmt.substring(1)); // should never case NFE
+ return () -> Long.toString(System.currentTimeMillis() / div);
+ }
+ DateTimeFormatter df = DateTimeFormatter
+ .ofPattern(fmt)
+ .withZone(ZoneId.systemDefault());
+ if (isPossibleUsageOfUInFormat(df, fmt)) {
+ log.warn(
+ MessageFormat.format(
+ JMeterUtils.getResString("time_format_changed"),
+ fmt));
+ }
+ return () -> df.format(Instant.now());
+ });
+
+ private static boolean isPossibleUsageOfUInFormat(DateTimeFormatter df, String fmt) {
+ ZoneId mst = ZoneId.of("-07:00");
+ return fmt.contains("u") &&
+ df.withZone(mst)
+ .format(ZonedDateTime.of(2006, 1, 2, 15, 4, 5, 6, mst))
+ .contains("2006");
+ }
+
static {
desc.add(JMeterUtils.getResString("time_format")); //$NON-NLS-1$
desc.add(JMeterUtils.getResString("function_name_paropt")); //$NON-NLS-1$
@@ -95,18 +129,7 @@ public class TimeFunction extends AbstractFunction {
if (fmt == null) {
fmt = format;// Not found
}
- if (DIVISOR_PATTERN.matcher(fmt).matches()) { // divisor is a positive number
- long div = Long.parseLong(fmt.substring(1)); // should never case NFE
- datetime = Long.toString(System.currentTimeMillis() / div);
- } else {
- if (fmt.contains("u")) {
- log.warn(JMeterUtils.getResString("time_format_changed"));
- }
- DateTimeFormatter df = DateTimeFormatter // Not synchronised, so can't be shared
- .ofPattern(fmt)
- .withZone(ZoneId.systemDefault());
- datetime = df.format(Instant.now());
- }
+ datetime = DATE_TIME_FORMATTER_CACHE.get(fmt).get();
}
if (!variable.isEmpty()) {
@@ -148,4 +171,26 @@ public class TimeFunction extends AbstractFunction {
public List<String> getArgumentDesc() {
return desc;
}
+
+ @Override
+ public void testStarted() {
+ // We invalidate the cache so it will parse the formats again and will raise a warning if there are
+ // %u usages.
+ DATE_TIME_FORMATTER_CACHE.invalidateAll();
+ }
+
+ @Override
+ public void testStarted(String host) {
+ testStarted();
+ }
+
+ @Override
+ public void testEnded() {
+ DATE_TIME_FORMATTER_CACHE.invalidateAll();
+ }
+
+ @Override
+ public void testEnded(String host) {
+ testEnded();
+ }
}
diff --git a/xdocs/changes.xml b/xdocs/changes.xml
index e9628c58b7..ea98f66f17 100644
--- a/xdocs/changes.xml
+++ b/xdocs/changes.xml
@@ -114,6 +114,7 @@ Summary
<li><pr>5920</pr>Cache bean properties in <code>TestBeanHelper</code> and avoid synchronization, so test plans with <code>TestBean</code>-based elements is faster</li>
<li><pr>5920</pr>Improve computation when many threads actively produce samplers by using <code>LongAdder</code> and similar concurrency classes to avoid synchronization in <code>Calculator</code></li>
<li><pr>5920</pr>Reduce synchronization contention on <code>AbstractTestElement</code> that are shared between threads (the ones that implement <code>NoThreadClone</code>)</li>
+ <li><pr>5934</pr>Added caching for date formatters for <code>__time</code> function</li>
</ul>
<ch_section>Non-functional changes</ch_section>