Skip to content

Commit

Permalink
MODE-1224 Improve startup time of Repository
Browse files Browse the repository at this point in the history
Several changes were made to improve the startup time of the ModeShape engine and Repository instances, especially when starting up after the first shutdown.

The JPA connector was improved to eliminate a dual-connection startup mode that attempted to discover the model before establishing the actual connection. This will have a noticeable impact when starting up with 'autoGenerateSchema' properties of 'update' or 'validate', since before this change both connection phases performed the update and/or validation. Now that there is only a single connection phase, the update and/or validation will only be performed once.

This change also improves the process of reading the existing node types during JcrRepository startup. Prior to this change, the existing node types were read from the '/jcr:system' area and the built-ins were always loaded from a CND on the classpath, replacing any "incorrect" or "invalid" built-in node type that was stored in '/jcr:system'. Now, the existing node types are read from the '/jcr:system' area and used as is, without overwriting the built-ins. This was thought to better accept any existing node types configured by the user, and will perhaps come into play when a user upgrades from 2.x to 3.x (assuming there are changes to the built-ins). As a result, users will be responsible for updating the built-in node types using the standard NodeTypeManager.registerNodeTypes(…) methods.

Several automated and manual integration tests were modified before these changes to allow better measurement of the startup times. Some test utility methods (e.g., simulation of Guvnor activities) were refactored so they could be more easily used in multiple (kinds of) tests.

All unit and integration tests pass with these changes, and a full build (e.g., "mvn install -Pintegration") appears to take 1 minute less.
  • Loading branch information
rhauch committed Aug 3, 2011
1 parent 85c41db commit 6524590
Show file tree
Hide file tree
Showing 8 changed files with 1,171 additions and 550 deletions.
Expand Up @@ -1228,66 +1228,58 @@ public synchronized RepositoryConnection getConnection() throws RepositorySource
Context context = new InitialContext();
dataSource = (DataSource)context.lookup(this.dataSourceJndiName);
} catch (Throwable t) {
Logger.getLogger(getClass()).error(t, JpaConnectorI18n.errorFindingDataSourceInJndi, name, dataSourceJndiName);
Logger.getLogger(getClass())
.error(t, JpaConnectorI18n.errorFindingDataSourceInJndi, name, dataSourceJndiName);
}
}

// Now, create another entity manager with the classes from the correct model and without changing the schema...
JpaAdapter jpaAdapter = new HibernateAdapter();
EntityManagerFactory bootstrapFactory = jpaAdapter.getEntityManagerFactory(this);
EntityManagerFactory entityManagerFactory = jpaAdapter.getEntityManagerFactory(this);
entityManagers = new EntityManagers(entityManagerFactory);

