Skip to content

Commit 1d73643

Browse files
committed
GH-5691 CLI for running and storing query explanations
1 parent e76d378 commit 1d73643

2 files changed

Lines changed: 73 additions & 42 deletions

File tree

testsuites/benchmark/src/main/java/org/eclipse/rdf4j/benchmark/plan/QueryPlanSnapshotCli.java

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,9 @@ public final class QueryPlanSnapshotCli {
6969
private static final ZoneId LOCAL_ZONE = ZoneId.systemDefault();
7070
private static final DateTimeFormatter LOCAL_TIME_FORMATTER = DateTimeFormatter
7171
.ofPattern("yyyy-MM-dd HH:mm:ss z");
72-
private static final long EXECUTION_REPEAT_SOFT_LIMIT_NANOS = TimeUnit.SECONDS.toNanos(60);
73-
private static final int EXECUTION_REPEAT_MIN_RUNS = 2;
74-
private static final int EXECUTION_REPEAT_MAX_RUNS = 128;
72+
private static final long DEFAULT_EXECUTION_REPEAT_SOFT_LIMIT_NANOS = TimeUnit.SECONDS.toNanos(60);
73+
private static final int DEFAULT_EXECUTION_REPEAT_MIN_RUNS = 2;
74+
private static final int DEFAULT_EXECUTION_REPEAT_MAX_RUNS = 128;
7575

7676
public static void main(String[] args) throws Exception {
7777
QueryPlanSnapshotCliOptions options = parseArgs(args);
@@ -83,15 +83,30 @@ public static void main(String[] args) throws Exception {
8383
private final BufferedReader input;
8484
private final PrintStream output;
8585
private final JLineChoiceMenu jLineChoiceMenu;
86+
private final int executionRepeatMinRuns;
87+
private final int executionRepeatMaxRuns;
88+
private final long executionRepeatSoftLimitNanos;
8689

8790
QueryPlanSnapshotCli(BufferedReader input, PrintStream output) {
8891
this(input, output, false);
8992
}
9093

9194
QueryPlanSnapshotCli(BufferedReader input, PrintStream output, boolean useTerminalChoiceMenu) {
95+
this(input, output, useTerminalChoiceMenu, DEFAULT_EXECUTION_REPEAT_MIN_RUNS,
96+
DEFAULT_EXECUTION_REPEAT_MAX_RUNS, DEFAULT_EXECUTION_REPEAT_SOFT_LIMIT_NANOS);
97+
}
98+
99+
QueryPlanSnapshotCli(BufferedReader input, PrintStream output, boolean useTerminalChoiceMenu,
100+
int executionRepeatMinRuns, int executionRepeatMaxRuns, long executionRepeatSoftLimitNanos) {
92101
this.input = Objects.requireNonNull(input, "input");
93102
this.output = Objects.requireNonNull(output, "output");
94103
this.jLineChoiceMenu = useTerminalChoiceMenu ? JLineChoiceMenu.tryCreate(output) : null;
104+
this.executionRepeatMinRuns = validateExecutionRepeatMinRuns(executionRepeatMinRuns);
105+
this.executionRepeatMaxRuns = validateExecutionRepeatMaxRuns(executionRepeatMaxRuns);
106+
if (this.executionRepeatMinRuns > this.executionRepeatMaxRuns) {
107+
throw new IllegalArgumentException("executionRepeatMinRuns must be <= executionRepeatMaxRuns.");
108+
}
109+
this.executionRepeatSoftLimitNanos = validateExecutionRepeatSoftLimitNanos(executionRepeatSoftLimitNanos);
95110
}
96111

97112
void run(QueryPlanSnapshotCliOptions options) throws Exception {
@@ -1162,6 +1177,27 @@ private static String formatQueryTimeoutSeconds(Integer queryTimeoutSeconds) {
11621177
return queryTimeoutSeconds.toString();
11631178
}
11641179

1180+
private static int validateExecutionRepeatMinRuns(int minRuns) {
1181+
if (minRuns < 1) {
1182+
throw new IllegalArgumentException("executionRepeatMinRuns must be >= 1.");
1183+
}
1184+
return minRuns;
1185+
}
1186+
1187+
private static int validateExecutionRepeatMaxRuns(int maxRuns) {
1188+
if (maxRuns < 1) {
1189+
throw new IllegalArgumentException("executionRepeatMaxRuns must be >= 1.");
1190+
}
1191+
return maxRuns;
1192+
}
1193+
1194+
private static long validateExecutionRepeatSoftLimitNanos(long softLimitNanos) {
1195+
if (softLimitNanos < 1L) {
1196+
throw new IllegalArgumentException("executionRepeatSoftLimitNanos must be >= 1.");
1197+
}
1198+
return softLimitNanos;
1199+
}
1200+
11651201
private static TupleQuery prepareTupleQuery(SailRepositoryConnection connection, String queryText,
11661202
Integer queryTimeoutSeconds) {
11671203
TupleQuery tupleQuery = connection.prepareTupleQuery(queryText);
@@ -1236,14 +1272,14 @@ private QueryExecutionVerification verifyRepeatedExecution(SailRepositoryConnect
12361272
int runs = 0;
12371273
boolean softLimitReached = false;
12381274

1239-
while (runs < EXECUTION_REPEAT_MAX_RUNS) {
1240-
if (runs >= EXECUTION_REPEAT_MIN_RUNS) {
1275+
while (runs < executionRepeatMaxRuns) {
1276+
if (runs >= executionRepeatMinRuns) {
12411277
long averageNanos = Math.max(1L, elapsedNanos / runs);
1242-
if (elapsedNanos + averageNanos > EXECUTION_REPEAT_SOFT_LIMIT_NANOS) {
1278+
if (elapsedNanos + averageNanos > executionRepeatSoftLimitNanos) {
12431279
softLimitReached = true;
12441280
break;
12451281
}
1246-
} else if (elapsedNanos >= EXECUTION_REPEAT_SOFT_LIMIT_NANOS) {
1282+
} else if (elapsedNanos >= executionRepeatSoftLimitNanos) {
12471283
softLimitReached = true;
12481284
break;
12491285
}
@@ -1272,7 +1308,7 @@ private QueryExecutionVerification verifyRepeatedExecution(SailRepositoryConnect
12721308
}
12731309
}
12741310

1275-
boolean maxRunsReached = runs >= EXECUTION_REPEAT_MAX_RUNS;
1311+
boolean maxRunsReached = runs >= executionRepeatMaxRuns;
12761312
if (runs == 0) {
12771313
return new QueryExecutionVerification(0, 0, 0, softLimitReached, maxRunsReached);
12781314
}
@@ -1296,7 +1332,7 @@ private void printExecutionVerification(QueryExecutionVerification executionVeri
12961332
+ ", totalMillis=" + totalMillis
12971333
+ ", averageMillis=" + averageMillis
12981334
+ ", resultCount=" + executionVerification.resultCount
1299-
+ ", softLimitMillis=" + TimeUnit.NANOSECONDS.toMillis(EXECUTION_REPEAT_SOFT_LIMIT_NANOS)
1335+
+ ", softLimitMillis=" + TimeUnit.NANOSECONDS.toMillis(executionRepeatSoftLimitNanos)
13001336
+ ", softLimitReached=" + executionVerification.softLimitReached
13011337
+ ", maxRunsReached=" + executionVerification.maxRunsReached);
13021338
}

testsuites/benchmark/src/test/java/org/eclipse/rdf4j/benchmark/plan/QueryPlanSnapshotCliTest.java

Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.nio.file.Path;
2727
import java.util.LinkedHashMap;
2828
import java.util.Map;
29+
import java.util.concurrent.TimeUnit;
2930

3031
import org.eclipse.rdf4j.benchmark.common.plan.QueryPlanCapture;
3132
import org.eclipse.rdf4j.benchmark.common.plan.QueryPlanCaptureContext;
@@ -36,6 +37,10 @@
3637

3738
class QueryPlanSnapshotCliTest {
3839

40+
private static final int TEST_EXECUTION_REPEAT_MIN_RUNS = 1;
41+
private static final int TEST_EXECUTION_REPEAT_MAX_RUNS = 1;
42+
private static final long TEST_EXECUTION_REPEAT_SOFT_LIMIT_NANOS = TimeUnit.MILLISECONDS.toNanos(1);
43+
3944
@Test
4045
void parsesThemeQueryShortcutAndStore() {
4146
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] {
@@ -217,8 +222,7 @@ void helpModeNeverRequiresInteractiveInput() {
217222
@Test
218223
void runModePrintsPrettyExplanationForAllLevels() throws Exception {
219224
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
220-
QueryPlanSnapshotCli cli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader("")),
221-
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()));
225+
QueryPlanSnapshotCli cli = newCli("", outputBuffer);
222226

223227
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] {
224228
"--no-interactive",
@@ -242,8 +246,7 @@ void runModePrintsPrettyExplanationForAllLevels() throws Exception {
242246
@Test
243247
void runModePrintsExecutionVerificationSummary() throws Exception {
244248
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
245-
QueryPlanSnapshotCli cli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader("")),
246-
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()));
249+
QueryPlanSnapshotCli cli = newCli("", outputBuffer);
247250

248251
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] {
249252
"--no-interactive",
@@ -257,7 +260,7 @@ void runModePrintsExecutionVerificationSummary() throws Exception {
257260

258261
String printed = outputBuffer.toString(StandardCharsets.UTF_8);
259262
assertTrue(printed.contains("=== Execution Verification ==="), printed);
260-
assertTrue(printed.contains("runs="), printed);
263+
assertTrue(printed.contains("runs=1,"), printed);
261264
}
262265

263266
@Test
@@ -273,13 +276,11 @@ void lmdbRunRecordsLoadedSizeAndSkipsReloadWhenSizeMatches() throws Exception {
273276
});
274277

275278
ByteArrayOutputStream firstRunOutput = new ByteArrayOutputStream();
276-
QueryPlanSnapshotCli firstRunCli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader("")),
277-
new PrintStream(firstRunOutput, true, StandardCharsets.UTF_8.name()));
279+
QueryPlanSnapshotCli firstRunCli = newCli("", firstRunOutput);
278280
firstRunCli.run(options);
279281

280282
ByteArrayOutputStream secondRunOutput = new ByteArrayOutputStream();
281-
QueryPlanSnapshotCli secondRunCli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader("")),
282-
new PrintStream(secondRunOutput, true, StandardCharsets.UTF_8.name()));
283+
QueryPlanSnapshotCli secondRunCli = newCli("", secondRunOutput);
283284
secondRunCli.run(options);
284285

285286
String secondRunPrinted = secondRunOutput.toString(StandardCharsets.UTF_8);
@@ -290,8 +291,7 @@ void lmdbRunRecordsLoadedSizeAndSkipsReloadWhenSizeMatches() throws Exception {
290291
@Test
291292
void runModePrintsConfiguredQueryTimeoutInResultsSection() throws Exception {
292293
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
293-
QueryPlanSnapshotCli cli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader("")),
294-
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()));
294+
QueryPlanSnapshotCli cli = newCli("", outputBuffer);
295295

296296
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] {
297297
"--no-interactive",
@@ -312,8 +312,7 @@ void runModePrintsConfiguredQueryTimeoutInResultsSection() throws Exception {
312312
void runModePersistsRunNameAsMetadataAndPrintsIt() throws Exception {
313313
Path outputDirectory = Files.createTempDirectory("rdf4j-cli-run-name-");
314314
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
315-
QueryPlanSnapshotCli cli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader("")),
316-
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()));
315+
QueryPlanSnapshotCli cli = newCli("", outputBuffer);
317316

318317
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] {
319318
"--no-interactive",
@@ -343,8 +342,7 @@ void runModePersistsRunNameAsMetadataAndPrintsIt() throws Exception {
343342
void runModePrintsOriginalQueryAtStartOfResultsSection() throws Exception {
344343
String query = "SELECT * WHERE { ?s ?p ?o } LIMIT 5";
345344
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
346-
QueryPlanSnapshotCli cli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader("")),
347-
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()));
345+
QueryPlanSnapshotCli cli = newCli("", outputBuffer);
348346

