1515
1616import java .io .File ;
1717import java .io .IOException ;
18- import java .nio .file .Path ;
1918import java .util .concurrent .TimeUnit ;
2019
2120import org .apache .commons .io .FileUtils ;
22- import org .assertj .core .util .Files ;
23- import org .eclipse .rdf4j .benchmark .common .BenchmarkQuery ;
2421import org .eclipse .rdf4j .benchmark .common .ThemeQueryCatalog ;
2522import org .eclipse .rdf4j .benchmark .common .plan .FeatureFlagCollector ;
2623import org .eclipse .rdf4j .benchmark .common .plan .QueryPlanCapture ;
3128import org .eclipse .rdf4j .query .explanation .Explanation ;
3229import org .eclipse .rdf4j .queryrender .sparql .TupleExprIRRenderer ;
3330import org .eclipse .rdf4j .repository .sail .SailRepository ;
34- import org .eclipse .rdf4j .repository .sail .SailRepositoryConnection ;
3531import org .eclipse .rdf4j .repository .util .RDFInserter ;
3632import org .eclipse .rdf4j .sail .lmdb .LmdbStore ;
3733import org .eclipse .rdf4j .sail .lmdb .config .LmdbStoreConfig ;
5248import org .openjdk .jmh .annotations .Warmup ;
5349import org .openjdk .jmh .runner .Runner ;
5450import org .openjdk .jmh .runner .RunnerException ;
55- import org .openjdk .jmh .runner .options .Options ;
5651import org .openjdk .jmh .runner .options .OptionsBuilder ;
5752
5853@ State (Scope .Benchmark )
6459public 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}
0 commit comments