]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.db.indexing/src/org/simantics/db/indexing/DatabaseIndexing.java
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 / DatabaseIndexing.java
index c8efdc2509edd9e5fedd59f32f9e5b8ad68bfdc6..f8a19504c8dc0165f38ddb34f27b7b31fe8a38ed 100644 (file)
  *******************************************************************************/
 package org.simantics.db.indexing;
 
-import java.io.File;
-import java.io.FileFilter;
 import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
 
 import org.simantics.db.Resource;
 import org.simantics.db.Session;
 import org.simantics.db.WriteGraph;
 import org.simantics.db.common.request.IndexRoot;
 import org.simantics.db.common.request.WriteRequest;
-import org.simantics.db.common.utils.Logger;
 import org.simantics.db.exception.DatabaseException;
+import org.simantics.db.indexing.internal.IndexChangedWriter;
 import org.simantics.db.layer0.adapter.GenericRelationIndex;
 import org.simantics.db.layer0.genericrelation.IndexedRelations;
 import org.simantics.db.layer0.internal.SimanticsInternal;
 import org.simantics.db.service.ServerInformation;
 import org.simantics.utils.FileUtils;
+import org.slf4j.LoggerFactory;
 
 /**
  * A facade for Simantics graph database index management facilities.
@@ -35,18 +40,13 @@ import org.simantics.utils.FileUtils;
  */
 public final class DatabaseIndexing {
 
-    private static final boolean DEBUG = IndexPolicy.TRACE_INDEX_MANAGEMENT;
+    private static final org.slf4j.Logger LOGGER = LoggerFactory.getLogger(DatabaseIndexing.class);
 
-    public static File getIndexBaseLocation() {
-       return Activator.getDefault().getIndexBaseFile();
-//     Activator activator = Activator.getDefault();
-//        Bundle b = Platform.getBundle(Activator.BUNDLE_ID);
-//        IPath state = Platform.getStateLocation(b);
-//        File path = state.append("index").toFile();
-//        return path;
+    public static Path getIndexBaseLocation() {
+        return Activator.getDefault().getIndexBaseFile();
     }
 
-    public static File getIndexLocation(Session session, Resource relation, Resource input) {
+    public static Path getIndexLocation(Session session, Resource relation, Resource input) {
         if (session == null)
             throw new NullPointerException("null session");
         if (relation == null)
@@ -58,66 +58,93 @@ public final class DatabaseIndexing {
         + "." + relation.getResourceId()
         + "." + input.getResourceId();
 
-        return new File(getIndexBaseLocation(), dir);
+        return getIndexBaseLocation().resolve(dir);
     }
 
-    private static File getAllDirtyFile() {
-        return new File(getIndexBaseLocation(), ".dirty");
+    private static Path getAllDirtyFile() {
+        return getIndexBaseLocation().resolve(".dirty");
     }
 
-    private static File getChangedFile(File indexPath) {
-        return new File(indexPath, ".changed");
+    private static Path getChangedFile(Path indexPath) {
+        return indexPath.resolve(".changed");
     }
 
     public static void markAllDirty() throws IOException {
-        File indexBase = getIndexBaseLocation();
-        if (!indexBase.exists() || !indexBase.isDirectory())
+        Path indexBase = getIndexBaseLocation();
+        if (!Files.exists(indexBase) || !Files.isDirectory(indexBase))
             return;
-        if (DEBUG)
-            System.out.println("Marking all indexes dirty");
-        getAllDirtyFile().createNewFile();
+        if (LOGGER.isDebugEnabled())
+            LOGGER.debug("Marking all indexes dirty");
+        Path allDirtyFile = getAllDirtyFile();
+        if (!Files.exists(allDirtyFile)) {
+            Files.createFile(allDirtyFile);
+            FileUtils.sync(allDirtyFile);
+        }
     }
 
     public static void clearAllDirty() throws IOException {
-        if (DEBUG)
-            System.out.println("Clearing dirty state of all indexes");
+        if (LOGGER.isDebugEnabled())
+            LOGGER.debug("Clearing dirty state of all indexes");
 
-        File indexBase = getIndexBaseLocation();
-        if (!indexBase.exists() || !indexBase.isDirectory())
+        Path indexBase = getIndexBaseLocation();
+        if (!Files.exists(indexBase) || !Files.isDirectory(indexBase))
             return;
-        delete(getAllDirtyFile());
 
-        forEachIndexPath(new Procedure<File, IOException>() {
-            @Override
-            public void execute(File indexPath) throws IOException {
-                delete(getChangedFile(indexPath));
+        forEachIndexPath(indexPath -> {
+            Path p = getChangedFile(indexPath);
+            try {
+                FileUtils.delete(p);
+            } catch (IOException e) {
+                LOGGER.error("Could not delete {}", p.toAbsolutePath(), e);
             }
         });
-    }
 
+        FileUtils.delete(getAllDirtyFile());
+    }
+    
     /**
      * Internal to indexing, invoked by {@link IndexedRelationsImpl} which
      * doesn't want to throw these exceptions forward. Just log it.
      * 
      * @param indexPath
      */
-    static void markIndexChanged(File indexPath) {
-        if (!indexPath.exists())
-            throw new IllegalArgumentException("index path " + indexPath + " does not exist");
-        if (!indexPath.isDirectory())
-            throw new IllegalArgumentException("index path " + indexPath + " is not a directory");
+    static void markIndexChanged(Session session, Path indexPath) {
+        if (LOGGER.isDebugEnabled())
+            LOGGER.debug("Marking index dirty: " + indexPath);
+        Path changedFile = getChangedFile(indexPath);
         try {
-            if (DEBUG)
-                System.out.println("Marking index dirty: " + indexPath);
-            getChangedFile(indexPath).createNewFile();
+            
+            // Mark change only once per DB session.
+            if (getIndexChangedWriter(session).markDirty(changedFile)) {
+                Files.createDirectories(indexPath);
+                Files.createFile(changedFile);
+                FileUtils.sync(changedFile);
+            }
         } catch (IOException e) {
-            Logger.defaultLogError(e);
+            LOGGER.error("Could not mark index changed for indexPath={} and changedFile={}", indexPath.toAbsolutePath(), changedFile.toAbsolutePath());
+        }
+    }
+
+    private static IndexChangedWriter getIndexChangedWriter(Session session) {
+        IndexChangedWriter writer = session.peekService(IndexChangedWriter.class);
+        if (writer == null) {
+            synchronized (IndexChangedWriter.class) {
+                if (writer == null)
+                    session.registerService(IndexChangedWriter.class, writer = new IndexChangedWriter());
+            }
         }
+        return writer;
     }
 
     public static void deleteAllIndexes() throws IOException {
-        File indexBase = DatabaseIndexing.getIndexBaseLocation();
-        delete(indexBase);
+        Path indexBase = DatabaseIndexing.getIndexBaseLocation();
+
+        ArrayList<Path> filter = new ArrayList<>(2);
+        filter.add(getAllDirtyFile());
+        filter.add(indexBase);
+
+        FileUtils.deleteWithFilter(indexBase, path -> !filter.contains(path));
+        FileUtils.delete(indexBase);
     }
 
     public static void deleteIndex(final Resource relation, final Resource modelPart) throws DatabaseException {
@@ -145,65 +172,60 @@ public final class DatabaseIndexing {
        
     }
     
-    public static void deleteIndex(File indexPath) throws IOException {
-        if (DEBUG)
-            System.out.println("Deleting index " + indexPath);
-        delete(indexPath);
+    public static void deleteIndex(Path indexPath) throws IOException {
+        if (LOGGER.isDebugEnabled())
+            LOGGER.debug("Deleting index " + indexPath.toAbsolutePath());
+
+        ArrayList<Path> filter = new ArrayList<>(2);
+        filter.add(getChangedFile(indexPath));
+        filter.add(indexPath);
+
+        FileUtils.deleteWithFilter(indexPath, path -> !filter.contains(path));
+        FileUtils.delete(indexPath);
     }
 
     public static void validateIndexes() throws IOException {
-        File indexBase = getIndexBaseLocation();
-        if (DEBUG)
-            System.out.println("Validating indexes at " + indexBase);
-        if (!indexBase.exists())
+        Path indexBase = getIndexBaseLocation();
+        if (LOGGER.isDebugEnabled())
+            LOGGER.debug("Validating indexes at " + indexBase);
+        if (!Files.exists(indexBase))
             return;
-        if (!indexBase.isDirectory()) {
+        if (!Files.isDirectory(indexBase)) {
             // Make sure that index-base is a valid directory
-            if (DEBUG)
-                System.out.println(indexBase + " is not a directory! Removing it.");
-            delete(indexBase);
-            indexBase.mkdirs();
+            if (LOGGER.isDebugEnabled())
+                LOGGER.debug(indexBase + " is not a directory! Removing it.");
+            FileUtils.delete(indexBase);
+            Files.createDirectories(indexBase);
             return;
         }
-        File allDirtyFile = getAllDirtyFile();
-        if (allDirtyFile.isFile()) {
-            if (DEBUG)
-                System.out.println("All indexes marked dirty, removing them.");
-            delete(allDirtyFile);
+        Path allDirtyFile = getAllDirtyFile();
+        if (Files.isRegularFile(allDirtyFile)) {
+            if (LOGGER.isDebugEnabled())
+                LOGGER.debug("All indexes marked dirty, removing them.");
             deleteAllIndexes();
         } else {
-            forEachIndexPath(new Procedure<File, IOException>() {
-                @Override
-                public void execute(File indexPath) throws IOException {
-                    File changed = getChangedFile(indexPath);
-                    if (changed.isFile()) {
-                        if (DEBUG)
-                            System.out.println("Index is dirty, removing: " + indexPath);
+            forEachIndexPath(indexPath -> {
+                Path changed = getChangedFile(indexPath);
+                if (Files.isRegularFile(changed)) {
+                    if (LOGGER.isDebugEnabled())
+                        LOGGER.debug("Index is dirty, removing: " + indexPath);
+                    try {
                         deleteIndex(indexPath);
+                    } catch (IOException e) {
+                        LOGGER.error("Could not delete index {}", indexPath.toAbsolutePath(), e);
                     }
                 }
             });
         }
     }
 
-
-    private static void delete(File fileOrDir) throws IOException {
-        if (fileOrDir.exists())
-            FileUtils.deleteAll(fileOrDir);
-    }
-
-    interface Procedure<T, E extends Throwable> {
-        void execute(T t) throws E;
-    }
-
-    private static <E extends Throwable> void forEachIndexPath(Procedure<File, E> callback) throws E {
-        for (File indexPath : getIndexBaseLocation().listFiles(new FileFilter() {
-            @Override
-            public boolean accept(File pathname) {
-                return pathname.isDirectory();
+    private static void forEachIndexPath(Consumer<Path> callback) throws IOException {
+        try (Stream<Path> paths = Files.walk(getIndexBaseLocation(), 1).filter(Files::isDirectory)) {
+            Iterator<Path> iter = paths.iterator();
+            while (iter.hasNext()) {
+                Path p = iter.next();
+                callback.accept(p);
             }
-        })) {
-            callback.execute(indexPath);
         }
     }