Skip to content

Commit 295b06f

Browse files
committed
GH-5447 general LMDB optimisation
1 parent 5ae6ddf commit 295b06f

11 files changed

Lines changed: 2348 additions & 112 deletions

File tree

core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbRecordIterator.java

Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import org.eclipse.rdf4j.sail.SailException;
3030
import org.eclipse.rdf4j.sail.lmdb.TripleStore.TripleIndex;
3131
import org.eclipse.rdf4j.sail.lmdb.TxnManager.Txn;
32-
import org.eclipse.rdf4j.sail.lmdb.Varint.GroupMatcher;
32+
import org.eclipse.rdf4j.sail.lmdb.util.GroupMatcher;
3333
import org.lwjgl.PointerBuffer;
3434
import org.lwjgl.system.MemoryStack;
3535
import org.lwjgl.util.lmdb.MDBVal;
@@ -45,11 +45,17 @@ class LmdbRecordIterator implements RecordIterator {
4545

4646
private final TripleIndex index;
4747

48+
private final long subj;
49+
private final long pred;
50+
private final long obj;
51+
private final long context;
52+
4853
private final long cursor;
4954

5055
private final MDBVal maxKey;
5156

52-
private final GroupMatcher groupMatcher;
57+
private final boolean matchValues;
58+
private GroupMatcher groupMatcher;
5359

5460
private final Txn txnRef;
5561

@@ -71,7 +77,8 @@ class LmdbRecordIterator implements RecordIterator {
7177

7278
private int lastResult;
7379

74-
private final long[] quad = new long[4];
80+
private final long[] quad;
81+
private final long[] originalQuad;
7582

7683
private boolean fetchNext = false;
7784

@@ -81,6 +88,12 @@ class LmdbRecordIterator implements RecordIterator {
8188

8289
LmdbRecordIterator(TripleIndex index, boolean rangeSearch, long subj, long pred, long obj,
8390
long context, boolean explicit, Txn txnRef) throws IOException {
91+
this.subj = subj;
92+
this.pred = pred;
93+
this.obj = obj;
94+
this.context = context;
95+
this.originalQuad = new long[] { subj, pred, obj, context };
96+
this.quad = new long[] { subj, pred, obj, context };
8497
this.pool = Pool.get();
8598
this.keyData = pool.getVal();
8699
this.valueData = pool.getVal();
@@ -100,12 +113,8 @@ class LmdbRecordIterator implements RecordIterator {
100113
this.maxKey = null;
101114
}
102115

103-
boolean matchValues = subj > 0 || pred > 0 || obj > 0 || context >= 0;
104-
if (matchValues) {
105-
this.groupMatcher = index.createMatcher(subj, pred, obj, context);
106-
} else {
107-
this.groupMatcher = null;
108-
}
116+
this.matchValues = subj > 0 || pred > 0 || obj > 0 || context >= 0;
117+
109118
this.dbi = index.getDB(explicit);
110119
this.txnRef = txnRef;
111120
this.txnLockManager = txnRef.lockManager();
@@ -145,6 +154,7 @@ public long[] next() {
145154
}
146155

147156
if (txnRefVersion != txnRef.version()) {
157+
// TODO: None of the tests in the LMDB Store cover this case!
148158
// cursor must be renewed
149159
mdb_cursor_renew(txn, cursor);
150160
if (fetchNext) {
@@ -188,12 +198,12 @@ public long[] next() {
188198
// if (maxKey != null && TripleStore.COMPARATOR.compare(keyData.mv_data(), maxKey.mv_data()) > 0) {
189199
if (maxKey != null && mdb_cmp(txn, dbi, keyData, maxKey) > 0) {
190200
lastResult = MDB_NOTFOUND;
191-
} else if (groupMatcher != null && !groupMatcher.matches(keyData.mv_data())) {
201+
} else if (matches()) {
192202
// value doesn't match search key/mask, fetch next value
193203
lastResult = mdb_cursor_get(cursor, keyData, valueData, MDB_NEXT);
194204
} else {
195205
// Matching value found
196-
index.keyToQuad(keyData.mv_data(), quad);
206+
index.keyToQuad(keyData.mv_data(), originalQuad, quad);
197207
// fetch next value
198208
fetchNext = true;
199209
return quad;
@@ -206,6 +216,18 @@ public long[] next() {
206216
}
207217
}
208218

219+
private boolean matches() {
220+
221+
if (groupMatcher != null) {
222+
return !this.groupMatcher.matches(keyData.mv_data());
223+
} else if (matchValues) {
224+
this.groupMatcher = index.createMatcher(subj, pred, obj, context);
225+
return !this.groupMatcher.matches(keyData.mv_data());
226+
} else {
227+
return false;
228+
}
229+
}
230+
209231
private void closeInternal(boolean maybeCalledAsync) {
210232
if (!closed) {
211233
long writeStamp = 0L;

core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbSailStore.java

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@
2929
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
3030
import org.eclipse.rdf4j.common.iteration.CloseableIteratorIteration;
3131
import org.eclipse.rdf4j.common.iteration.ConvertingIteration;
32-
import org.eclipse.rdf4j.common.iteration.EmptyIteration;
3332
import org.eclipse.rdf4j.common.iteration.FilterIteration;
3433
import org.eclipse.rdf4j.common.iteration.UnionIteration;
3534
import org.eclipse.rdf4j.common.order.StatementOrder;
@@ -72,6 +71,7 @@ class LmdbSailStore implements SailStore {
7271
private boolean multiThreadingActive;
7372
private volatile boolean asyncTransactionFinished;
7473
private volatile boolean nextTransactionAsync;
74+
private volatile boolean mayHaveInferred;
7575

7676
boolean enableMultiThreading = true;
7777

@@ -143,6 +143,9 @@ class AddQuadOperation implements Operation {
143143

144144
@Override
145145
public void execute() throws IOException {
146+
if (!explicit) {
147+
mayHaveInferred = true;
148+
}
146149
if (!unusedIds.isEmpty()) {
147150
// these ids are used again
148151
unusedIds.remove(s);
@@ -191,8 +194,10 @@ public LmdbSailStore(File dataDir, LmdbStoreConfig config) throws IOException, S
191194
boolean initialized = false;
192195
try {
193196
namespaceStore = new NamespaceStore(dataDir);
194-
valueStore = new ValueStore(new File(dataDir, "values"), config);
195-
tripleStore = new TripleStore(new File(dataDir, "triples"), config);
197+
var valueStore = new ValueStore(new File(dataDir, "values"), config);
198+
this.valueStore = valueStore;
199+
tripleStore = new TripleStore(new File(dataDir, "triples"), config, valueStore);
200+
mayHaveInferred = tripleStore.hasTriples(false);
196201
initialized = true;
197202
} finally {
198203
if (!initialized) {
@@ -348,19 +353,23 @@ protected void handleClose() throws SailException {
348353
*/
349354
CloseableIteration<? extends Statement> createStatementIterator(
350355
Txn txn, Resource subj, IRI pred, Value obj, boolean explicit, Resource... contexts) throws IOException {
356+
if (!explicit && !mayHaveInferred) {
357+
// there are no inferred statements and the iterator should only return inferred statements
358+
return CloseableIteration.EMPTY_STATEMENT_ITERATION;
359+
}
351360
long subjID = LmdbValue.UNKNOWN_ID;
352361
if (subj != null) {
353362
subjID = valueStore.getId(subj);
354363
if (subjID == LmdbValue.UNKNOWN_ID) {
355-
return new EmptyIteration<>();
364+
return CloseableIteration.EMPTY_STATEMENT_ITERATION;
356365
}
357366
}
358367

359368
long predID = LmdbValue.UNKNOWN_ID;
360369
if (pred != null) {
361370
predID = valueStore.getId(pred);
362371
if (predID == LmdbValue.UNKNOWN_ID) {
363-
return new EmptyIteration<>();
372+
return CloseableIteration.EMPTY_STATEMENT_ITERATION;
364373
}
365374
}
366375

@@ -369,7 +378,7 @@ CloseableIteration<? extends Statement> createStatementIterator(
369378
objID = valueStore.getId(obj);
370379

371380
if (objID == LmdbValue.UNKNOWN_ID) {
372-
return new EmptyIteration<>();
381+
return CloseableIteration.EMPTY_STATEMENT_ITERATION;
373382
}
374383
}
375384

core/sail/lmdb/src/main/java/org/eclipse/rdf4j/sail/lmdb/LmdbStatementIterator.java

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@
1111
package org.eclipse.rdf4j.sail.lmdb;
1212

1313
import java.io.IOException;
14+
import java.util.NoSuchElementException;
1415

15-
import org.eclipse.rdf4j.common.iteration.LookAheadIteration;
16+
import org.eclipse.rdf4j.common.iteration.AbstractCloseableIteration;
1617
import org.eclipse.rdf4j.model.IRI;
1718
import org.eclipse.rdf4j.model.Resource;
1819
import org.eclipse.rdf4j.model.Statement;
@@ -23,7 +24,7 @@
2324
* A statement iterator that wraps a RecordIterator containing statement records and translates these records to
2425
* {@link Statement} objects.
2526
*/
26-
class LmdbStatementIterator extends LookAheadIteration<Statement> {
27+
class LmdbStatementIterator extends AbstractCloseableIteration<Statement> {
2728

2829
/*-----------*
2930
* Variables *
@@ -32,6 +33,7 @@ class LmdbStatementIterator extends LookAheadIteration<Statement> {
3233
private final RecordIterator recordIt;
3334

3435
private final ValueStore valueStore;
36+
private Statement nextElement;
3537

3638
/*--------------*
3739
* Constructors *
@@ -49,7 +51,6 @@ public LmdbStatementIterator(RecordIterator recordIt, ValueStore valueStore) {
4951
* Methods *
5052
*---------*/
5153

52-
@Override
5354
public Statement getNextElement() throws SailException {
5455
try {
5556
long[] quad = recordIt.next();
@@ -86,4 +87,52 @@ protected void handleClose() throws SailException {
8687
private SailException causeIOException(IOException e) {
8788
return new SailException(e);
8889
}
90+
91+
@Override
92+
public final boolean hasNext() {
93+
if (isClosed()) {
94+
return false;
95+
}
96+
97+
return lookAhead() != null;
98+
}
99+
100+
@Override
101+
public final Statement next() {
102+
if (isClosed()) {
103+
throw new NoSuchElementException("The iteration has been closed.");
104+
}
105+
Statement result = lookAhead();
106+
107+
if (result != null) {
108+
nextElement = null;
109+
return result;
110+
} else {
111+
throw new NoSuchElementException();
112+
}
113+
}
114+
115+
/**
116+
* Fetches the next element if it hasn't been fetched yet and stores it in {@link #nextElement}.
117+
*
118+
* @return The next element, or null if there are no more results.
119+
*/
120+
private Statement lookAhead() {
121+
if (nextElement == null) {
122+
nextElement = getNextElement();
123+
124+
if (nextElement == null) {
125+
close();
126+
}
127+
}
128+
return nextElement;
129+
}
130+
131+
/**
132+
* Throws an {@link UnsupportedOperationException}.
133+
*/
134+
@Override
135+
public void remove() {
136+
throw new UnsupportedOperationException();
137+
}
89138
}

0 commit comments

Comments
 (0)