Skip to content

Commit 15c23e9

Browse files
Add support for synchronizingTimer
1 parent eddbb24 commit 15c23e9

File tree

10 files changed

+142
-4
lines changed

10 files changed

+142
-4
lines changed

docs/guide/request-generation/index.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@
66
<!-- @include: csv-dataset.md -->
77
<!-- @include: counter.md -->
88
<!-- @include: jsr223-pre-processor.md -->
9-
<!-- @include: timers.md -->
10-
<!-- @include: throughput-timer.md -->
9+
<!-- @include: timers/index.md -->
1110
<!-- @include: percent-controller.md -->
1211
<!-- @include: weighted-switch-controller.md -->
1312
<!-- @include: parallel-controller.md -->

docs/guide/request-generation/timers.md renamed to docs/guide/request-generation/timers/constant-and-random.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
### Emulate user delays between requests
1+
#### Emulate user delays between requests
22

33
Sometimes, is necessary to replicate users' behavior on the test plan adding pauses between requests is one of the most used practices. For example, simulate the time it will take to complete a purchase form. JMeter (and the DSL) provide Constant & Uniform Random timers for this purpose. Here is an example that adds a delay of 3 seconds and another between 4 and 10 seconds:
44

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
### Timers
2+
3+
<!-- @include: constant-and-random.md -->
4+
<!-- @include: throughput-timer.md -->
5+
<!-- @include: synchronizing-timer.md -->
270 KB
Loading
273 KB
Loading
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#### Requests synchronization
2+
3+
Usually, samples generated by different threads in a test plan thread group start deviating from each other according to the different durations each of them may experience.
4+
5+
Here is a diagram depicting this behavior, extracted from [this nice example](https://github.com/abstracta/jmeter-java-dsl/discussions/204) provided by one of JMeter DSL users:
6+
7+
![not synchronized samples in 2 threads and 3 iterations](./not-synchronized-samples.png)
8+
9+
In most cases this is ok. But, if you want to generate batches of simultaneous requests to a system under test, this variability will prevent you from getting the expected behavior.
10+
11+
So, to synchronize requests, by holding some of them until all are in sync, like in this diagram:
12+
13+
![synchronized samples in 2 threads and 3 iterations](./synchronized-samples.png)
14+
15+
You can use `synchronizingTimer` like in the following example:
16+
17+
```java
18+
testPlan(
19+
threadGroup(2, 3,
20+
httpSample("https://mysite"),
21+
synchronizingTimer()
22+
)
23+
)
24+
```

docs/guide/request-generation/throughput-timer.md renamed to docs/guide/request-generation/timers/throughput-timer.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
### Control throughput
1+
#### Control throughput
22

33
To achieve a specific constant throughput for specific samplers or section of a test plan, you can use `throughputTimer`, which uses JMeter `ConstantThroughputTimer`.
44

jmeter-java-dsl/src/main/java/us/abstracta/jmeter/javadsl/JmeterDsl.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import us.abstracta.jmeter.javadsl.core.threadgroups.DslTeardownThreadGroup;
4343
import us.abstracta.jmeter.javadsl.core.threadgroups.RpsThreadGroup;
4444
import us.abstracta.jmeter.javadsl.core.timers.DslConstantTimer;
45+
import us.abstracta.jmeter.javadsl.core.timers.DslSynchronizingTimer;
4546
import us.abstracta.jmeter.javadsl.core.timers.DslThroughputTimer;
4647
import us.abstracta.jmeter.javadsl.core.timers.DslUniformRandomTimer;
4748
import us.abstracta.jmeter.javadsl.core.util.PropertyScriptBuilder.PropertyScript;
@@ -1609,6 +1610,20 @@ public static DslThroughputTimer throughputTimer(double throughputPerMinute) {
16091610
return new DslThroughputTimer(throughputPerMinute);
16101611
}
16111612

1613+
/**
1614+
* Builds a Synchronizing Timer that allows synchronizing samples to be sent all at once.
1615+
* <p>
1616+
* This timer is useful when you need to send requests in simultaneous batches, as a way to asure
1617+
* the system under test gets the requests all at the same time.
1618+
*
1619+
* @return the timer for usage in a test plan.
1620+
* @see DslSynchronizingTimer
1621+
* @since 1.17
1622+
*/
1623+
public static DslSynchronizingTimer synchronizingTimer() {
1624+
return new DslSynchronizingTimer();
1625+
}
1626+
16121627
/**
16131628
* Builds a CSV Data Set which allows loading from a CSV file variables to be used in test plan.
16141629
* <p>
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package us.abstracta.jmeter.javadsl.core.timers;
2+
3+
import java.lang.reflect.Method;
4+
import java.util.List;
5+
import org.apache.jmeter.testbeans.gui.TestBeanGUI;
6+
import org.apache.jmeter.testelement.TestElement;
7+
import org.apache.jmeter.timers.SyncTimer;
8+
import us.abstracta.jmeter.javadsl.codegeneration.MethodCall;
9+
import us.abstracta.jmeter.javadsl.codegeneration.MethodCallContext;
10+
import us.abstracta.jmeter.javadsl.codegeneration.SingleTestElementCallBuilder;
11+
12+
/**
13+
* Uses JMeter Synchronizing Timer to allow sending a batch of requests simultaneously to a system
14+
* under test.
15+
*
16+
* @since 1.17
17+
*/
18+
public class DslSynchronizingTimer extends BaseTimer {
19+
20+
public DslSynchronizingTimer() {
21+
super("Synchronizing Timer", TestBeanGUI.class);
22+
}
23+
24+
@Override
25+
protected TestElement buildTestElement() {
26+
return new SyncTimer();
27+
}
28+
29+
public static class CodeBuilder extends SingleTestElementCallBuilder<SyncTimer> {
30+
31+
public CodeBuilder(List<Method> builderMethods) {
32+
super(SyncTimer.class, builderMethods);
33+
}
34+
35+
@Override
36+
protected MethodCall buildMethodCall(SyncTimer testElement, MethodCallContext context) {
37+
return buildMethodCall();
38+
}
39+
40+
}
41+
42+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package us.abstracta.jmeter.javadsl.core.timers;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
import static us.abstracta.jmeter.javadsl.JmeterDsl.*;
5+
6+
import java.time.Duration;
7+
import java.time.Instant;
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import org.junit.jupiter.api.Nested;
11+
import org.junit.jupiter.api.Test;
12+
import us.abstracta.jmeter.javadsl.codegeneration.MethodCallBuilderTest;
13+
import us.abstracta.jmeter.javadsl.core.DslTestPlan;
14+
15+
public class DslSynchronizingTimerTest {
16+
17+
@Test
18+
public void shouldSynchronizeRequestsWhenTestPlanWithSynchronizingTimer() throws Exception {
19+
List<Instant> reqTimes = new ArrayList<>();
20+
testPlan(
21+
threadGroup(2, 1,
22+
jsr223Sampler(s -> {
23+
if (Thread.currentThread().getName().endsWith("2")) {
24+
Thread.sleep(3000);
25+
}
26+
}),
27+
jsr223Sampler(s -> {
28+
synchronized (reqTimes) {
29+
reqTimes.add(Instant.now());
30+
}
31+
}).children(
32+
synchronizingTimer()
33+
)
34+
)
35+
).run();
36+
assertThat(Duration.between(reqTimes.get(0), reqTimes.get(1))).isLessThan(Duration.ofMillis(500));
37+
}
38+
39+
@Nested
40+
public class CodeBuilderTest extends MethodCallBuilderTest {
41+
42+
public DslTestPlan testPlanWithSynchronizingTimer() {
43+
return testPlan(
44+
threadGroup(2, 3,
45+
synchronizingTimer(),
46+
httpSampler("http://localhost")
47+
)
48+
);
49+
}
50+
51+
}
52+
53+
}

0 commit comments

Comments
 (0)