*******************************************************************************/
package org.simantics.db.indexing;
-import java.io.File;
import java.io.IOException;
+import java.nio.file.Path;
+import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.WeakHashMap;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.simantics.db.ReadGraph;
import org.simantics.db.RequestProcessor;
import org.simantics.db.Resource;
+import org.simantics.db.common.request.ReadRequest;
import org.simantics.db.common.request.UniqueRead;
+import org.simantics.db.common.utils.NameUtils;
import org.simantics.db.exception.DatabaseException;
import org.simantics.db.exception.InvalidResourceReferenceException;
import org.simantics.db.indexing.IndexedRelationsSearcherBase.State;
+import org.simantics.db.indexing.exception.IndexingException;
import org.simantics.db.layer0.adapter.GenericRelation;
import org.simantics.db.layer0.genericrelation.IndexException;
import org.simantics.db.layer0.genericrelation.IndexedRelations;
+import org.simantics.db.layer0.util.Layer0Utils;
import org.simantics.db.service.QueryControl;
import org.simantics.db.service.SerialisationSupport;
+import org.simantics.operation.Layer0X;
import org.simantics.utils.datastructures.Pair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
/**
* @author Tuukka Lehtonen
*/
public class IndexedRelationsImpl implements IndexedRelations {
+ private static final Logger LOGGER = LoggerFactory.getLogger(IndexedRelationsImpl.class);
+
Map<Object, RWLock> indexLocks = new WeakHashMap<Object, RWLock>();
static class LockHandle {
return rwlock.tryLock(processor, write);
}
- private IndexedRelationsSearcherBase makeSearcher(final RequestProcessor processor, final Resource relation, final Resource input) {
+ private static IndexedRelationsSearcherBase makeSearcher(final RequestProcessor processor, final Resource relation, final Resource input) {
try {
return processor.syncRequest(new UniqueRead<IndexedRelationsSearcherBase>() {
// 3. something was wrong, but the index has been successfully cleaned
}
- if(!searcher.checkState(State.NONE))
- throw new IndexException("Illegal searcher state, contact application support.");
-
+ searcher.assertState(State.NONE);
+
// We loop until the index is loaded
while(true) {
searcher.startAccess(null, graph.getSession(), false);
// At this point we have three options:
// 1. we have access
- if(searcher.hasAccess(false)) loaded = true;
+ if(searcher.hasAccess(false))
+ loaded = true;
// 2. something is wrong and the index cannot be cleaned
- if(searcher.checkState(State.PROBLEM)) throw new DatabaseException("Searcher is in problematic state", searcher.getException());
+ if(searcher.checkState(State.PROBLEM))
+ throw new IndexingException("Searcher is in problematic state", searcher.getException());
// 3. something was wrong, but the index has been successfully cleaned
}
if(!loaded) {
if(!searcher.checkState(State.NONE))
- throw new DatabaseException("Illegal searcher state, contact application support.");
+ throw new IndexingException("Illegal searcher state " + searcher.state());
try {
SerialisationSupport ss = graph.getService(SerialisationSupport.class);
searcher.setReady();
} catch (IOException e) {
searcher.setProblem(e);
- throw new DatabaseException(e);
+ throw new IndexingException(e);
}
}
}
if(!success)
- throw new IndexException("Did not manage to load index. Contact application support.");
+ throw new IndexException("Did not manage to load index.");
// Try again
if (searcher.isIndexAvailable()) {
searcher.startAccess(progress.newChild(50), processor.getSession(), false);
if(searcher.hasAccess(false)) return lock;
- throw new IndexException("Illegal searcher state, contact application support.");
+ throw new IndexException("Illegal searcher state " + searcher.state());
}
}
@Override
public void insert(IProgressMonitor monitor, RequestProcessor processor, GenericRelation relation,
- Resource relationResource, Resource input, Collection<Object[]> documents) {
+ Resource relationResource, Resource input, Collection<Object[]> documents) throws IndexException {
// System.out.println("Inserting to index: " + input + " " + documents);
@Override
public void remove(IProgressMonitor monitor, RequestProcessor processor, GenericRelation relation,
- Resource relationResource, Resource input, String key, Collection<Object> keyValues) {
+ Resource relationResource, Resource input, String key, Collection<Object> keyValues) throws IndexException {
if (relation == null)
throw new IllegalArgumentException("null relation");
@Override
public boolean replace(IProgressMonitor monitor, RequestProcessor processor, GenericRelation relation,
- Resource relationResource, Resource input, String key, Collection<Object> keyValues, Collection<Object[]> documents) {
+ Resource relationResource, Resource input, String key, Collection<Object> keyValues, Collection<Object[]> documents) throws IndexException {
if (relation == null)
throw new IllegalArgumentException("null relation");
IndexedRelationsSearcherBase searcher = makeSearcher(processor, relationResource, input);
LockHandle handle = lock(processor, Pair.make(relationResource, input), true);
-
+ Path path = DatabaseIndexing.getIndexLocation(processor.getSession(), relationResource, input);
try {
searcher.changeState(monitor, processor.getSession(), State.NONE);
if (!searcher.checkState(State.NONE))
throw new IndexException("Could not close index for input " + input + " before removing it");
- File path = DatabaseIndexing.getIndexLocation(processor.getSession(), relationResource, input);
DatabaseIndexing.deleteIndex(path);
-
} catch (IOException e) {
+ LOGGER.error("Could not delete {}", path.toAbsolutePath(), e);
throw new IndexException(e);
} finally {
handle.unlock();
}
+ @Override
+ public void fullRebuild(IProgressMonitor monitor, RequestProcessor processor) throws IndexException {
+ try {
+ processor.syncRequest(new ReadRequest() {
+ @Override
+ public void run(ReadGraph graph) throws DatabaseException {
+ try {
+ fullRebuild(monitor, graph);
+ } catch (IOException e) {
+ throw new IndexingException(e);
+ }
+ }
+ });
+ } catch (DatabaseException e) {
+ throw new IndexException(e);
+ }
+ }
+
+ private void fullRebuild(IProgressMonitor monitor, ReadGraph graph) throws DatabaseException, IOException {
+ long startTime = System.currentTimeMillis();
+ Resource relation = Layer0X.getInstance(graph).DependenciesRelation;
+ SerialisationSupport ss = graph.getService(SerialisationSupport.class);
+ Set<Resource> indexRoots = Layer0Utils.listIndexRoots(graph);
+ List<CompletableFuture<?>> waitFor = new ArrayList<>(indexRoots.size());
+ SubMonitor mon = SubMonitor.convert(monitor, indexRoots.size()*2);
+
+ for (Resource indexRoot : indexRoots) {
+ monitor.subTask(NameUtils.getSafeName(graph, indexRoot));
+
+ IndexedRelationsSearcherBase searcher = makeSearcher(graph, relation, indexRoot);
+
+ GenericRelation r = graph.adapt(relation, GenericRelation.class);
+ if (r == null)
+ throw new IndexingException("Given resource " + relation + "could not be adapted to GenericRelation.");
+
+ Object[] bound = new Object[] { ss.getRandomAccessId(indexRoot) };
+ GenericRelation selection = r.select(IndexedRelationsSearcherBase.getPattern(r, bound.length), bound);
+
+ long relStart = System.currentTimeMillis();
+ List<Object[]> results = selection.realize(graph);
+ if (LOGGER.isDebugEnabled()) {
+ long relEnd = System.currentTimeMillis() - relStart;
+ LOGGER.debug(indexRoot + " realized " + relEnd);
+ }
+ mon.worked(1);
+
+ CompletableFuture<?> result = new CompletableFuture<>();
+ waitFor.add(result);
+ ForkJoinPool.commonPool().submit(() -> {
+ long startTime1 = System.currentTimeMillis();
+ try {
+ searcher.initializeIndexImpl(result, mon.newChild(1, SubMonitor.SUPPRESS_ALL_LABELS), r, results, bound, true);
+ searcher.setReady();
+ } catch (IOException e) {
+ result.completeExceptionally(e);
+ LOGGER.error("Could not initialize index", e);
+ } finally {
+ if (LOGGER.isDebugEnabled())
+ LOGGER.debug(indexRoot + " initialized " + (System.currentTimeMillis() - startTime1));
+ }
+ });
+ }
+ for (CompletableFuture<?> fut : waitFor) {
+ try {
+ fut.get();
+ } catch (InterruptedException | ExecutionException e) {
+ throw (IOException) e.getCause();
+ }
+ }
+ if (LOGGER.isInfoEnabled()) {
+ long endTime = System.currentTimeMillis() - startTime;
+ LOGGER.info("All indexes rebuilt in {}", endTime);
+ }
+ }
+
}