349347
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] {
350348
"--no-interactive",
@@ -379,8 +377,7 @@ void interactiveRunAsksForQuerySourceBeforeTheme() throws Exception {
379377
"")
380378
+ "\n";
381379
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
382-
QueryPlanSnapshotCli cli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader(interactiveInput)),
383-
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()));
380+
QueryPlanSnapshotCli cli = newCli(interactiveInput, outputBuffer);
384381

385382
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] { "--persist", "false" });
386383
cli.run(options);
@@ -407,8 +404,7 @@ void interactiveRunSupportsArrowKeySelectionForStore() throws Exception {
407404
"")
408405
+ "\n";
409406
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
410-
QueryPlanSnapshotCli cli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader(interactiveInput)),
411-
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()));
407+
QueryPlanSnapshotCli cli = newCli(interactiveInput, outputBuffer);
412408

413409
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] { "--persist", "false" });
414410
cli.run(options);
@@ -430,8 +426,7 @@ void interactiveRunSupportsQueryFileSourceSelection() throws Exception {
430426
"")
431427
+ "\n";
432428
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
433-
QueryPlanSnapshotCli cli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader(interactiveInput)),
434-
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()));
429+
QueryPlanSnapshotCli cli = newCli(interactiveInput, outputBuffer);
435430

