From: Antti Villberg Date: Thu, 11 May 2017 13:19:58 +0000 (+0300) Subject: Support using a DB baseline for faster program startup. X-Git-Tag: v1.31.0~132 X-Git-Url: https://gerrit.simantics.org/r/gitweb?p=simantics%2Fplatform.git;a=commitdiff_plain;h=34c2ce4fc18f58ca00275b9296f7a449d3395ced Support using a DB baseline for faster program startup. Use by specifying the JVM argument -Dorg.simantics.db.baseline= where the baseline zip file must contain two directories: * db/ which contains the initial database version * .metadata/.plugins/org.simantics.db.indexing which contains the initial database index During baseline initialization, the specified zip file is extracted directly at the root of the workspace directory. The baseline file is hard-linked/copied under the workspace root as a file called `.baseline`. If this file exists, it indicates that the workspace has already been initialized from a baseline archive and should not be initialized/updated anymore. Limitations: * Automatic ontology updates or installation of new ontology plugins is disabled when a database baseline is in use. refs #7211 Change-Id: Id67156c65047b429af71e0eab83b31a5d2fd5117 --- diff --git a/bundles/org.simantics.utils/src/org/simantics/utils/FileUtils.java b/bundles/org.simantics.utils/src/org/simantics/utils/FileUtils.java index 4e68f29c4..798fd02b8 100644 --- a/bundles/org.simantics.utils/src/org/simantics/utils/FileUtils.java +++ b/bundles/org.simantics.utils/src/org/simantics/utils/FileUtils.java @@ -840,8 +840,8 @@ public class FileUtils { * @throws IOException */ public static void extractZip(File zipFile, File dst) throws IOException { - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Extracting zip "+zipFile); + if (LOGGER.isTraceEnabled()) + LOGGER.trace("Extracting zip "+zipFile); try (FileInputStream fis = new FileInputStream(zipFile)) { extractZip(fis, dst); } @@ -863,8 +863,8 @@ public class FileUtils { while (entry != null) { // for each entry to be extracted String name = entry.getName(); - if (LOGGER.isDebugEnabled()) - LOGGER.debug("Extracting "+name); + if (LOGGER.isTraceEnabled()) + LOGGER.trace("Extracting "+name); File file = new File(dst, name); if (entry.isDirectory()) diff --git a/bundles/org.simantics/src/org/simantics/SimanticsPlatform.java b/bundles/org.simantics/src/org/simantics/SimanticsPlatform.java index faee6944d..961dfc718 100644 --- a/bundles/org.simantics/src/org/simantics/SimanticsPlatform.java +++ b/bundles/org.simantics/src/org/simantics/SimanticsPlatform.java @@ -21,6 +21,10 @@ import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.time.Instant; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -31,6 +35,8 @@ import java.util.Properties; import java.util.Set; import java.util.TreeMap; import java.util.UUID; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; import org.eclipse.core.runtime.ILog; import org.eclipse.core.runtime.IProduct; @@ -43,6 +49,8 @@ import org.eclipse.core.runtime.SubMonitor; import org.eclipse.osgi.service.resolver.BundleDescription; import org.ini4j.Ini; import org.ini4j.InvalidFileFormatException; +import org.simantics.SimanticsPlatform.OntologyRecoveryPolicy; +import org.simantics.SimanticsPlatform.RecoveryPolicy; import org.simantics.databoard.Bindings; import org.simantics.databoard.Databoard; import org.simantics.datatypes.literal.Font; @@ -705,6 +713,75 @@ public class SimanticsPlatform implements LifecycleListener { resetDatabase(monitor); } + public boolean handleBaselineDatabase() throws PlatformException { + Path workspaceLocation = Platform.getLocation().toFile().toPath(); + Path baselineIndicatorFile = workspaceLocation.resolve(".baselined"); + if (Files.isRegularFile(baselineIndicatorFile)) { + // This means that the workspace has already been initialized from + // a database baseline and further initialization is not necessary. + return true; + } + + String dbBaselineArchive = System.getProperty("org.simantics.db.baseline", null); + if (dbBaselineArchive == null) + return false; + + Path baseline = Paths.get(dbBaselineArchive); + if (!Files.isRegularFile(baseline)) + throw new PlatformException("Specified database baseline archive " + baseline + " does not exist. Cannot initialize workspace database."); + + validateBaselineFile(baseline); + validateWorkspaceForBaselineInitialization(workspaceLocation); + + try { + Files.createDirectories(workspaceLocation); + FileUtils.extractZip(baseline.toFile(), workspaceLocation.toFile()); + Files.write(baselineIndicatorFile, baselineIndicatorContents(baselineIndicatorFile)); + return true; + } catch (IOException e) { + throw new PlatformException(e); + } + } + + private static final DateTimeFormatter TIMESTAMP_FORMAT = DateTimeFormatter.ofPattern("d. MMM yyyy HH:mm:ss"); + + private static byte[] baselineIndicatorContents(Path path) throws IOException { + return String.format("%s%n%s%n", + path.toString(), + Instant.now().atZone(ZoneId.systemDefault()).format(TIMESTAMP_FORMAT)) + .getBytes("UTF-8"); + } + + private void validateWorkspaceForBaselineInitialization(Path workspaceLocation) throws PlatformException { + try { + Path db = workspaceLocation.resolve("db"); + if (Files.exists(db)) + throw new PlatformException("Database location " + db + " already exists. Cannot re-initialize workspace from baseline."); + Path index = workspaceLocation.resolve(".metadata/.plugins/org.simantics.db.indexing"); + if (!Files.exists(index) || !isEmptyDirectory(index)) + throw new PlatformException("Index location " + index + " already exists. Cannot re-initialize workspace from baseline."); + } catch (IOException e) { + throw new PlatformException("Failed to validate workspace for baseline initialization", e); + } + } + + private static boolean isEmptyDirectory(Path dir) throws IOException { + return Files.walk(dir).count() == 1; + } + + private void validateBaselineFile(Path baseline) throws PlatformException { + try (ZipFile zip = new ZipFile(baseline.toFile())) { + ZipEntry db = zip.getEntry("db"); + if (db == null) + throw new PlatformException("Baseline archive does not contain database directory 'db'"); + ZipEntry index = zip.getEntry(".metadata/.plugins/org.simantics.db.indexing"); + if (index == null) + throw new PlatformException("Baseline archive does not contain database index directory '.metadata/.plugins/org.simantics.db.indexing'"); + } catch (IOException e) { + throw new PlatformException("Failed to validate baseline archive " + baseline, e); + } + } + /** * Start-up the platform. The procedure consists of 8 steps. Once everything * is up and running, all fields are set property. @@ -757,6 +834,9 @@ public class SimanticsPlatform implements LifecycleListener { // 0.2 Clear VariableRepository.repository static map which holds references to SessionImplDb VariableRepository.clear(); + // 0.3 Handle baseline database before opening db + boolean usingBaseline = handleBaselineDatabase(); + // 1. Assert there is a database at /db session = setupDatabase(databaseDriverId, progressMonitor, workspacePolicy, userAgent); TimeLogger.log("Database setup complete"); @@ -772,8 +852,10 @@ public class SimanticsPlatform implements LifecycleListener { } // 2. Assert all graphs, and correct versions, are installed to the database - synchronizeOntologies(progressMonitor, ontologyPolicy, requireSynchronize); - TimeLogger.log("Synchronized ontologies"); + if(!usingBaseline) { + synchronizeOntologies(progressMonitor, ontologyPolicy, requireSynchronize); + TimeLogger.log("Synchronized ontologies"); + } // 4. Assert simantics.cfg exists boolean installProject = assertConfiguration(progressMonitor,workspacePolicy); @@ -862,8 +944,11 @@ public class SimanticsPlatform implements LifecycleListener { try { // Construct and initialize SessionContext from Session. SessionContext sessionContext = SessionContext.create(session, init); - if (init) + TimeLogger.log("Session context created"); + if (init) { sessionContext.registerServices(); + TimeLogger.log("Session services registered"); + } return sessionContext; } catch (DatabaseException e) { throw new PlatformException(e);