]> gerrit.simantics Code Review - simantics/platform.git/commitdiff
Fixed bad bug related to AcornBackupProvider 85/485/3
authorTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Fri, 5 May 2017 13:59:12 +0000 (16:59 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Fri, 5 May 2017 21:02:52 +0000 (00:02 +0300)
AcornBackupProvider was invoking GraphClientImpl2.makeSnapshot with true
parameter which caused the invocation to write the main.state file when
backups were made which was totally unintentional. The main.state file
is now only written only when the database session is shut down, i.e.
not at all in ClusterManager.makeSnapshot.

Also includes robustness fixes for Acorn database directory locking by
using the Java NIO API for atomic existence testing and creation of the
lock file.

refs #7186

Change-Id: Id668528f8aea789945a4ae3a212d14b07554ba0d

bundles/org.simantics.acorn/src/org/simantics/acorn/AcornManagement.java
bundles/org.simantics.acorn/src/org/simantics/acorn/ClusterManager.java
bundles/org.simantics.acorn/src/org/simantics/acorn/GraphClientImpl2.java
bundles/org.simantics.acorn/src/org/simantics/acorn/internal/AcornDatabase.java

index c214912108900db80abe0dab11af777af7c2d5fe..231578ac6a6cd3734189a4cbb9509dfcc7a1ca44 100644 (file)
@@ -1,6 +1,5 @@
 package org.simantics.acorn;
 
 package org.simantics.acorn;
 
-import java.nio.file.Path;
 import java.util.Properties;
 
 import org.simantics.db.Database;
 import java.util.Properties;
 
 import org.simantics.db.Database;
index e0bcbebf7421906a61cd13ff626719aac0f00ef0..4c8df933777742532fcbf598f386362b81adafb5 100644 (file)
@@ -263,11 +263,11 @@ public class ClusterManager {
                // Nothing has been written => no need to do anything
                long amountOfFiles = countFiles(workingDirectory);
                if(!fullSave && amountOfFiles == 0) {
                // Nothing has been written => no need to do anything
                long amountOfFiles = countFiles(workingDirectory);
                if(!fullSave && amountOfFiles == 0) {
-    //             System.err.println("amountOfFiles < 3");
+                       //LOGGER.info("makeSnapshot: " + amountOfFiles + " files, skipping snapshot");
                    return false;
                }
     
                    return false;
                }
     
-               LOGGER.info("makeSnapshot");
+               LOGGER.info("makeSnapshot: start with " + amountOfFiles + " files");
     
                // Schedule writing of all data to disk
             refreshHeadState();
     
                // Schedule writing of all data to disk
             refreshHeadState();
@@ -287,13 +287,11 @@ public class ClusterManager {
 
                persistHeadState();
 
 
                persistHeadState();
 
-               if (fullSave)
-                   mainState.save(dbFolder);
-    
-               amountOfFiles = countFiles(workingDirectory);
-               
-               LOGGER.info(" -finished: amount of files is {}", amountOfFiles);
-    
+               if (LOGGER.isInfoEnabled()) {
+                       amountOfFiles = countFiles(workingDirectory);
+                       LOGGER.info(" -finished: amount of files is {}", amountOfFiles);
+               }
+
                workingDirectory = dbFolder.resolve(Integer.toString(mainState.headDir));
                if (!Files.exists(workingDirectory)) {
                    Files.createDirectories(workingDirectory);
                workingDirectory = dbFolder.resolve(Integer.toString(mainState.headDir));
                if (!Files.exists(workingDirectory)) {
                    Files.createDirectories(workingDirectory);
index dce477ebeae0fdf35d1db078acfd7e6e885ea825..07fe18e61201cc16fd4e62bd62b808d8d6b5041d 100644 (file)
@@ -192,7 +192,7 @@ public class GraphClientImpl2 implements Database.Session {
                        try {
                            if (!unexpectedClose)
                                makeSnapshot(true);
                        try {
                            if (!unexpectedClose)
                                makeSnapshot(true);
-                               
+
                                mainProgram.close();
                                clusters.shutdown();
                                executor.shutdown();
                                mainProgram.close();
                                clusters.shutdown();
                                executor.shutdown();
@@ -201,7 +201,13 @@ public class GraphClientImpl2 implements Database.Session {
                                boolean saverTerminated = saver.awaitTermination(500, TimeUnit.MILLISECONDS);
                                
                                System.err.println("executorTerminated=" + executorTerminated + ", saverTerminated=" + saverTerminated);
                                boolean saverTerminated = saver.awaitTermination(500, TimeUnit.MILLISECONDS);
                                
                                System.err.println("executorTerminated=" + executorTerminated + ", saverTerminated=" + saverTerminated);
-                               
+
+                               try {
+                                       clusters.mainState.save(dbFolder);
+                               } catch (IOException e) {
+                                       LOGGER.error("Failed to save " + MainState.MAIN_STATE + " file in database folder " + dbFolder);
+                               }
+
                                mainProgram = null;
                                executor = null;
                                saver = null;
                                mainProgram = null;
                                executor = null;
                                saver = null;
index 1025cc6a5dac0a2e6d5b940b1ad0d17ba971fa00..12f0552e434e96638d6bdd7bcd22d440d3d9a674 100644 (file)
@@ -2,14 +2,16 @@ package org.simantics.acorn.internal;
 
 import java.io.File;
 import java.io.IOException;
 
 import java.io.File;
 import java.io.IOException;
-import java.io.RandomAccessFile;
+import java.nio.channels.FileChannel;
 import java.nio.channels.FileLock;
 import java.nio.file.DirectoryStream;
 import java.nio.channels.FileLock;
 import java.nio.file.DirectoryStream;
+import java.nio.file.FileAlreadyExistsException;
 import java.nio.file.FileVisitOption;
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.SimpleFileVisitor;
 import java.nio.file.FileVisitOption;
 import java.nio.file.FileVisitResult;
 import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.SimpleFileVisitor;
+import java.nio.file.StandardOpenOption;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.EnumSet;
 import java.util.Properties;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.EnumSet;
 import java.util.Properties;
@@ -18,24 +20,28 @@ import org.simantics.acorn.GraphClientImpl2;
 import org.simantics.db.Database;
 import org.simantics.db.DatabaseUserAgent;
 import org.simantics.db.ServiceLocator;
 import org.simantics.db.Database;
 import org.simantics.db.DatabaseUserAgent;
 import org.simantics.db.ServiceLocator;
-import org.simantics.db.common.utils.Logger;
-import org.simantics.db.exception.SDBException;
 import org.simantics.db.server.DatabaseStartException;
 import org.simantics.db.server.ProCoreException;
 import org.simantics.db.server.DatabaseStartException;
 import org.simantics.db.server.ProCoreException;
-import org.simantics.db.server.internal.InternalException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 /**
  * @author Tuukka Lehtonen
  */
 public class AcornDatabase implements Database {
 
 
 /**
  * @author Tuukka Lehtonen
  */
 public class AcornDatabase implements Database {
 
+    private static final Logger LOGGER = LoggerFactory.getLogger(AcornDatabase.class);
+
+    private static final String LOCK_FILE_NAME = "lock";
+
     private final Path folder;
     private final Path folder;
-    
+    private final Path lockFile;
+
     private GraphClientImpl2 currentClient;
 
     private DatabaseUserAgent userAgent;
 
     private GraphClientImpl2 currentClient;
 
     private DatabaseUserAgent userAgent;
 
-    private RandomAccessFile raLockFile;
+    private FileChannel lockFileChannel;
 
     private FileLock lock;
 
 
     private FileLock lock;
 
@@ -43,6 +49,7 @@ public class AcornDatabase implements Database {
 
     public AcornDatabase(Path folder) {
         this.folder = folder;
 
     public AcornDatabase(Path folder) {
         this.folder = folder;
+        this.lockFile = folder.resolve(LOCK_FILE_NAME);
     }
 
     @Override
     }
 
     @Override
@@ -90,7 +97,7 @@ public class AcornDatabase implements Database {
         try (DirectoryStream<Path> folderStream = Files.newDirectoryStream(path)) {
             return !folderStream.iterator().hasNext();
         } catch (IOException e) {
         try (DirectoryStream<Path> folderStream = Files.newDirectoryStream(path)) {
             return !folderStream.iterator().hasNext();
         } catch (IOException e) {
-            Logger.defaultLogError("Failed to open folder stream. folder=" + path, e);
+            LOGGER.error("Failed to open folder stream. folder=" + path, e);
             return false;
         }
     }
             return false;
         }
     }
@@ -110,22 +117,25 @@ public class AcornDatabase implements Database {
     }
 
     @Override
     }
 
     @Override
-    public void start() throws ProCoreException {
-        Path lockFile = folder.resolve("lock");
+    public synchronized void start() throws ProCoreException {
         try {
         try {
-            if (!Files.exists(lockFile))
-                Files.createFile(lockFile);
-            
-            raLockFile = new RandomAccessFile(lockFile.toFile(), "rw");
-            lock = raLockFile.getChannel().tryLock();
+            try {
+                lockFileChannel = lockFile.getFileSystem().provider().newFileChannel(lockFile,
+                        EnumSet.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE));
+            } catch (FileAlreadyExistsException e) {
+                throw new ProCoreException("The database in folder " + folder.toAbsolutePath() + " is already in use!", e);
+            }
+
+            lock = lockFileChannel.tryLock();
             if (lock == null) {
             if (lock == null) {
+                safeLoggingClose(lockFileChannel, lockFile);
                 throw new ProCoreException("The database in folder " + folder.toAbsolutePath() + " is already in use!");
             }
                 throw new ProCoreException("The database in folder " + folder.toAbsolutePath() + " is already in use!");
             }
-            
+
             isRunning = true;
             isRunning = true;
-            
         } catch (IOException e) {
         } catch (IOException e) {
-            e.printStackTrace();
+            LOGGER.error("Failed to start database at " + folder.toAbsolutePath(), e);
+            safeLoggingClose(lockFileChannel, lockFile);
         }
     }
 
         }
     }
 
@@ -135,19 +145,19 @@ public class AcornDatabase implements Database {
     }
 
     @Override
     }
 
     @Override
-    public boolean tryToStop() throws ProCoreException {
+    public synchronized boolean tryToStop() throws ProCoreException {
+        if (!isRunning)
+            return false;
         try {
         try {
-            lock.release();
-            raLockFile.close();
-            
-            Files.deleteIfExists(folder.resolve("lock"));
-            
+            safeLoggingClose(lock, lockFile);
+            lock = null;
+            safeLoggingClose(lockFileChannel, lockFile);
+            lockFileChannel = null;
+            Files.deleteIfExists(lockFile);
             isRunning = false;
             isRunning = false;
-            
         } catch (IOException e) {
         } catch (IOException e) {
-            e.printStackTrace();
+            LOGGER.error("Failed to start database at " + folder.toAbsolutePath(), e);
         }
         }
-        
         return true;
     }
 
         return true;
     }
 
@@ -265,4 +275,13 @@ public class AcornDatabase implements Database {
                return "LZ4";
        }
 
                return "LZ4";
        }
 
+    private static void safeLoggingClose(AutoCloseable closeable, Path file) {
+        if (closeable == null)
+            return;
+        try (AutoCloseable c = closeable) {
+        } catch (Exception e) {
+            LOGGER.error("Failed to close " + closeable.getClass() + " of " + file.toAbsolutePath());
+        }
+    }
+
 }
 }