From 45996dbd69cf9ad1ee95e0cc522a10196e4e6e0f Mon Sep 17 00:00:00 2001
From: Felix Schumacher
Date: Tue, 24 Sep 2024 17:05:28 +0200
Subject: [PATCH 1/4] Use current time as start time for OMTG
When we use the test start time via JMeterContextService#getTestStartTime,
we might create a storm of test samples at the beginning, when a setup thread group
made us wait.
---
.../org/apache/jmeter/threads/openmodel/OpenModelThreadGroup.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/core/src/main/kotlin/org/apache/jmeter/threads/openmodel/OpenModelThreadGroup.kt b/src/core/src/main/kotlin/org/apache/jmeter/threads/openmodel/OpenModelThreadGroup.kt
index 167e17f628f..5948dc6d40b 100644
--- a/src/core/src/main/kotlin/org/apache/jmeter/threads/openmodel/OpenModelThreadGroup.kt
+++ b/src/core/src/main/kotlin/org/apache/jmeter/threads/openmodel/OpenModelThreadGroup.kt
@@ -204,7 +204,7 @@ public class OpenModelThreadGroup :
val seed = randomSeed
val rnd = if (seed == 0L) Random() else Random(seed)
val gen = ThreadScheduleProcessGenerator(rnd, parsedSchedule)
- val testStartTime = JMeterContextService.getTestStartTime()
+ val testStartTime = System.currentTimeMillis()
val executorService = Executors.newCachedThreadPool()
this.executorService = executorService
val starter = ThreadsStarter(testStartTime, executorService, activeThreads, gen) { threadNumber ->
From 9e2e2bcbd13d128c0d887a23e67e2e7a9dcf9355 Mon Sep 17 00:00:00 2001
From: Felix Schumacher
Date: Wed, 25 Sep 2024 12:28:54 +0200
Subject: [PATCH 2/4] Add startTime to AbstractThreadGroup and use it in OMTG
and PreciseThroughputTimer
A thread group might start later than the test. When we calculate delays on the start time of
the test, we might get a stampede of samples run at the start of the thread group.
---
.../PreciseThroughputTimer.java | 4 ++--
.../jmeter/engine/StandardJMeterEngine.java | 1 +
.../jmeter/threads/AbstractThreadGroup.java | 20 +++++++++++++++++++
.../threads/openmodel/OpenModelThreadGroup.kt | 2 +-
xdocs/changes.xml | 4 +++-
5 files changed, 27 insertions(+), 4 deletions(-)
diff --git a/src/components/src/main/java/org/apache/jmeter/timers/poissonarrivals/PreciseThroughputTimer.java b/src/components/src/main/java/org/apache/jmeter/timers/poissonarrivals/PreciseThroughputTimer.java
index 4864e22117c..ee4998a62c7 100644
--- a/src/components/src/main/java/org/apache/jmeter/timers/poissonarrivals/PreciseThroughputTimer.java
+++ b/src/components/src/main/java/org/apache/jmeter/timers/poissonarrivals/PreciseThroughputTimer.java
@@ -99,7 +99,7 @@ public long delay() {
nextEvent = events.next();
}
long now = System.currentTimeMillis();
- long testStarted = JMeterContextService.getTestStartTime();
+ long testStarted = JMeterContextService.getContext().getThreadGroup().getStartTime();
long delay = (long) (nextEvent * TimeUnit.SECONDS.toMillis(1) + testStarted - now);
if (log.isDebugEnabled()) {
log.debug("Calculated delay is {}", delay);
@@ -117,7 +117,7 @@ public long delay() {
}
private EventProducer getEventProducer() {
- long testStarted = JMeterContextService.getTestStartTime();
+ long testStarted = JMeterContextService.getContext().getThreadGroup().getStartTime();
long prevStarted = PREV_TEST_STARTED.get();
if (prevStarted != testStarted && PREV_TEST_STARTED.compareAndSet(prevStarted, testStarted)) {
// Reset counters if we are calculating throughput for a new test, see https://github.com/apache/jmeter/issues/6165
diff --git a/src/core/src/main/java/org/apache/jmeter/engine/StandardJMeterEngine.java b/src/core/src/main/java/org/apache/jmeter/engine/StandardJMeterEngine.java
index a72602fb2e4..618f5b92124 100644
--- a/src/core/src/main/java/org/apache/jmeter/engine/StandardJMeterEngine.java
+++ b/src/core/src/main/java/org/apache/jmeter/engine/StandardJMeterEngine.java
@@ -574,6 +574,7 @@ private void startThreadGroup(AbstractThreadGroup group, int groupCount, SearchB
threadGroupTree.add(group, testLevelElements);
groups.add(group);
+ group.setStartTime(System.currentTimeMillis());
group.start(groupCount, notifier, threadGroupTree, this);
} catch (JMeterStopTestException ex) { // NOSONAR Reported by log
JMeterUtils.reportErrorToUser("Error occurred starting thread group :" + group.getName()+ ", error message:"+ex.getMessage()
diff --git a/src/core/src/main/java/org/apache/jmeter/threads/AbstractThreadGroup.java b/src/core/src/main/java/org/apache/jmeter/threads/AbstractThreadGroup.java
index 37b3f104b25..63a4462a4d1 100644
--- a/src/core/src/main/java/org/apache/jmeter/threads/AbstractThreadGroup.java
+++ b/src/core/src/main/java/org/apache/jmeter/threads/AbstractThreadGroup.java
@@ -21,6 +21,7 @@
import java.time.Duration;
import java.util.IdentityHashMap;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import org.apache.jmeter.control.Controller;
import org.apache.jmeter.control.IteratingController;
@@ -86,6 +87,8 @@ public abstract class AbstractThreadGroup extends AbstractTestElement
private final AtomicInteger numberOfThreads = new AtomicInteger(0); // Number of active threads in this group
+ private final AtomicLong startTime = new AtomicLong(0);
+
@Override
public AbstractThreadGroupSchema getSchema() {
return AbstractThreadGroupSchema.INSTANCE;
@@ -97,6 +100,23 @@ public AbstractThreadGroupSchema getSchema() {
return new PropertiesAccessor<>(this, getSchema());
}
+ /**
+ * Get the time when this thread group has been started
+ * @return time in milliseconds since epoch
+ */
+ public long getStartTime() {
+ return startTime.get();
+ }
+
+ /**
+ * Set the time when this thread group has been started.
+ * Will probably be set by StandardJMeterEngine.
+ * @param startTime time in milliseconds since epoch
+ */
+ public void setStartTime(long startTime) {
+ this.startTime.set(startTime);
+ }
+
/** {@inheritDoc} */
@Override
public boolean isDone() {
diff --git a/src/core/src/main/kotlin/org/apache/jmeter/threads/openmodel/OpenModelThreadGroup.kt b/src/core/src/main/kotlin/org/apache/jmeter/threads/openmodel/OpenModelThreadGroup.kt
index 5948dc6d40b..45c0f6cef6b 100644
--- a/src/core/src/main/kotlin/org/apache/jmeter/threads/openmodel/OpenModelThreadGroup.kt
+++ b/src/core/src/main/kotlin/org/apache/jmeter/threads/openmodel/OpenModelThreadGroup.kt
@@ -204,7 +204,7 @@ public class OpenModelThreadGroup :
val seed = randomSeed
val rnd = if (seed == 0L) Random() else Random(seed)
val gen = ThreadScheduleProcessGenerator(rnd, parsedSchedule)
- val testStartTime = System.currentTimeMillis()
+ val testStartTime = this.startTime
val executorService = Executors.newCachedThreadPool()
this.executorService = executorService
val starter = ThreadsStarter(testStartTime, executorService, activeThreads, gen) { threadNumber ->
diff --git a/xdocs/changes.xml b/xdocs/changes.xml
index 60836279fde..025af48bca1 100644
--- a/xdocs/changes.xml
+++ b/xdocs/changes.xml
@@ -57,7 +57,7 @@ JMeter 6.x requires Java 17 or later for execution (Java 21 is recommended).
Summary
Changes
@@ -65,6 +65,8 @@ Summary
- 6220 Require Java 17 or later for running JMeter
- 6274 Change references to old MySQL driver to new class
com.mysql.cj.jdbc.Driver
+ - 6352 Calculate delays in Open Model Thread Group and Precise Throughput
+ Timer relative to start of Thread Group instead of the start of the test.
From 66c2df72ad3d8cb39aae8cef6d2b68cc17624a1e Mon Sep 17 00:00:00 2001
From: Felix Schumacher
Date: Wed, 25 Sep 2024 18:25:09 +0200
Subject: [PATCH 3/4] Use simple long, as we are initialized before the reading
threads are started
---
.../org/apache/jmeter/threads/AbstractThreadGroup.java | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/src/core/src/main/java/org/apache/jmeter/threads/AbstractThreadGroup.java b/src/core/src/main/java/org/apache/jmeter/threads/AbstractThreadGroup.java
index 63a4462a4d1..5b7c6c68575 100644
--- a/src/core/src/main/java/org/apache/jmeter/threads/AbstractThreadGroup.java
+++ b/src/core/src/main/java/org/apache/jmeter/threads/AbstractThreadGroup.java
@@ -21,7 +21,6 @@
import java.time.Duration;
import java.util.IdentityHashMap;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicLong;
import org.apache.jmeter.control.Controller;
import org.apache.jmeter.control.IteratingController;
@@ -87,7 +86,7 @@ public abstract class AbstractThreadGroup extends AbstractTestElement
private final AtomicInteger numberOfThreads = new AtomicInteger(0); // Number of active threads in this group
- private final AtomicLong startTime = new AtomicLong(0);
+ private long startTime;
@Override
public AbstractThreadGroupSchema getSchema() {
@@ -105,7 +104,7 @@ public AbstractThreadGroupSchema getSchema() {
* @return time in milliseconds since epoch
*/
public long getStartTime() {
- return startTime.get();
+ return startTime;
}
/**
@@ -114,7 +113,7 @@ public long getStartTime() {
* @param startTime time in milliseconds since epoch
*/
public void setStartTime(long startTime) {
- this.startTime.set(startTime);
+ this.startTime = startTime;
}
/** {@inheritDoc} */
From 7848ff4fd62283f8ed2fc752e3e5e9e5b5240d3f Mon Sep 17 00:00:00 2001
From: Felix Schumacher
Date: Fri, 27 Sep 2024 10:07:56 +0200
Subject: [PATCH 4/4] Add since annotation to javadoc for new feature
---
.../java/org/apache/jmeter/threads/AbstractThreadGroup.java | 2 ++
1 file changed, 2 insertions(+)
diff --git a/src/core/src/main/java/org/apache/jmeter/threads/AbstractThreadGroup.java b/src/core/src/main/java/org/apache/jmeter/threads/AbstractThreadGroup.java
index 5b7c6c68575..f64cf41e7d1 100644
--- a/src/core/src/main/java/org/apache/jmeter/threads/AbstractThreadGroup.java
+++ b/src/core/src/main/java/org/apache/jmeter/threads/AbstractThreadGroup.java
@@ -101,6 +101,7 @@ public AbstractThreadGroupSchema getSchema() {
/**
* Get the time when this thread group has been started
+ * @since 6.0.0
* @return time in milliseconds since epoch
*/
public long getStartTime() {
@@ -110,6 +111,7 @@ public long getStartTime() {
/**
* Set the time when this thread group has been started.
* Will probably be set by StandardJMeterEngine.
+ * @since 6.0.0
* @param startTime time in milliseconds since epoch
*/
public void setStartTime(long startTime) {