Skip to content

Commit

Permalink
GH-3523 close iterator, higher cost required before considering cachi…
Browse files Browse the repository at this point in the history
…ng, less work when invalidating the cache and also some other fixes

Signed-off-by: Håvard Ottestad <hmottestad@gmail.com>
  • Loading branch information
hmottestad committed Jan 5, 2022
1 parent 49d8e79 commit 78d9390
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 52 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,20 @@ public class CachingMemStatementIteration<X extends Exception> extends LookAhead
private Exception e;

public CachingMemStatementIteration(MemStatementIterator<X> iterator, MemorySailStore memorySailStore) {
if (memorySailStore.shouldBeCached(iterator)) {
CloseableIteration<MemStatement, X> memStatementXCloseableIteration = null;
if (memorySailStore.shouldBeCached(iterator.getMinimal())) {
CloseableIteration<MemStatement, X> cachedIterator = null;
try {
memStatementXCloseableIteration = memorySailStore.cacheIterator(iterator);
cachedIterator = memorySailStore.cacheIterator(iterator);
} catch (Exception e) {
this.e = e;
} finally {
try {
iterator.close();
} catch (Exception ex) {
this.e = ex;
}
}
this.iterator = memStatementXCloseableIteration;
this.iterator = cachedIterator;
this.usesCache = true;
} else {
this.iterator = iterator;
Expand Down Expand Up @@ -64,7 +70,7 @@ protected void handleClose() throws X {
iterator.close();
} finally {
if (!usesCache && ((MemStatementIterator<X>) iterator).considerForCaching()) {
memorySailStore.addCacheableIteration(((MemStatementIterator<X>) iterator));
memorySailStore.incrementIteratorFrequencyMap(((MemStatementIterator<X>) iterator).getMinimal());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,14 @@ class MemorySailStore implements SailStore {
public static final EmptyIteration<MemStatement, SailException> EMPTY_ITERATION = new EmptyIteration<>();
public static final EmptyIteration<MemTriple, SailException> EMPTY_TRIPLE_ITERATION = new EmptyIteration<>();
public static final MemResource[] EMPTY_CONTEXT = new MemResource[0];
public static final int CACHE_FREQUENCY_THRESHOLD = 10;
private final Logger logger = LoggerFactory.getLogger(MemorySailStore.class);

// a map that tracks the number of times a cacheable iterator has been used
private final ConcurrentHashMap<MemStatementIterator<? extends Exception>, Integer> cacheCount = new ConcurrentHashMap<>();
private final ConcurrentHashMap<MemStatementIterator.Minimal<? extends Exception>, Integer> iteratorFrequencyMap = new ConcurrentHashMap<>();

// a cache for commonly used iterators that are particularly costly
private final Cache<MemStatementIterator<? extends Exception>, List<MemStatement>> iteratorCache = CacheBuilder
private final Cache<MemStatementIterator.Minimal<? extends Exception>, List<MemStatement>> iteratorCache = CacheBuilder
.newBuilder()
.softValues()
.build();
Expand Down Expand Up @@ -155,8 +156,16 @@ public void close() {
}

private void invalidateCache() {
cacheCount.clear();
iteratorCache.invalidateAll();
if (!(iteratorFrequencyMap.isEmpty() && iteratorCache.size() == 0)) {
logger.debug("Invalidated cache");

if (logger.isTraceEnabled()) {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
logger.debug("Invalidated cache called from {}", stackTrace[2]);
}
iteratorFrequencyMap.clear();
iteratorCache.invalidateAll();
}
}

@Override
Expand Down Expand Up @@ -438,31 +447,37 @@ protected void scheduleSnapshotCleanup() {
}
}

<X extends Exception> void addCacheableIteration(MemStatementIterator<X> iterator) {
cacheCount.compute(iterator, (key, value) -> {
<X extends Exception> void incrementIteratorFrequencyMap(MemStatementIterator.Minimal<X> iterator) {
Integer compute = iteratorFrequencyMap.compute(iterator, (key, value) -> {
if (value == null) {
return 0;
}
return value + 1;
});
if (logger.isDebugEnabled()) {
logger.debug("Incremented iteratorFrequencyMap to {}\n{} \n{}", compute, iterator, iterator.getStats());
}
}

<X extends Exception> boolean shouldBeCached(MemStatementIterator<X> iterator) {
Integer integer = cacheCount.get(iterator);
return integer != null && integer > 10;
<X extends Exception> boolean shouldBeCached(MemStatementIterator.Minimal<X> iterator) {
Integer integer = iteratorFrequencyMap.get(iterator);
return integer != null && integer > CACHE_FREQUENCY_THRESHOLD;
}

public <X extends Exception> CloseableIteration<MemStatement, X> cacheIterator(MemStatementIterator<X> iterator)
throws Exception {
List<MemStatement> cached = iteratorCache.getIfPresent(iterator);

MemStatementIterator.Minimal<X> minimal = iterator.getMinimal();

List<MemStatement> cached = iteratorCache.getIfPresent(minimal);

if (cached == null) {
logger.debug("Filling cache for MemStatementIterator {}", iterator);
logger.debug("Filling cache {}", iterator);
cached = new ArrayList<>();
while (iterator.hasNext()) {
cached.add(iterator.next());
}
iteratorCache.put(iterator, cached);
iteratorCache.put(minimal, cached);
}

return new CloseableIteratorIteration<>(cached.iterator());
Expand Down Expand Up @@ -566,8 +581,8 @@ public synchronized void prepare() throws SailException {

@Override
public synchronized void flush() throws SailException {
invalidateCache();
if (txnLock) {
invalidateCache();
currentSnapshot = Math.max(currentSnapshot, nextSnapshot);
if (requireCleanup) {
scheduleSnapshotCleanup();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,58 +167,120 @@ private boolean matchesExplicitAndSnapshot(MemStatement st) {

/**
* Returnes true if this iterator was particularly costly and should be cached
*
*
* @return
*/
public boolean considerForCaching() {
if (exhausted) { // we will only consider caching if the iterator has been completely consumed
if (statementIndex > 100) { // minimum 100 statements need to have been checked by the iterator
if (statementIndex > 1000) { // minimum 1000 statements need to have been checked by the iterator
if (matchingStatements == 0) { // if the iterator was effectively empty we can always cache it
return true;
} else if (matchingStatements < 100) { // we will not cache iterators that returned more than 99
// statements
// statements
double ratio = (statementIndex + 0.0) / matchingStatements;
return ratio > 100; // for every returned statement we need to have checked 100 non-matching
// statements
// statements
}
}
}
return false;
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
public Minimal<X> getMinimal() {
return new Minimal<X>(this);
}

public static class Minimal<X extends Exception> {

private final MemResource subject;
private final MemIRI predicate;
private final MemValue object;
private final MemResource[] contexts;

private final boolean explicit;
private final boolean explicitNotSpecified;

private final int snapshot;
private final boolean noIsolation;
private final int statementIndex;
private final int matchingStatements;

public Minimal(MemStatementIterator<X> memStatementIterator) {
this.subject = memStatementIterator.subject;
this.predicate = memStatementIterator.predicate;
this.object = memStatementIterator.object;
this.contexts = memStatementIterator.contexts;
this.explicit = memStatementIterator.explicit;
this.explicitNotSpecified = memStatementIterator.explicitNotSpecified;
this.snapshot = memStatementIterator.snapshot;
this.noIsolation = memStatementIterator.noIsolation;
this.statementIndex = memStatementIterator.statementIndex;
this.matchingStatements = memStatementIterator.matchingStatements;
}
if (!(o instanceof MemStatementIterator)) {
return false;

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof MemStatementIterator)) {
return false;
}
MemStatementIterator<?> that = (MemStatementIterator<?>) o;
return explicit == that.explicit && explicitNotSpecified == that.explicitNotSpecified
&& snapshot == that.snapshot && noIsolation == that.noIsolation
&& Objects.equals(subject, that.subject)
&& Objects.equals(predicate, that.predicate) && Objects.equals(object, that.object)
&& Arrays.equals(contexts, that.contexts);
}
MemStatementIterator<?> that = (MemStatementIterator<?>) o;
return explicit == that.explicit && explicitNotSpecified == that.explicitNotSpecified
&& snapshot == that.snapshot && noIsolation == that.noIsolation && Objects.equals(subject, that.subject)
&& Objects.equals(predicate, that.predicate) && Objects.equals(object, that.object)
&& Arrays.equals(contexts, that.contexts);
}

@Override
public int hashCode() {
int result = Objects.hash(subject, predicate, object, explicit, explicitNotSpecified, snapshot, noIsolation);
result = 31 * result + Arrays.hashCode(contexts);
return result;
}
private int cachedHashCode = 0;

@Override
public String toString() {
return "MemStatementIterator{" +
"subject=" + subject +
", predicate=" + predicate +
", object=" + object +
", contexts=" + Arrays.toString(contexts) +
", explicit=" + explicit +
", explicitNotSpecified=" + explicitNotSpecified +
", snapshot=" + snapshot +
", noIsolation=" + noIsolation +
'}';
@Override
public int hashCode() {
if (cachedHashCode == 0) {
int cachedHashCode = Objects.hash(subject, predicate, object, explicit, explicitNotSpecified, snapshot,
noIsolation);
cachedHashCode = 31 * cachedHashCode + Arrays.hashCode(contexts);
this.cachedHashCode = cachedHashCode;
}
return cachedHashCode;
}

@Override
public String toString() {
return "MemStatementIterator{" +
"subject=" + subject +
", predicate=" + predicate +
", object=" + object +
", contexts=" + Arrays.toString(contexts) +
", explicit=" + explicit +
", explicitNotSpecified=" + explicitNotSpecified +
", snapshot=" + snapshot +
", noIsolation=" + noIsolation +
'}';
}

public Stats getStats() {
return new Stats(statementIndex, matchingStatements);
}

static class Stats {
private final int checkedStatements;
private final int matchingStatements;

public Stats(int checkStatements, int matchingStatements) {
this.checkedStatements = checkStatements;
this.matchingStatements = matchingStatements;
}

@Override
public String toString() {
return "Stats{" +
"checkedStatements=" + checkedStatements +
", matchingStatements=" + matchingStatements +
'}';
}
}
}
}

0 comments on commit 78d9390

Please sign in to comment.