// Establish a connection and obtain the store options...
EntityManager entityManager = entityManagers.checkout();
try {
// Establish a connection and obtain the store options...
EntityManager entityManager = bootstrapFactory.createEntityManager();
try {

// Find and update/set the root node's UUID ...
StoreOptions options = new StoreOptions(entityManager);
UUID actualUuid = options.getRootNodeUuid();
if (actualUuid != null) {
this.setRootNodeUuid(actualUuid.toString());
} else {
options.setRootNodeUuid(this.rootUuid);
}
// Determine the dialect, if it was to be automatically discovered ...
if (this.dialect == null || this.dialect.trim().length() == 0) {
this.dialect = jpaAdapter.determineDialect(entityManager);
}
if (this.dialect == null || this.dialect.trim().length() == 0) {
// The dialect could not be determined ...
String msg = JpaConnectorI18n.dialectCouldNotBeDeterminedAndMustBeSpecified.text(name);
throw new RepositorySourceException(msg);
}

// Find or set the type of model that will be used.
String actualModelName = options.getModelName();
if (actualModelName == null) {
// This is a new store, so set to the specified model ...
if (model == null) setModel(Models.DEFAULT.getName());
assert model != null;
options.setModelName(model);
} else {
// Set the model to the what's listed in the database ...
try {
setModel(actualModelName);
} catch (Throwable e) {
// The actual model name doesn't match what's available in the software ...
String msg = JpaConnectorI18n.existingStoreSpecifiesUnknownModel.text(name, actualModelName);
throw new RepositorySourceException(msg);
}
}
// Find and update/set the root node's UUID ...
StoreOptions options = new StoreOptions(entityManager);
UUID actualUuid = options.getRootNodeUuid();
if (actualUuid != null) {
this.setRootNodeUuid(actualUuid.toString());
} else {
options.setRootNodeUuid(this.rootUuid);
}

// Determine the dialect, if it was to be automatically discovered ...
if (this.dialect == null || this.dialect.trim().length() == 0) {
this.dialect = jpaAdapter.determineDialect(entityManager);
}
if (this.dialect == null || this.dialect.trim().length() == 0) {
// The dialect could not be determined ...
String msg = JpaConnectorI18n.dialectCouldNotBeDeterminedAndMustBeSpecified.text(name);
// Find or set the type of model that will be used.
String actualModelName = options.getModelName();
if (actualModelName == null) {
// This is a new store, so set to the specified model ...
if (model == null) setModel(Models.DEFAULT.getName());
assert model != null;
options.setModelName(model);
} else {
// Set the model to the what's listed in the database ...
try {
setModel(actualModelName);
} catch (Throwable e) {
// The actual model name doesn't match what's available in the software ...
String msg = JpaConnectorI18n.existingStoreSpecifiesUnknownModel.text(name, actualModelName);
throw new RepositorySourceException(msg);
}

} finally {
entityManager.close();
}
} finally {
bootstrapFactory.close();
if (entityManager != null) entityManagers.checkin(entityManager);
}

EntityManagerFactory entityManagerFactory = jpaAdapter.getEntityManagerFactory(this);

// Now, create another entity manager with the classes from the correct model and without changing the schema...
entityManagers = new EntityManagers(entityManagerFactory);
}

return model.createConnection(this);
Expand Down
Expand Up @@ -2,6 +2,7 @@

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import javax.jcr.ImportUUIDBehavior;
import javax.jcr.NamespaceException;
import javax.jcr.NamespaceRegistry;
import javax.jcr.PropertyType;
Expand All @@ -19,6 +20,9 @@
import org.modeshape.common.util.FileUtil;
import org.modeshape.jcr.JcrConfiguration;
import org.modeshape.jcr.JcrEngine;
import org.modeshape.jcr.JcrTools;
import org.modeshape.test.ModeShapeUnitTest;
import org.modeshape.test.integration.performance.GuvnorEmulator;
import org.xml.sax.SAXException;

/**
Expand All @@ -30,7 +34,7 @@
* <li>Change the value of the "FIRST_TIME" constant to "false" and recompile.</li>
* <li>Run this application any number of times.</li>
*/
public class ReadNodeTypesUponRestart {
public class ReadNodeTypesUponRestart extends ModeShapeUnitTest {

private static final boolean FIRST_TIME = false;
private static final String CONFIG_FILE_PATH = "src/test/resources/config/read-node-types-upon-restart-config.xml";
Expand All @@ -53,7 +57,21 @@ public static void main( String[] args ) {
getOrCreateNodeType(workspace);
// getNodeType(workspace);
// session.save();
} catch (RepositoryException e) {

GuvnorEmulator guvnor = new GuvnorEmulator(getSession().getRepository(), 150, true);
guvnor.importGuvnorNodeTypes(!FIRST_TIME);

if (FIRST_TIME) {
System.out.print("Importing Guvnor content ... ");
JcrTools tools = new JcrTools();
int importBehavior = ImportUUIDBehavior.IMPORT_UUID_COLLISION_REPLACE_EXISTING;
tools.importContent(session, "io/drools/mortgage-sample-repository.xml", importBehavior);
} else {
System.out.print("Verifying Guvnor content ... ");
guvnor.verifyContent();
}
System.out.println("completed");
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.print("exception" + e);
}
Expand Down Expand Up @@ -82,7 +100,10 @@ public static void main( String[] args ) {
getNamespace(workspace, "jboss", "http://www.jboss.org");
getNodeType(workspace);
// session.save();
} catch (RepositoryException e) {

GuvnorEmulator guvnor = new GuvnorEmulator(getSession().getRepository(), 150, true);
guvnor.importGuvnorNodeTypes(true);
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.print("exception" + e);
}
Expand All @@ -97,8 +118,11 @@ private static void shutdownEngine() {

// Shutdown the engine ...
try {
System.out.print("Shutting down ModeShape engine ... ");
long nanos = System.nanoTime();
engine.shutdownAndAwaitTermination(5, TimeUnit.SECONDS);
System.out.println("Successfully shut down ModeShape engine");
nanos = System.nanoTime() - nanos;
System.out.println("completed in " + TimeUnit.MILLISECONDS.convert(nanos, TimeUnit.NANOSECONDS) + " millis");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
Expand All @@ -110,6 +134,12 @@ private static void shutdownEngine() {
private static void startEngine( boolean firstTime ) {
if (engine != null) return; // already started

if (firstTime) {
System.out.print("Starting ModeShape engine ... ");
} else {
System.out.println("");
System.out.print("Restarting ModeShape engine ... ");
}
try {
JcrConfiguration config = new JcrConfiguration().loadFrom(CONFIG_FILE_PATH);

Expand All @@ -120,20 +150,38 @@ private static void startEngine( boolean firstTime ) {
}

engine = config.build();
engine.start();
long nanos = System.nanoTime();
engine.start(true);
nanos = System.nanoTime() - nanos;

if (engine.getProblems().hasProblems()) {
System.out.println("resulted in problems:");
for (Problem problem : engine.getProblems()) {
System.err.println(problem.getMessageString());
}
throw new RuntimeException("Could not start due to problems");
}
System.out.println("completed in " + TimeUnit.MILLISECONDS.convert(nanos, TimeUnit.NANOSECONDS) + " millis");
} catch (IOException e) {
System.out.print("exception" + e);
} catch (SAXException e) {
// TODO Auto-generated catch block
System.out.print("exception" + e);
}

// Get a session to make sure everything is initialized ...
Session session = null;
try {
System.out.print("Getting initial session ... ");
long nanos = System.nanoTime();
session = getSession();
nanos = System.nanoTime() - nanos;
System.out.println("completed in " + TimeUnit.MILLISECONDS.convert(nanos, TimeUnit.NANOSECONDS) + " millis");
} catch (RepositoryException e) {
System.out.print("exception" + e);
} finally {
if (session != null) session.logout();
}
}

private static Session getSession() throws RepositoryException {
Expand Down

0 comments on commit 6524590

Please sign in to comment.