436431
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] { "--persist", "false" });
437432
cli.run(options);
@@ -457,8 +452,7 @@ void interactiveNoArgsCanSelectCompareExistingModeAndCompare() throws Exception
457452
"0,1")
458453
+ "\n";
459454
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
460-
QueryPlanSnapshotCli cli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader(interactiveInput)),
461-
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()));
455+
QueryPlanSnapshotCli cli = newCli(interactiveInput, outputBuffer);
462456

463457
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] {});
464458
cli.run(options);
@@ -485,8 +479,7 @@ void interactiveNoArgsCanRenameAllRunsForSelectedCommit() throws Exception {
485479
"release-candidate")
486480
+ "\n";
487481
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
488-
QueryPlanSnapshotCli cli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader(interactiveInput)),
489-
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()));
482+
QueryPlanSnapshotCli cli = newCli(interactiveInput, outputBuffer);
490483

491484
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] {});
492485
cli.run(options);
@@ -534,8 +527,7 @@ void interactiveCompareExistingListsAllAvailableQueryIds() throws Exception {
534527
"0,1")
535528
+ "\n";
536529
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
537-
QueryPlanSnapshotCli cli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader(interactiveInput)),
538-
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()));
530+
QueryPlanSnapshotCli cli = newCli(interactiveInput, outputBuffer);
539531

