Skip to content

Commit ed95708

Browse files
author
Hendrik Leuschner
committed
fix: Update Loadtests and config to reflect matrix properly
1 parent db1beb1 commit ed95708

File tree

3 files changed

+190
-30
lines changed

3 files changed

+190
-30
lines changed

ors-benchmark/src/main/java/org/heigit/ors/benchmark/Config.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.heigit.ors.benchmark;
22

33
import org.heigit.ors.benchmark.BenchmarkEnums.DirectionsModes;
4+
import org.heigit.ors.benchmark.BenchmarkEnums.MatrixModes;
45
import org.heigit.ors.benchmark.BenchmarkEnums.TestUnit;
56
import org.slf4j.Logger;
67
import org.slf4j.LoggerFactory;
@@ -9,6 +10,7 @@
910
import java.util.List;
1011

1112
import static org.heigit.ors.benchmark.BenchmarkEnums.DirectionsModes.*;
13+
import static org.heigit.ors.benchmark.BenchmarkEnums.MatrixModes.*;
1214

1315
public class Config {
1416
private static final Logger logger = LoggerFactory.getLogger(Config.class);
@@ -28,7 +30,8 @@ public class Config {
2830
private final boolean parallelExecution;
2931
private final TestUnit testUnit;
3032
private final List<String> sourceFiles;
31-
private final List<String> modes;
33+
private final List<String> directionsModes;
34+
private final List<String> matrixModes;
3235
private final List<Integer> ranges;
3336

3437
public Config() {
@@ -48,8 +51,8 @@ public Config() {
4851
this.testUnit = TestUnit.fromString(getSystemProperty("test_unit", "distance"));
4952
this.sourceFiles = parseCommaSeparatedStringToStrings(getSystemProperty("source_files", ""));
5053
this.ranges = parseCommaSeparatedStringToInts(this.range);
51-
this.modes = parseCommaSeparatedStringToStrings(getSystemProperty("modes", ""));
52-
}
54+
this.directionsModes = parseCommaSeparatedStringToStrings(getSystemProperty("modes", ""));
55+
this.matrixModes = parseCommaSeparatedStringToStrings(getSystemProperty("matrix_modes", ""));}
5356

5457
private String getSystemProperty(String key, String defaultValue) {
5558
String value = System.getProperty(key) != null ? System.getProperty(key) : defaultValue;
@@ -145,9 +148,16 @@ public List<Integer> getRanges() {
145148
}
146149

147150
public List<DirectionsModes> getDirectionsModes() {
148-
return modes.isEmpty() ? List.of(ALGO_CH, ALGO_CORE, ALGO_LM_ASTAR)
149-
: modes.stream()
151+
return directionsModes.isEmpty() ? List.of(ALGO_CH, ALGO_CORE, ALGO_LM_ASTAR)
152+
: directionsModes.stream()
150153
.map(DirectionsModes::fromString)
151154
.toList();
152155
}
156+
157+
public List<MatrixModes> getMatrixModes() {
158+
return matrixModes.isEmpty() ? List.of(ALGO_DIJKSTRA_MATRIX, ALGO_CORE_MATRIX, ALGO_RPHAST_MATRIX)
159+
: directionsModes.stream()
160+
.map(MatrixModes::fromString)
161+
.toList();
162+
}
153163
}

ors-benchmark/src/main/java/org/heigit/ors/benchmark/MatrixAlgorithmLoadTest.java

Lines changed: 171 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
import io.gatling.javaapi.core.ScenarioBuilder;
66
import io.gatling.javaapi.core.Session;
77
import io.gatling.javaapi.http.HttpRequestActionBuilder;
8-
import org.heigit.ors.benchmark.BenchmarkEnums.DirectionsModes;
8+
import org.heigit.ors.benchmark.BenchmarkEnums.MatrixModes;
99
import org.heigit.ors.benchmark.exceptions.RequestBodyCreationException;
1010
import org.heigit.ors.util.SourceUtils;
1111
import org.slf4j.LoggerFactory;
@@ -20,16 +20,38 @@
2020
import static io.gatling.javaapi.http.HttpDsl.http;
2121
import static io.gatling.javaapi.http.HttpDsl.status;
2222

23+
/**
24+
* Load test implementation for OpenRouteService Matrix API using Gatling
25+
* framework.
26+
*
27+
* This class performs load testing on the matrix endpoint by:
28+
* - Reading matrix test data from CSV files containing coordinates, sources,
29+
* and destinations
30+
* - Creating HTTP requests to the /v2/matrix/{profile} endpoint
31+
* - Testing different matrix calculation modes and routing profiles
32+
* - Measuring response times and throughput under concurrent load
33+
*
34+
* The test data is expected to be in CSV format with columns:
35+
* coordinates, sources, destinations, distances, profile
36+
*/
2337
public class MatrixAlgorithmLoadTest extends AbstractLoadTest {
2438

2539
static {
2640
logger = LoggerFactory.getLogger(MatrixAlgorithmLoadTest.class);
2741
}
2842

43+
/**
44+
* Constructs a new MatrixAlgorithmLoadTest instance.
45+
* Initializes the load test with configuration from the parent class.
46+
*/
2947
public MatrixAlgorithmLoadTest() {
3048
super();
3149
}
3250

51+
/**
52+
* Logs configuration information specific to matrix load testing.
53+
* Displays source files, concurrent users, and execution mode.
54+
*/
3355
@Override
3456
protected void logConfigInfo() {
3557
logger.info("Initializing MatrixAlgorithmLoadTest:");
@@ -38,33 +60,69 @@ protected void logConfigInfo() {
3860
logger.info("- Execution mode: {}", config.isParallelExecution() ? "parallel" : "sequential");
3961
}
4062

63+
/**
64+
* Logs the type of test being performed.
65+
*/
4166
@Override
4267
protected void logTestTypeInfo() {
4368
logger.info("Testing matrix");
4469
}
4570

71+
/**
72+
* Creates test scenarios for all combinations of matrix modes, source files,
73+
* and profiles.
74+
*
75+
* @param isParallel whether scenarios should be executed in parallel
76+
* @return stream of PopulationBuilder instances for each test scenario
77+
*/
4678
@Override
4779
protected Stream<PopulationBuilder> createScenarios(boolean isParallel) {
48-
return config.getDirectionsModes().stream()
80+
return config.getMatrixModes().stream()
4981
.flatMap(mode -> config.getSourceFiles().stream()
5082
.flatMap(sourceFile -> mode.getProfiles().stream()
5183
.map(profile -> createScenarioWithInjection(sourceFile, isParallel, mode, profile))));
5284
}
5385

54-
private PopulationBuilder createScenarioWithInjection(String sourceFile, boolean isParallel, DirectionsModes mode,
86+
/**
87+
* Creates a single test scenario with user injection configuration.
88+
*
89+
* @param sourceFile path to the CSV file containing test data
90+
* @param isParallel whether the scenario runs in parallel mode
91+
* @param mode the matrix calculation mode to test
92+
* @param profile the routing profile to test
93+
* @return PopulationBuilder configured with the specified parameters
94+
*/
95+
private PopulationBuilder createScenarioWithInjection(String sourceFile, boolean isParallel, MatrixModes mode,
5596
String profile) {
5697
String scenarioName = formatScenarioName(mode, profile, isParallel);
5798
return createMatrixScenario(scenarioName, sourceFile, config, mode, profile)
5899
.injectOpen(atOnceUsers(config.getNumConcurrentUsers()));
59100
}
60101

61-
private String formatScenarioName(DirectionsModes mode, String profile, boolean isParallel) {
102+
/**
103+
* Formats a descriptive name for the test scenario.
104+
*
105+
* @param mode the matrix calculation mode
106+
* @param profile the routing profile
107+
* @param isParallel whether the scenario runs in parallel
108+
* @return formatted scenario name string
109+
*/
110+
private String formatScenarioName(MatrixModes mode, String profile, boolean isParallel) {
62111
return String.format("%s - %s - %s", isParallel ? "Parallel" : "Sequential", mode, profile);
63112
}
64113

114+
/**
115+
* Creates a Gatling scenario for matrix load testing.
116+
*
117+
* @param name descriptive name for the scenario
118+
* @param sourceFile path to CSV file containing test coordinates
119+
* @param config test configuration parameters
120+
* @param mode matrix calculation mode to test
121+
* @param profile routing profile to test
122+
* @return ScenarioBuilder configured for matrix testing
123+
*/
65124
private static ScenarioBuilder createMatrixScenario(String name, String sourceFile, Config config,
66-
DirectionsModes mode, String profile) {
67-
125+
MatrixModes mode, String profile) {
68126
try {
69127
List<Map<String, Object>> records = csv(sourceFile).readRecords();
70128
List<Map<String, Object>> targetRecords = SourceUtils.getRecordsByProfile(records, profile);
@@ -86,7 +144,16 @@ private static ScenarioBuilder createMatrixScenario(String name, String sourceFi
86144
}
87145
}
88146

89-
private static HttpRequestActionBuilder createRequest(String name, Config config, DirectionsModes mode,
147+
/**
148+
* Creates an HTTP request action for the matrix API endpoint.
149+
*
150+
* @param name request name for identification in test results
151+
* @param config test configuration
152+
* @param mode matrix calculation mode
153+
* @param profile routing profile
154+
* @return HttpRequestActionBuilder configured for matrix API calls
155+
*/
156+
private static HttpRequestActionBuilder createRequest(String name, Config config, MatrixModes mode,
90157
String profile) {
91158
return http(name)
92159
.post("/v2/matrix/" + profile)
@@ -95,34 +162,113 @@ private static HttpRequestActionBuilder createRequest(String name, Config config
95162
.check(status().is(200));
96163
}
97164

98-
static String createRequestBody(Session session, Config config, DirectionsModes mode) {
165+
/**
166+
* Creates the JSON request body for matrix API calls from CSV session data.
167+
*
168+
* @param session Gatling session containing CSV row data
169+
* @param config test configuration
170+
* @param mode matrix calculation mode providing additional parameters
171+
* @return JSON string representation of the request body
172+
* @throws RequestBodyCreationException if JSON serialization fails
173+
*/
174+
static String createRequestBody(Session session, Config config, MatrixModes mode) {
99175
try {
176+
// Get the data from the CSV row
177+
String coordinatesStr = (String) session.get("coordinates");
178+
String sourcesStr = (String) session.get("sources");
179+
String destinationsStr = (String) session.get("destinations");
180+
100181
Map<String, Object> requestBody = new java.util.HashMap<>(Map.of(
101-
"locations", createLocationsListFromArrays(session, config),
102-
"sources", List.of(0),
103-
"destinations", List.of(1)));
182+
"locations", parseCoordinatesFromString(coordinatesStr),
183+
"sources", parseIntegerArrayFromString(sourcesStr),
184+
"destinations", parseIntegerArrayFromString(destinationsStr)));
185+
104186
requestBody.putAll(mode.getRequestParams());
105187
return objectMapper.writeValueAsString(requestBody);
106188
} catch (JsonProcessingException e) {
107189
throw new RequestBodyCreationException("Failed to create request body", e);
108190
}
109191
}
110192

111-
static List<List<Double>> createLocationsListFromArrays(Session session, Config config) {
112-
List<List<Double>> locations = new ArrayList<>();
193+
/**
194+
* Parses coordinate pairs from CSV string format to nested list structure.
195+
*
196+
* Converts strings like "[[8.695556, 49.392701], [8.684623, 49.398284]]"
197+
* into List<List<Double>> format expected by the matrix API.
198+
*
199+
* @param coordinatesStr string representation of coordinate array from CSV
200+
* @return list of coordinate pairs as [longitude, latitude] arrays
201+
* @throws RequestBodyCreationException if parsing fails or format is invalid
202+
*/
203+
static List<List<Double>> parseCoordinatesFromString(String coordinatesStr) {
204+
try {
205+
if (coordinatesStr == null || coordinatesStr.trim().isEmpty()) {
206+
throw new RequestBodyCreationException("Coordinates string is null or empty");
207+
}
208+
209+
// Remove quotes if present
210+
String cleaned = coordinatesStr.trim();
211+
if (cleaned.startsWith("\"") && cleaned.endsWith("\"")) {
212+
cleaned = cleaned.substring(1, cleaned.length() - 1);
213+
}
214+
215+
// Remove outer brackets
216+
cleaned = cleaned.substring(2, cleaned.length() - 2);
217+
String[] coordinatePairs = cleaned.split("\\], \\[");
218+
219+
List<List<Double>> locations = new ArrayList<>();
220+
for (String pair : coordinatePairs) {
221+
String[] values = pair.split(", ");
222+
if (values.length != 2) {
223+
throw new RequestBodyCreationException("Invalid coordinate pair: " + pair);
224+
}
225+
double lon = Double.parseDouble(values[0]);
226+
double lat = Double.parseDouble(values[1]);
227+
locations.add(List.of(lon, lat));
228+
}
229+
return locations;
230+
} catch (Exception e) {
231+
throw new RequestBodyCreationException("Failed to parse coordinates: " + coordinatesStr, e);
232+
}
233+
}
234+
235+
/**
236+
* Parses integer arrays from CSV string format.
237+
*
238+
* Converts strings like "[0, 1, 2]" into List<Integer> format
239+
* for sources and destinations parameters.
240+
*
241+
* @param arrayStr string representation of integer array from CSV
242+
* @return list of integers
243+
* @throws RequestBodyCreationException if parsing fails or format is invalid
244+
*/
245+
static List<Integer> parseIntegerArrayFromString(String arrayStr) {
113246
try {
114-
Double startLon = Double.valueOf((String) session.getList(config.getFieldStartLon()).get(0));
115-
Double startLat = Double.valueOf((String) session.getList(config.getFieldStartLat()).get(0));
116-
locations.add(List.of(startLon, startLat));
117-
Double endLon = Double.valueOf((String) session.getList(config.getFieldEndLon()).get(0));
118-
Double endLat = Double.valueOf((String) session.getList(config.getFieldEndLat()).get(0));
119-
locations.add(List.of(endLon, endLat));
120-
} catch (NumberFormatException e) {
121-
String errorMessage = String.format(
122-
"Failed to parse coordinate values in locations list at index %d. Original value could not be converted to double",
123-
locations.size());
124-
throw new RequestBodyCreationException("Error processing coordinates: " + errorMessage, e);
247+
if (arrayStr == null || arrayStr.trim().isEmpty()) {
248+
throw new RequestBodyCreationException("Array string is null or empty");
249+
}
250+
251+
// Remove quotes if present
252+
String cleaned = arrayStr.trim();
253+
if (cleaned.startsWith("\"") && cleaned.endsWith("\"")) {
254+
cleaned = cleaned.substring(1, cleaned.length() - 1);
255+
}
256+
257+
// Remove brackets
258+
cleaned = cleaned.substring(1, cleaned.length() - 1);
259+
260+
if (cleaned.trim().isEmpty()) {
261+
return new ArrayList<>();
262+
}
263+
264+
String[] values = cleaned.split(", ");
265+
List<Integer> result = new ArrayList<>();
266+
for (String value : values) {
267+
result.add(Integer.parseInt(value.trim()));
268+
}
269+
return result;
270+
} catch (Exception e) {
271+
throw new RequestBodyCreationException("Failed to parse integer array: " + arrayStr, e);
125272
}
126-
return locations;
127273
}
128274
}

ors-benchmark/src/main/java/org/heigit/ors/benchmark/exceptions/RequestBodyCreationException.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,8 @@ public class RequestBodyCreationException extends RuntimeException {
44
public RequestBodyCreationException(String message, Throwable cause) {
55
super(message, cause);
66
}
7+
8+
public RequestBodyCreationException(String message) {
9+
super(message);
10+
}
711
}

0 commit comments

Comments
 (0)