From: Tuukka Lehtonen Date: Fri, 5 May 2017 13:59:12 +0000 (+0300) Subject: Fixed bad bug related to AcornBackupProvider X-Git-Tag: v1.29.0~71 X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=commitdiff_plain;h=2aa77c56ec691aad39d1909bfbe4aff101e988fb Fixed bad bug related to AcornBackupProvider 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 --- diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornManagement.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornManagement.java index c21491210..231578ac6 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornManagement.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/AcornManagement.java @@ -1,6 +1,5 @@ package org.simantics.acorn; -import java.nio.file.Path; import java.util.Properties; import org.simantics.db.Database; diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/ClusterManager.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/ClusterManager.java index e0bcbebf7..4c8df9337 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/ClusterManager.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/ClusterManager.java @@ -263,11 +263,11 @@ public class ClusterManager { // 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; } - LOGGER.info("makeSnapshot"); + LOGGER.info("makeSnapshot: start with " + amountOfFiles + " files"); // Schedule writing of all data to disk refreshHeadState(); @@ -287,13 +287,11 @@ public class ClusterManager { 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); diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/GraphClientImpl2.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/GraphClientImpl2.java index dce477ebe..07fe18e61 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/GraphClientImpl2.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/GraphClientImpl2.java @@ -192,7 +192,7 @@ public class GraphClientImpl2 implements Database.Session { try { if (!unexpectedClose) makeSnapshot(true); - + 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); - + + 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; diff --git a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/AcornDatabase.java b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/AcornDatabase.java index 1025cc6a5..12f0552e4 100644 --- a/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/AcornDatabase.java +++ b/bundles/org.simantics.acorn/src/org/simantics/acorn/internal/AcornDatabase.java @@ -2,14 +2,16 @@ package org.simantics.acorn.internal; 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.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; @@ -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.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.internal.InternalException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @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 lockFile; + private GraphClientImpl2 currentClient; private DatabaseUserAgent userAgent; - private RandomAccessFile raLockFile; + private FileChannel lockFileChannel; private FileLock lock; @@ -43,6 +49,7 @@ public class AcornDatabase implements Database { public AcornDatabase(Path folder) { this.folder = folder; + this.lockFile = folder.resolve(LOCK_FILE_NAME); } @Override @@ -90,7 +97,7 @@ public class AcornDatabase implements Database { try (DirectoryStream 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; } } @@ -110,22 +117,25 @@ public class AcornDatabase implements Database { } @Override - public void start() throws ProCoreException { - Path lockFile = folder.resolve("lock"); + public synchronized void start() throws ProCoreException { 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) { + safeLoggingClose(lockFileChannel, lockFile); throw new ProCoreException("The database in folder " + folder.toAbsolutePath() + " is already in use!"); } - + isRunning = true; - } 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 - public boolean tryToStop() throws ProCoreException { + public synchronized boolean tryToStop() throws ProCoreException { + if (!isRunning) + return false; 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; - } catch (IOException e) { - e.printStackTrace(); + LOGGER.error("Failed to start database at " + folder.toAbsolutePath(), e); } - return true; } @@ -265,4 +275,13 @@ public class AcornDatabase implements Database { 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()); + } + } + }