540532
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] {});
541533
cli.run(options);
@@ -564,8 +556,7 @@ void interactiveCompareQueryIdPromptsOutputDirectoryOnceWhenBlank() throws Excep
564556
"0,1")
565557
+ "\n";
566558
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
567-
QueryPlanSnapshotCli cli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader(interactiveInput)),
568-
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()));
559+
QueryPlanSnapshotCli cli = newCli(interactiveInput, outputBuffer);
569560

570561
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] {});
571562
cli.run(options);
@@ -593,8 +584,7 @@ void compareExistingInteractiveModeCanBrowseAndViewRun() throws Exception {
593584
"3")
594585
+ "\n";
595586
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
596-
QueryPlanSnapshotCli cli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader(interactiveInput)),
597-
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()));
587+
QueryPlanSnapshotCli cli = newCli(interactiveInput, outputBuffer);
598588

599589
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] {
600590
"--compare-existing",
@@ -620,8 +610,7 @@ void compareExistingCanFilterByRunName() throws Exception {
620610
Map.of("store", "memory", "runName", "candidate"));
621611

622612
ByteArrayOutputStream outputBuffer = new ByteArrayOutputStream();
623-
QueryPlanSnapshotCli cli = new QueryPlanSnapshotCli(new BufferedReader(new StringReader("")),
624-
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()));
613+
QueryPlanSnapshotCli cli = newCli("", outputBuffer);
625614
QueryPlanSnapshotCliOptions options = QueryPlanSnapshotCli.parseArgs(new String[] {
626615
"--compare-existing",
627616
"--no-interactive",
@@ -636,6 +625,12 @@ void compareExistingCanFilterByRunName() throws Exception {
636625
assertFalse(printed.contains("queryId=q-alpha"), printed);
637626
}
638627

628+
private static QueryPlanSnapshotCli newCli(String inputText, ByteArrayOutputStream outputBuffer) throws Exception {
629+
return new QueryPlanSnapshotCli(new BufferedReader(new StringReader(inputText)),
630+
new PrintStream(outputBuffer, true, StandardCharsets.UTF_8.name()), false,
631+
TEST_EXECUTION_REPEAT_MIN_RUNS, TEST_EXECUTION_REPEAT_MAX_RUNS, TEST_EXECUTION_REPEAT_SOFT_LIMIT_NANOS);
632+
}
633+
639634
private static int countOccurrences(String value, String token) {
640635
int count = 0;
641636
int fromIndex = 0;

0 commit comments

Comments
 (0)