Skip to content

Commit 92a7328

Browse files
committed
GH-5691 CLI for running and storing query explanations
1 parent 9c1a661 commit 92a7328

6 files changed

Lines changed: 149 additions & 48 deletions

File tree

compliance/elasticsearch/pom.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,10 +109,16 @@
109109
</dependency>
110110
<dependency>
111111
<groupId>org.testcontainers</groupId>
112-
<artifactId>junit-jupiter</artifactId>
112+
<artifactId>testcontainers-junit-jupiter</artifactId>
113113
<version>${testcontainers.version}</version>
114114
<scope>test</scope>
115115
</dependency>
116+
<dependency>
117+
<groupId>com.fasterxml.jackson.core</groupId>
118+
<artifactId>jackson-annotations</artifactId>
119+
<version>${jackson.annotations.version}</version>
120+
<scope>test</scope>
121+
</dependency>
116122
<dependency>
117123
<groupId>org.apache.logging.log4j</groupId>
118124
<artifactId>log4j-core</artifactId>

core/sail/elasticsearch-store/pom.xml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,10 +91,16 @@
9191
</dependency>
9292
<dependency>
9393
<groupId>org.testcontainers</groupId>
94-
<artifactId>junit-jupiter</artifactId>
94+
<artifactId>testcontainers-junit-jupiter</artifactId>
9595
<version>${testcontainers.version}</version>
9696
<scope>test</scope>
9797
</dependency>
98+
<dependency>
99+
<groupId>com.fasterxml.jackson.core</groupId>
100+
<artifactId>jackson-annotations</artifactId>
101+
<version>${jackson.annotations.version}</version>
102+
<scope>test</scope>
103+
</dependency>
98104
<dependency>
99105
<groupId>${project.groupId}</groupId>
100106
<artifactId>rdf4j-benchmark-common</artifactId>

core/sail/lmdb/src/test/java/org/eclipse/rdf4j/sail/lmdb/benchmark/ThemeQueryBenchmark.java

Lines changed: 104 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,9 @@
1515

1616
import java.io.File;
1717
import java.io.IOException;
18-
import java.nio.file.Path;
1918
import java.util.concurrent.TimeUnit;
2019

2120
import org.apache.commons.io.FileUtils;
22-
import org.assertj.core.util.Files;
23-
import org.eclipse.rdf4j.benchmark.common.BenchmarkQuery;
2421
import org.eclipse.rdf4j.benchmark.common.ThemeQueryCatalog;
2522
import org.eclipse.rdf4j.benchmark.common.plan.FeatureFlagCollector;
2623
import org.eclipse.rdf4j.benchmark.common.plan.QueryPlanCapture;
@@ -31,7 +28,6 @@
3128
import org.eclipse.rdf4j.query.explanation.Explanation;
3229
import org.eclipse.rdf4j.queryrender.sparql.TupleExprIRRenderer;
3330
import org.eclipse.rdf4j.repository.sail.SailRepository;
34-
import org.eclipse.rdf4j.repository.sail.SailRepositoryConnection;
3531
import org.eclipse.rdf4j.repository.util.RDFInserter;
3632
import org.eclipse.rdf4j.sail.lmdb.LmdbStore;
3733
import org.eclipse.rdf4j.sail.lmdb.config.LmdbStoreConfig;
@@ -52,7 +48,6 @@
5248
import org.openjdk.jmh.annotations.Warmup;
5349
import org.openjdk.jmh.runner.Runner;
5450
import org.openjdk.jmh.runner.RunnerException;
55-
import org.openjdk.jmh.runner.options.Options;
5651
import org.openjdk.jmh.runner.options.OptionsBuilder;
5752

