Skip to content

Commit a143dec

Browse files
perf: Adds Gatling performance test for aggregate data export with descendants [DHIS2-21490] (#23895)
* perf: add Gatling performance test for aggregate data export with descendants [DHIS2-21490] Adds AggregateDataExportPerformanceTest covering GET /api/dataValueSets/ with children=true against the Sierra Leone demo database — the exact scenario that exposed the O(n²) org unit descendants cross-join fixed in DHIS2-21490. Uses Sierra Leone root org unit and ART monthly summary dataset with a rolling two-year date window, 5 concurrent users over 10 seconds. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add aggregate data export with descendats performance test --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent d409d36 commit a143dec

1 file changed

Lines changed: 104 additions & 0 deletions

File tree

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
* Copyright (c) 2004-2025, University of Oslo
3+
* All rights reserved.
4+
*
5+
* Redistribution and use in source and binary forms, with or without
6+
* modification, are permitted provided that the following conditions are met:
7+
*
8+
* 1. Redistributions of source code must retain the above copyright notice, this
9+
* list of conditions and the following disclaimer.
10+
*
11+
* 2. Redistributions in binary form must reproduce the above copyright notice,
12+
* this list of conditions and the following disclaimer in the documentation
13+
* and/or other materials provided with the distribution.
14+
*
15+
* 3. Neither the name of the copyright holder nor the names of its contributors
16+
* may be used to endorse or promote products derived from this software without
17+
* specific prior written permission.
18+
*
19+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20+
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21+
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
23+
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24+
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25+
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
26+
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28+
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
package org.hisp.dhis.test.platform;
31+
32+
import static io.gatling.javaapi.core.CoreDsl.*;
33+
import static io.gatling.javaapi.http.HttpDsl.*;
34+
35+
import io.gatling.javaapi.core.ClosedInjectionStep;
36+
import io.gatling.javaapi.core.ScenarioBuilder;
37+
import io.gatling.javaapi.core.Simulation;
38+
import io.gatling.javaapi.http.HttpProtocolBuilder;
39+
import java.time.LocalDate;
40+
import java.time.format.DateTimeFormatter;
41+
42+
/**
43+
* Performance test for data value set export with org unit descendants (DHIS2-21490).
44+
*
45+
* <p>Exercises {@code GET /api/dataValueSets/} with {@code children=true}, which activates the
46+
* {@code ou_with_descendants_ids} CTE in {@code HibernateDataExportStore}. The original query used
47+
* an O(n²) self-join across the entire org unit table; the fix drives from the selected roots and
48+
* uses the {@code varchar_pattern_ops} path index to find descendants in O(roots × log n).
49+
*
50+
* <p>Assumes a Sierra Leone demo database at {@code localhost:8080}. UIDs are stable across demo
51+
* database versions:
52+
*
53+
* <ul>
54+
* <li>Sierra Leone root org unit: {@code ImspTQPwCqd}
55+
* <li>ART monthly summary dataset: {@code lyLU2wR22tC}
56+
* </ul>
57+
*/
58+
public class AggregateDataExportPerformanceTest extends Simulation {
59+
60+
private static final String BASE_URL = "http://localhost:8080";
61+
private static final String EXPORT_REQUEST = "GET dataValueSets - root OU with descendants";
62+
63+
@Override
64+
public void before() {
65+
MetadataImporter.importJsonFile("platform/superuser-data-sl-db.json", "admin", "district");
66+
}
67+
68+
public AggregateDataExportPerformanceTest() {
69+
DateTimeFormatter fmt = DateTimeFormatter.ISO_LOCAL_DATE;
70+
String endDate = LocalDate.now().format(fmt);
71+
String startDate = LocalDate.now().minusYears(2).format(fmt);
72+
73+
HttpProtocolBuilder httpProtocol =
74+
http.baseUrl(BASE_URL)
75+
.acceptHeader("application/json")
76+
.warmUp(BASE_URL + "/api/ping")
77+
.disableCaching()
78+
.basicAuth("admin", "district");
79+
80+
ScenarioBuilder exportScenario =
81+
scenario(EXPORT_REQUEST)
82+
.group(EXPORT_REQUEST)
83+
.on(
84+
repeat(1)
85+
.on(
86+
exec(http(EXPORT_REQUEST)
87+
.get("/api/dataValueSets/")
88+
.queryParam("orgUnit", "ImspTQPwCqd")
89+
.queryParam("children", "true")
90+
.queryParam("dataSet", "lyLU2wR22tC")
91+
.queryParam("startDate", startDate)
92+
.queryParam("endDate", endDate))
93+
.pause(1)));
94+
95+
ClosedInjectionStep closedInjection = rampConcurrentUsers(0).to(5).during(10);
96+
97+
setUp(exportScenario.injectClosed(closedInjection))
98+
.protocols(httpProtocol)
99+
.assertions(
100+
details(EXPORT_REQUEST).responseTime().percentile(95).lt(300),
101+
details(EXPORT_REQUEST).responseTime().max().lt(500),
102+
details(EXPORT_REQUEST).successfulRequests().percent().is(100D));
103+
}
104+
}

0 commit comments

Comments
 (0)