Improved logic in new head state creation. 97/497/2
authorJussi Koskela <jussi.koskela@semantum.fi>
Tue, 9 May 2017 13:12:17 +0000 (16:12 +0300)
committerTuukka Lehtonen <tuukka.lehtonen@semantum.fi>
Tue, 9 May 2017 14:37:04 +0000 (17:37 +0300)
Earlier any IOException during the reading of head state was interpreted
as empty DB. This might cause unwanted DB reset. It's better to identify
need for empty head state based on main state head directory.

Switched AcornDatabase.start logic back to using RandomAccessFile for
touching the db/lock file. Using RandomAccessFile instead of
FileSystemProvider.newFileChannel in Windows better prevents any other
process from removing the lock file. The newFileChannel version did not
prevent the user from initially running 'del lock' to remove the file -
although the file will be recreated quickly by the system.

Also AcornDatabase.start now re-throws ProCoreException if
opening/locking the lock-file fails with IOException to prevent the
system from attempting to start up without a proper database to work
with. Previously the system just logged the start-up problem and
continued.

refs #7124

Change-Id: I850b47d8f692e3d1b8ce177b9269540edc4dc272

bundles/org.simantics.acorn/src/org/simantics/acorn/ClusterManager.java
bundles/org.simantics.acorn/src/org/simantics/acorn/HeadState.java
bundles/org.simantics.acorn/src/org/simantics/acorn/MainState.java
bundles/org.simantics.acorn/src/org/simantics/acorn/internal/AcornDatabase.java

