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
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;
// 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");
- 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();
- 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);
try {
if (!unexpectedClose)
makeSnapshot(true);
try {
if (!unexpectedClose)
makeSnapshot(true);
mainProgram.close();
clusters.shutdown();
executor.shutdown();
mainProgram.close();
clusters.shutdown();
executor.shutdown();
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;
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;
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;
public AcornDatabase(Path folder) {
this.folder = folder;
public AcornDatabase(Path folder) {
this.folder = folder;
+ this.lockFile = folder.resolve(LOCK_FILE_NAME);
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);
- public void start() throws ProCoreException {
- Path lockFile = folder.resolve("lock");
+ public synchronized void start() throws ProCoreException {
- 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();
+ 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!");
}
} catch (IOException e) {
} catch (IOException e) {
+ LOGGER.error("Failed to start database at " + folder.toAbsolutePath(), e);
+ safeLoggingClose(lockFileChannel, lockFile);
- public boolean tryToStop() throws ProCoreException {
+ public synchronized boolean tryToStop() throws ProCoreException {
+ if (!isRunning)
+ return false;
- lock.release();
- raLockFile.close();
-
- Files.deleteIfExists(folder.resolve("lock"));
-
+ safeLoggingClose(lock, lockFile);
+ lock = null;
+ safeLoggingClose(lockFileChannel, lockFile);
+ lockFileChannel = null;
+ Files.deleteIfExists(lockFile);
} catch (IOException e) {
} catch (IOException e) {
+ LOGGER.error("Failed to start database at " + folder.toAbsolutePath(), e);
+ 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());
+ }
+ }
+