5853
@State(Scope.Benchmark)
@@ -64,6 +59,11 @@
6459
public class ThemeQueryBenchmark {
6560

6661
private static final String STORE_NAME = "lmdb";
62+
private static final File STORE_DIRECTORY = new File("target", "lmdb-theme-query-benchmark");
63+
private static final String TRIPLES_DATA_FILE = "triples/data.mdb";
64+
private static final String VALUES_DATA_FILE = "values/data.mdb";
65+
private static final long EXPECTED_TRIPLES_DATA_SIZE_BYTES = 1500921856L;
66+
private static final long EXPECTED_VALUES_DATA_SIZE_BYTES = 713687040L;
6767

6868
@Param({ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10" })
6969
public int z_queryIndex;
@@ -80,7 +80,6 @@ public class ThemeQueryBenchmark {
8080
})
8181
public String themeName;
8282

83-
private File dataDir;
8483
private SailRepository repository;
8584
private LmdbStore store;
8685
private LmdbStoreConfig storeConfig;
@@ -89,7 +88,7 @@ public class ThemeQueryBenchmark {
8988
private long expected;
9089

9190
public static void main(String[] args) throws RunnerException {
92-
Options opt = new OptionsBuilder()
91+
var opt = new OptionsBuilder()
9392
.include("ThemeQueryBenchmark")
9493
.forks(1)
9594
.build();
@@ -101,28 +100,71 @@ public void setup() throws IOException {
101100
theme = Theme.valueOf(themeName);
102101
query = ThemeQueryCatalog.queryFor(theme, z_queryIndex);
103102
expected = ThemeQueryCatalog.expectedCountFor(theme, z_queryIndex);
104-
dataDir = Files.newTemporaryFolder();
103+
if (!STORE_DIRECTORY.exists() && !STORE_DIRECTORY.mkdirs()) {
104+
throw new IOException("Unable to create fixed LMDB benchmark directory: " + STORE_DIRECTORY);
105+
}
105106
storeConfig = ConfigUtil.createConfig();
106-
store = new LmdbStore(dataDir, storeConfig);
107+
store = new LmdbStore(STORE_DIRECTORY, storeConfig);
107108
repository = new SailRepository(store);
108-
loadData();
109+
ensureDataLoadedAndValidated();
109110
if (QueryPlanCapture.isCaptureEnabled()) {
110111
captureQueryPlanSnapshot();
111112
}
112113
}
113114

115+
private void ensureDataLoadedAndValidated() throws IOException {
116+
if (!hasExpectedDbFileSizes()) {
117+
rebuildStoreFromScratch();
118+
}
119+
120+
if (!hasExpectedDbFileSizes()) {
121+
throw new IllegalStateException("Unexpected LMDB db file sizes in fixed benchmark store. Expected "
122+
+ TRIPLES_DATA_FILE + "=" + EXPECTED_TRIPLES_DATA_SIZE_BYTES + " and "
123+
+ VALUES_DATA_FILE + "=" + EXPECTED_VALUES_DATA_SIZE_BYTES + " but got "
124+
+ TRIPLES_DATA_FILE + "=" + dbFileSize(TRIPLES_DATA_FILE) + " and "
125+
+ VALUES_DATA_FILE + "=" + dbFileSize(VALUES_DATA_FILE));
126+
}
127+
}
128+
129+
private void rebuildStoreFromScratch() throws IOException {
130+
if (repository != null) {
131+
repository.shutDown();
132+
}
133+
134+
FileUtils.deleteDirectory(STORE_DIRECTORY);
135+
if (!STORE_DIRECTORY.exists() && !STORE_DIRECTORY.mkdirs()) {
136+
throw new IOException("Unable to recreate fixed LMDB benchmark directory: " + STORE_DIRECTORY);
137+
}
138+
139+
storeConfig = ConfigUtil.createConfig();
140+
store = new LmdbStore(STORE_DIRECTORY, storeConfig);
141+
repository = new SailRepository(store);
142+
loadData();
143+
}
144+
145+
private boolean hasExpectedDbFileSizes() {
146+
return dbFileSize(TRIPLES_DATA_FILE) == EXPECTED_TRIPLES_DATA_SIZE_BYTES
147+
&& dbFileSize(VALUES_DATA_FILE) == EXPECTED_VALUES_DATA_SIZE_BYTES;
148+
}
149+
150+
private long dbFileSize(String relativePath) {
151+
return new File(STORE_DIRECTORY, relativePath).length();
152+
}
153+
114154
private void loadData() throws IOException {
115-
try (SailRepositoryConnection connection = repository.getConnection()) {
155+
try (var connection = repository.getConnection()) {
116156
connection.begin(IsolationLevels.NONE);
117-
RDFInserter inserter = new RDFInserter(connection);
118-
ThemeDataSetGenerator.generate(theme, inserter);
157+
var inserter = new RDFInserter(connection);
158+
for (var themeDataset : Theme.values()) {
159+
ThemeDataSetGenerator.generate(themeDataset, inserter);
160+
}
119161
connection.commit();
120162
}
121163
}
122164

123165
private void captureQueryPlanSnapshot() throws IOException {
124-
BenchmarkQuery benchmarkQuery = ThemeQueryCatalog.benchmarkQueryFor(theme, z_queryIndex);
125-
FeatureFlagCollector featureFlags = new FeatureFlagCollector()
166+
var benchmarkQuery = ThemeQueryCatalog.benchmarkQueryFor(theme, z_queryIndex);
167+
var featureFlags = new FeatureFlagCollector()
126168
.addValue("themeBenchmark.themeName", () -> themeName)
127169
.addValue("themeBenchmark.queryIndex", () -> z_queryIndex)
128170
.addReflectiveGetter("lmdbStore.writable", store, "isWritable")
@@ -133,7 +175,7 @@ private void captureQueryPlanSnapshot() throws IOException {
133175
.addReflectiveGetter("lmdbConfig.tripleDbSize", storeConfig, "getTripleDBSize");
134176
QueryPlanCapture.registerConfiguredFeatureFlags(featureFlags);
135177

136-
QueryPlanCaptureContext context = QueryPlanCaptureContext.builder()
178+
var context = QueryPlanCaptureContext.builder()
137179
.outputDirectory(QueryPlanCapture.resolveOutputDirectory().resolve(STORE_NAME))
138180
.queryId(STORE_NAME + "-" + themeName + "-q" + z_queryIndex)
139181
.queryString(query)
@@ -148,33 +190,38 @@ private void captureQueryPlanSnapshot() throws IOException {
148190
.tupleExprRenderer(this::renderTupleExprWithIr)
149191
.build();
150192

151-
try (SailRepositoryConnection connection = repository.getConnection()) {
152-
Path snapshotPath = new QueryPlanCapture()
193+
try (var connection = repository.getConnection()) {
194+
var snapshotPath = new QueryPlanCapture()
153195
.captureAndWrite(context, () -> connection.prepareTupleQuery(query));
154196
System.out.println("Query plan snapshot written to: " + snapshotPath);
155197
}
156198
}
157199

158200
private String renderTupleExprWithIr(org.eclipse.rdf4j.query.algebra.TupleExpr tupleExpr) {
159-
TupleExprIRRenderer.Config config = new TupleExprIRRenderer.Config();
201+
var config = new TupleExprIRRenderer.Config();
160202
config.verifyRoundTrip = false;
161203
return new TupleExprIRRenderer(config).render(tupleExpr);
162204
}
163205

164206
@TearDown(Level.Trial)
165-
public void tearDown() throws IOException {
166-
repository.shutDown();
167-
FileUtils.deleteDirectory(dataDir);
207+
public void tearDown() {
208+
if (repository != null) {
209+
repository.shutDown();
210+
repository = null;
211+
}
212+
store = null;
213+
storeConfig = null;
168214
}
169215

170216
@Benchmark
171217
public long executeQuery() {
172-
try (SailRepositoryConnection connection = repository.getConnection()) {
173-
long count = connection
174-
.prepareTupleQuery(query)
175-
.evaluate()
176-
.stream()
177-
.count();
218+
try (var connection = repository.getConnection()) {
219+
long count;
220+
try (var evaluate = connection.prepareTupleQuery(query).evaluate()) {
221+
count = evaluate
222+
.stream()
223+
.count();
224+
}
178225

179226
if (count != expected) {
180227
throw new IllegalStateException("Unexpected count: expected " + expected + " but got " + count);
@@ -187,16 +234,16 @@ public long executeQuery() {
187234
@Test
188235
@Disabled
189236
public void testQueryCounts() throws IOException {
190-
String[] queryIndexes = paramValues("z_queryIndex");
191-
String[] themeNames = paramValues("themeName");
192-
for (String themeNameValue : themeNames) {
193-
for (String queryIndexValue : queryIndexes) {
237+
var queryIndexes = paramValues("z_queryIndex");
238+
var themeNames = paramValues("themeName");
239+
for (var themeNameValue : themeNames) {
240+
for (var queryIndexValue : queryIndexes) {
194241
themeName = themeNameValue;
195242
z_queryIndex = Integer.parseInt(queryIndexValue);
196243
setup();
197244
try {
198-
long actual = executeQuery();
199-
long expected = ThemeQueryCatalog.expectedCountFor(theme, z_queryIndex);
245+
var actual = executeQuery();
246+
var expected = ThemeQueryCatalog.expectedCountFor(theme, z_queryIndex);
200247
System.out.println("For theme " + themeName + " and query index " + z_queryIndex
201248
+ ", expected count is " + expected + " and actual count is " + actual);
202249
assertEquals(expected, actual,
@@ -208,17 +255,32 @@ public void testQueryCounts() throws IOException {
208255
}
209256
}
210257

258+
@Test
259+
public void setupVerifiesExpectedDbFileSizesInFixedStore() throws IOException {
260+
themeName = "MEDICAL_RECORDS";
261+
z_queryIndex = 0;
262+
setup();
263+
try {
264+
assertEquals(EXPECTED_TRIPLES_DATA_SIZE_BYTES, dbFileSize(TRIPLES_DATA_FILE),
265+
"Unexpected byte size for " + TRIPLES_DATA_FILE);
266+
assertEquals(EXPECTED_VALUES_DATA_SIZE_BYTES, dbFileSize(VALUES_DATA_FILE),
267+
"Unexpected byte size for " + VALUES_DATA_FILE);
268+
} finally {
269+
tearDown();
270+
}
271+
}
272+
211273
@Test
212274
public void testQueryExplanation() throws IOException {
213-
String[] queryIndexes = paramValues("z_queryIndex");
214-
String[] themeNames = paramValues("themeName");
215-
for (String themeNameValue : themeNames) {
216-
for (String queryIndexValue : queryIndexes) {
275+
var queryIndexes = paramValues("z_queryIndex");
276+
var themeNames = paramValues("themeName");
277+
for (var themeNameValue : themeNames) {
278+
for (var queryIndexValue : queryIndexes) {
217279
themeName = themeNameValue;
218280
z_queryIndex = Integer.parseInt(queryIndexValue);
219281
setup();
220-
try (SailRepositoryConnection connection = repository.getConnection()) {
221-
String explanation = connection
282+
try (var connection = repository.getConnection()) {
283+
var explanation = connection
222284
.prepareTupleQuery(query)
223285
.explain(Explanation.Level.Executed)
224286
.toString();
@@ -233,7 +295,7 @@ public void testQueryExplanation() throws IOException {
233295

234296
private static String[] paramValues(String fieldName) {
235297
try {
236-
Param param = ThemeQueryBenchmark.class.getField(fieldName).getAnnotation(Param.class);
298+
var param = ThemeQueryBenchmark.class.getField(fieldName).getAnnotation(Param.class);
237299
if (param == null) {
238300
throw new IllegalStateException("Missing @Param annotation for field " + fieldName);
239301
}
@@ -242,4 +304,5 @@ private static String[] paramValues(String fieldName) {
242304
throw new IllegalStateException("Missing field " + fieldName, e);
243305
}
244306
}
307+
245308
}

core/sail/memory/src/test/java/org/eclipse/rdf4j/sail/memory/benchmark/ThemeQueryBenchmark.java

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
package org.eclipse.rdf4j.sail.memory.benchmark;
1313

1414
import static org.junit.jupiter.api.Assertions.assertEquals;
15+
import static org.junit.jupiter.api.Assertions.assertTrue;
1516

1617
import java.io.IOException;
1718
import java.nio.file.Path;
@@ -107,7 +108,9 @@ private void loadData() throws IOException {
107108
try (SailRepositoryConnection connection = repository.getConnection()) {
108109
connection.begin(IsolationLevels.NONE);
109110
RDFInserter inserter = new RDFInserter(connection);
110-
ThemeDataSetGenerator.generate(theme, inserter);
111+
for (Theme themeDataset : Theme.values()) {
112+
ThemeDataSetGenerator.generate(themeDataset, inserter);
113+
}
111114
connection.commit();
112115
}
113116
}
@@ -197,6 +200,26 @@ public void testQueryCounts() throws IOException {
197200
}
198201
}
199202

203+
@Test
204+
public void setupLoadsAllThemesIntoRepository() throws IOException {
205+
themeName = "MEDICAL_RECORDS";
206+
z_queryIndex = 0;
207+
setup();
208+
try (SailRepositoryConnection connection = repository.getConnection()) {
209+
boolean hasMedicalPatients = connection.prepareBooleanQuery(
210+
"PREFIX med: <http://example.com/theme/medical/> ASK { ?s a med:Patient . }")
211+
.evaluate();
212+
boolean hasSocialUsers = connection.prepareBooleanQuery(
213+
"PREFIX social: <http://example.com/theme/social/> ASK { ?s a social:User . }")
214+
.evaluate();
215+
216+
assertTrue(hasMedicalPatients, "Expected medical theme data to be present");
217+
assertTrue(hasSocialUsers, "Expected social theme data to be present");
218+
} finally {
219+
tearDown();
220+
}
221+
}
222+
200223
@Test
201224
public void testQueryExplanation() throws IOException {
202225
String[] queryIndexes = paramValues("z_queryIndex");

core/sail/nativerdf/src/test/java/org/eclipse/rdf4j/sail/nativerdf/benchmark/ThemeQueryBenchmark.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,9 @@ private void loadData() throws IOException {
100100
try (SailRepositoryConnection connection = repository.getConnection()) {
101101
connection.begin(IsolationLevels.NONE);
102102
RDFInserter inserter = new RDFInserter(connection);
103-
ThemeDataSetGenerator.generate(theme, inserter);
103+
for (Theme themeDataset : Theme.values()) {
104+
ThemeDataSetGenerator.generate(themeDataset, inserter);
105+
}
104106
connection.commit();
105107
}
106108
}

pom.xml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,8 @@
5959
<logback.version>1.2.13</logback.version>
6060
<log4j.version>2.25.3</log4j.version>
6161
<httpclient.version>4.5.14</httpclient.version>
62-
<jackson.version>2.13.5</jackson.version>
62+
<jackson.version>2.21.0</jackson.version>
63+
<jackson.annotations.version>2.21</jackson.annotations.version>
6364
<httpcore.version>4.4.16</httpcore.version>
6465
<jsonldjava.version>0.13.4</jsonldjava.version>
6566
<last.japicmp.compare.version>5.0.0</last.japicmp.compare.version>
@@ -75,7 +76,7 @@
7576
<junit.version>5.9.3</junit.version>
7677
<jetty.version>9.4.54.v20240208</jetty.version>
7778
<netty.version>4.1.111.Final</netty.version>
78-
<testcontainers.version>1.20.6</testcontainers.version>
79+
<testcontainers.version>2.0.3</testcontainers.version>
7980
<argLine />
8081
</properties>
8182
<profiles>
@@ -455,7 +456,7 @@
455456
<dependency>
456457
<groupId>com.fasterxml.jackson.core</groupId>
457458
<artifactId>jackson-annotations</artifactId>
458-
<version>${jackson.version}</version>
459+
<version>${jackson.annotations.version}</version>
459460
</dependency>
460461
<dependency>
461462
<groupId>com.opencsv</groupId>

0 commit comments

Comments
 (0)