Improve startup time for fresh or rollback'd session in index writing
[simantics/platform.git] / bundles / org.simantics.db.indexing / src / org / simantics / db / indexing / IndexedRelationsImpl.java
index 1d027a8252c6c4894f9a3b87cc94ceb033f4a536..390c0d17fce817225bde69d07e4515408fee4229 100644 (file)
  *******************************************************************************/
 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;
 
@@ -28,15 +32,19 @@ import org.eclipse.core.runtime.SubMonitor;
 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.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;
@@ -130,7 +138,7 @@ public class IndexedRelationsImpl implements IndexedRelations {
         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>() {
 
@@ -542,4 +550,79 @@ public class IndexedRelationsImpl implements IndexedRelations {
 
     }
 
+    @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 DatabaseException(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 DatabaseException("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);
+        }
+    }
+
 }