index 4c8df933777742532fcbf598f386362b81adafb5..9725ffe8da2df0aabb169b301b955642781205dd 100644 (file)
@@ -406,35 +406,39 @@ public class ClusterManager {
                lastSessionDirectory = dbFolder.resolve(Integer.toString(mainState.headDir - 1));
                
                // Head State
-               try {
-            state = HeadState.load(lastSessionDirectory);
-        } catch (InvalidHeadStateException e) {
-            // For backwards compatibility only!
-            Throwable cause = e.getCause();
-            if (cause instanceof Throwable) {
-                try {
-                    org.simantics.db.javacore.HeadState oldState = org.simantics.db.javacore.HeadState.load(lastSessionDirectory);
-                    
-                    HeadState newState = new HeadState();
-                    newState.clusters = oldState.clusters;
-                    newState.cs = oldState.cs;
-                    newState.files = oldState.files;
-                    newState.stream = oldState.stream;
-                    newState.headChangeSetId = oldState.headChangeSetId;
-                    newState.reservedIds = oldState.reservedIds;
-                    newState.transactionId = oldState.transactionId;
-                    state = newState;
-                } catch (InvalidHeadStateException e1) {
-                    throw new IOException("Could not load HeadState due to corruption", e1);
-                }
-            } else {
-                // This should never happen as MainState.load() checks the integrity
-                // of head.state files and rolls back in cases of corruption until a
-                // consistent state is found (could be case 0 - initial db state)
-                // IF this does happen something is completely wrong
-                throw new IOException("Could not load HeadState due to corruption", e);
-            }
-        }
+               if (mainState.isInitial()) {
+                       state = new HeadState();
+               } else {
+                       try {
+                   state = HeadState.load(lastSessionDirectory);
+               } catch (InvalidHeadStateException e) {
+                   // For backwards compatibility only!
+                   Throwable cause = e.getCause();
+                   if (cause instanceof Throwable) {
+                       try {
+                           org.simantics.db.javacore.HeadState oldState = org.simantics.db.javacore.HeadState.load(lastSessionDirectory);
+                           
+                           HeadState newState = new HeadState();
+                           newState.clusters = oldState.clusters;
+                           newState.cs = oldState.cs;
+                           newState.files = oldState.files;
+                           newState.stream = oldState.stream;
+                           newState.headChangeSetId = oldState.headChangeSetId;
+                           newState.reservedIds = oldState.reservedIds;
+                           newState.transactionId = oldState.transactionId;
+                           state = newState;
+                       } catch (InvalidHeadStateException e1) {
+                           throw new IOException("Could not load HeadState due to corruption", e1);
+                       }
+                   } else {
+                       // This should never happen as MainState.load() checks the integrity
+                       // of head.state files and rolls back in cases of corruption until a
+                       // consistent state is found (could be case 0 - initial db state)
+                       // IF this does happen something is completely wrong
+                       throw new IOException("Could not load HeadState due to corruption", e);
+                   }
+               }
+               }
                try {
                workingDirectory = dbFolder.resolve(Integer.toString(mainState.headDir));
                Files.createDirectories(workingDirectory);
index a6a1622c80027d68b11b05a7de6b6d1891bb26f5..ea54a418578c13dd335e6054ae547e51c4424092 100644 (file)
@@ -62,8 +62,7 @@ public class HeadState {
                        HeadState1 old = HeadState1.load(directory);
                        return old.migrate();
                }
-            return new HeadState();
-//            throw new InvalidHeadStateException(i);
+            throw new InvalidHeadStateException(i);
         } catch (NoSuchAlgorithmException e) {
             throw new Error("SHA-1 Algorithm not found", e);
         } catch (Throwable t) {
index ebc4079d51c64ccf1c2b40e372b89c3602e399cf..f5644c2aeda95b479ffab6f2331dc8c775a88f0e 100644 (file)
@@ -38,6 +38,10 @@ public class MainState implements Serializable {
     private MainState(int headDir) {
         this.headDir = headDir;
     }
+    
+    public boolean isInitial() {
+       return this.headDir == 0;
+    }
 
     public static MainState load(Path directory, Runnable rollbackCallback) throws IOException {
         Files.createDirectories(directory);
index db83f1395d005babeb09dbd500f5034d2326f503..a423fbdbe23b815871688fb17cb2c6fb165550f7 100644 (file)
@@ -2,16 +2,14 @@ package org.simantics.acorn.internal;
 
 import java.io.File;
 import java.io.IOException;
-import java.nio.channels.FileChannel;
+import java.io.RandomAccessFile;
 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.StandardOpenOption;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.util.EnumSet;
 import java.util.Properties;
@@ -41,7 +39,7 @@ public class AcornDatabase implements Database {
 
     private DatabaseUserAgent userAgent;
 
-    private FileChannel lockFileChannel;
+    private RandomAccessFile raLockFile;
 
     private FileLock lock;
 
@@ -119,19 +117,17 @@ public class AcornDatabase implements Database {
     @Override
     public synchronized void start() throws ProCoreException {
         try {
-            lockFileChannel = lockFile.getFileSystem().provider().newFileChannel(lockFile,
-                    EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE));
-
-            lock = lockFileChannel.tryLock();
+            raLockFile = new RandomAccessFile(lockFile.toFile(), "rw");
+            lock = raLockFile.getChannel().tryLock();
             if (lock == null) {
-                safeLoggingClose(lockFileChannel, lockFile);
+                safeLoggingClose(raLockFile, lockFile);
                 throw new ProCoreException("The database in folder " + folder.toAbsolutePath() + " is already in use!");
             }
-
             isRunning = true;
         } catch (IOException e) {
             LOGGER.error("Failed to start database at " + folder.toAbsolutePath(), e);
-            safeLoggingClose(lockFileChannel, lockFile);
+            safeLoggingClose(raLockFile, lockFile);
+            throw new ProCoreException("Failed to start database at " + folder.toAbsolutePath(), e);
         }
     }
 
@@ -147,8 +143,8 @@ public class AcornDatabase implements Database {
         try {
             safeLoggingClose(lock, lockFile);
             lock = null;
-            safeLoggingClose(lockFileChannel, lockFile);
-            lockFileChannel = null;
+            safeLoggingClose(raLockFile, lockFile);
+            raLockFile = null;
             Files.deleteIfExists(lockFile);
             isRunning = false;
         } catch (IOException e) {