Author: markt Date: Wed Nov 17 12:59:06 2010 New Revision: 1036019 URL: http://svn.apache.org/viewvc?rev=1036019&view=rev Log: Session manager performance Focused on Windows. Use a queue of Random's to generate session ID to remove sync bottleneck on random.nextBytes(bytes) Timings suggest some bottlenecks still present in default Windows code path
Modified: tomcat/trunk/java/org/apache/catalina/ha/session/BackupManager.java tomcat/trunk/java/org/apache/catalina/ha/session/DeltaManager.java tomcat/trunk/java/org/apache/catalina/session/LocalStrings.properties tomcat/trunk/java/org/apache/catalina/session/LocalStrings_es.properties tomcat/trunk/java/org/apache/catalina/session/LocalStrings_fr.properties tomcat/trunk/java/org/apache/catalina/session/LocalStrings_ja.properties tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java tomcat/trunk/java/org/apache/catalina/session/StandardManager.java tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java Modified: tomcat/trunk/java/org/apache/catalina/ha/session/BackupManager.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/ha/session/BackupManager.java?rev=1036019&r1=1036018&r2=1036019&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/ha/session/BackupManager.java (original) +++ tomcat/trunk/java/org/apache/catalina/ha/session/BackupManager.java Wed Nov 17 12:59:06 2010 @@ -203,7 +203,7 @@ public class BackupManager extends Clust } cluster.removeManager(this); - this.random = null; + this.randoms.clear(); } @Override Modified: tomcat/trunk/java/org/apache/catalina/ha/session/DeltaManager.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/ha/session/DeltaManager.java?rev=1036019&r1=1036018&r2=1036019&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/ha/session/DeltaManager.java (original) +++ tomcat/trunk/java/org/apache/catalina/ha/session/DeltaManager.java Wed Nov 17 12:59:06 2010 @@ -960,7 +960,7 @@ public CatalinaCluster getCluster() { } // Require a new random number generator if we are restarted - this.random = null; + this.randoms.clear(); getCluster().removeManager(this); replicationValve = null; } Modified: tomcat/trunk/java/org/apache/catalina/session/LocalStrings.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/LocalStrings.properties?rev=1036019&r1=1036018&r2=1036019&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/session/LocalStrings.properties (original) +++ tomcat/trunk/java/org/apache/catalina/session/LocalStrings.properties Wed Nov 17 12:59:06 2010 @@ -27,12 +27,13 @@ JDBCStore.checkConnectionDBClosed=The da JDBCStore.checkConnectionDBReOpenFail=The re-open on the database failed. The database could be down. JDBCStore.checkConnectionSQLException=A SQL exception occurred {0} JDBCStore.checkConnectionClassNotFoundException=JDBC driver class not found {0} -managerBase.complete=Seeding of random number generator has been completed +managerBase.createRandom=Created random number generator for session ID generation in {0}ms. +managerBase.createRandomSeed=Created SecureRandom instance to seed random number generators for session ID generation in {0}ms. managerBase.createSession.ise=createSession: Too many active sessions managerBase.getting=Getting message digest component for algorithm {0} managerBase.gotten=Completed getting message digest component -managerBase.random=Exception initializing random number generator of class {0} -managerBase.seeding=Seeding random number generator class {0} +managerBase.random=Exception initializing random number generator of class {0}. Falling back to java.util.Random. +managerBase.seedFailed=Failed to seed random number generator class {0} managerBase.sessionTimeout=Invalid session timeout setting {0} serverSession.value.iae=null value standardManager.expireException=processsExpire: Exception during session expiration Modified: tomcat/trunk/java/org/apache/catalina/session/LocalStrings_es.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/LocalStrings_es.properties?rev=1036019&r1=1036018&r2=1036019&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/session/LocalStrings_es.properties (original) +++ tomcat/trunk/java/org/apache/catalina/session/LocalStrings_es.properties Wed Nov 17 12:59:06 2010 @@ -26,12 +26,10 @@ JDBCStore.checkConnectionDBClosed = La c JDBCStore.checkConnectionDBReOpenFail = Fall\u00F3 la reapertura de la base de datos. Puede que la base de datos est\u00E9 ca\u00EDda. JDBCStore.checkConnectionSQLException = Ha tenido lugar una excepci\u00F3n SQL {0} JDBCStore.checkConnectionClassNotFoundException = No se ha hallado la clase del manejador (driver) JDBC {0} -managerBase.complete = Se ha completado la siembra del generador de n\u00FAmeros aleatorios managerBase.createSession.ise = createSession\: Demasiadas sesiones activas managerBase.getting = Obteniendo mensaje de componente de resumen (digest) para algoritmo {0} managerBase.gotten = Completada la obtenci\u00F3n de mensaje de componente de resumen (digest) managerBase.random = Excepci\u00F3n inicializando generador de n\u00FAmeros aleatorios de clase {0} -managerBase.seeding = Sembrando clase de generador de n\u00FAmeros aleatorios {0} managerBase.sessionTimeout = Valor inv\u00E1lido de Tiempo Agotado de sesi\u00F3n {0} serverSession.value.iae = valor nulo standardManager.expireException = processsExpire\: Excepci\u00F3n durante la expiraci\u00F3n de sesi\u00F3n Modified: tomcat/trunk/java/org/apache/catalina/session/LocalStrings_fr.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/LocalStrings_fr.properties?rev=1036019&r1=1036018&r2=1036019&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/session/LocalStrings_fr.properties (original) +++ tomcat/trunk/java/org/apache/catalina/session/LocalStrings_fr.properties Wed Nov 17 12:59:06 2010 @@ -26,12 +26,10 @@ JDBCStore.checkConnectionDBClosed=La con JDBCStore.checkConnectionDBReOpenFail=La tentative de r\u00e9ouverture de la base de donn\u00e9es a \u00e9chou\u00e9. La base de donn\u00e9es est peut-\u00eatre arr\u00eat\u00e9e. JDBCStore.checkConnectionSQLException=Une exception SQL s''est produite {0} JDBCStore.checkConnectionClassNotFoundException=La classe du driver JDBC n''a pas \u00e9t\u00e9 trouv\u00e9e {0} -managerBase.complete=L''alimentation du g\u00e9n\u00e9rateur de nombre al\u00e9atoire est termin\u00e9 managerBase.createSession.ise="createSession": Trop de sessions actives managerBase.getting=Prise du composant d''algorithme empreinte de message (message digest) pour l''algorithme {0} managerBase.gotten=Prise du composant d''algorithme empreinte de message (message digest) termin\u00e9e managerBase.random=Exception durant l''initialisation de la classe du g\u00e9n\u00e9rateur de nombre al\u00e9atoire {0} -managerBase.seeding=Alimentation de la classe du g\u00e9n\u00e9rateur de nombre al\u00e9atoire {0} managerBase.sessionTimeout=R\u00e9glage du d\u00e9lai d''inactivit\u00e9 (timeout) de session invalide {0} serverSession.value.iae=valeur nulle standardManager.expireException="processsExpire": Exception lors de l''expiration de la session Modified: tomcat/trunk/java/org/apache/catalina/session/LocalStrings_ja.properties URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/LocalStrings_ja.properties?rev=1036019&r1=1036018&r2=1036019&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/session/LocalStrings_ja.properties (original) +++ tomcat/trunk/java/org/apache/catalina/session/LocalStrings_ja.properties Wed Nov 17 12:59:06 2010 @@ -27,12 +27,10 @@ JDBCStore.checkConnectionDBClosed=\u30c7 JDBCStore.checkConnectionDBReOpenFail=\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u306e\u518d\u30aa\u30fc\u30d7\u30f3\u304c\u5931\u6557\u3057\u307e\u3057\u305f\u3002\u30c7\u30fc\u30bf\u30d9\u30fc\u30b9\u304c\u30c0\u30a6\u30f3\u3057\u3066\u3044\u308b\u304b\u3082\u3057\u308c\u307e\u305b\u3093\u3002 JDBCStore.checkConnectionSQLException=SQL\u4f8b\u5916\u304c\u767a\u751f\u3057\u307e\u3057\u305f {0} JDBCStore.checkConnectionClassNotFoundException=JDBC\u30c9\u30e9\u30a4\u30d0\u30af\u30e9\u30b9\u304c\u898b\u3064\u304b\u308a\u307e\u305b\u3093 {0} -managerBase.complete=\u4e71\u6570\u767a\u751f\u5668\u306e\u30b7\u30fc\u30c9\u306e\u751f\u6210\u304c\u5b8c\u4e86\u3057\u307e\u3057\u305f managerBase.createSession.ise=createSession: \u30a2\u30af\u30c6\u30a3\u30d6\u30bb\u30c3\u30b7\u30e7\u30f3\u304c\u591a\u3059\u304e\u307e\u3059 managerBase.getting=\u30a2\u30eb\u30b4\u30ea\u30ba\u30e0 {0} \u306e\u30e1\u30c3\u30bb\u30fc\u30b8\u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u3092\u53d6\u5f97\u3057\u307e\u3059 managerBase.gotten=\u30e1\u30c3\u30bb\u30fc\u30b8\u30c0\u30a4\u30b8\u30a7\u30b9\u30c8\u30b3\u30f3\u30dd\u30fc\u30cd\u30f3\u30c8\u306e\u53d6\u5f97\u3092\u5b8c\u4e86\u3057\u307e\u3057\u305f managerBase.random=\u30af\u30e9\u30b9 {0} \u306e\u4e71\u6570\u767a\u751f\u5668\u306e\u521d\u671f\u5316\u306e\u4f8b\u5916\u3067\u3059 -managerBase.seeding=\u4e71\u6570\u767a\u751f\u5668\u30af\u30e9\u30b9 {0} \u306e\u30b7\u30fc\u30c9\u3092\u751f\u6210\u3057\u3066\u3044\u307e\u3059 managerBase.sessionTimeout=\u7121\u52b9\u306a\u30bb\u30c3\u30b7\u30e7\u30f3\u30bf\u30a4\u30e0\u30a2\u30a6\u30c8\u8a2d\u5b9a\u3067\u3059 {0} serverSession.value.iae=null\u5024\u3067\u3059 standardManager.expireException=processsExpire: \u30bb\u30c3\u30b7\u30e7\u30f3\u306e\u7d42\u4e86\u51e6\u7406\u4e2d\u306e\u4f8b\u5916\u3067\u3059 Modified: tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java?rev=1036019&r1=1036018&r2=1036019&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java (original) +++ tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java Wed Nov 17 12:59:06 2010 @@ -22,6 +22,8 @@ package org.apache.catalina.session; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; @@ -31,6 +33,7 @@ import java.security.AccessController; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.PrivilegedAction; +import java.security.SecureRandom; import java.util.Date; import java.util.Enumeration; import java.util.HashMap; @@ -99,8 +102,11 @@ public abstract class ManagerBase extend /** - * Return the MessageDigest implementation to be used when - * creating session identifiers. + * Queue of MessageDigest objects to be used when creating session + * identifiers. If the queue is empty when a MessageDigest is required, a + * new MessageDigest object is created. This is designed this way since + * MessageDigest objects are not thread-safe and sync'ing on a single object + * is slow(er). */ protected Queue<MessageDigest> digests = new ConcurrentLinkedQueue<MessageDigest>(); @@ -147,14 +153,23 @@ public abstract class ManagerBase extend /** - * A random number generator to use when generating session identifiers. + * Queue of random number generator objects to be used when creating session + * identifiers. If the queue is empty when a random number generator is + * required, a new random number generator object is created. This is + * designed this way since random number generator use a sync to make them + * thread-safe and the sync makes using a a single object slow(er). */ - protected volatile Random random = null; + protected Queue<Random> randoms = new ConcurrentLinkedQueue<Random>(); + /** + * Random number generator used to see @{link {...@link #randoms}. + */ + protected SecureRandom randomSeed = null; /** * The Java class name of the random number generator class to be used - * when generating session identifiers. + * when generating session identifiers. The random number generator(s) will + * always be seeded from a SecureRandom instance. */ protected String randomClass = "java.security.SecureRandom"; @@ -597,45 +612,78 @@ public abstract class ManagerBase extend } /** - * Return the random number generator instance we should use for - * generating session identifiers. If there is no such generator - * currently defined, construct and seed a new one. + * Create a new random number generator instance we should use for + * generating session identifiers. */ - public Random getRandom() { - if (this.random == null) { - synchronized (this) { - if (this.random == null) { - // Calculate the new random number generator seed - long seed = System.currentTimeMillis(); - long t1 = seed; - char entropy[] = getEntropy().toCharArray(); - for (int i = 0; i < entropy.length; i++) { - long update = ((byte) entropy[i]) << ((i % 8) * 8); - seed ^= update; - } - try { - // Construct and seed a new random number generator - Class<?> clazz = Class.forName(randomClass); - this.random = (Random) clazz.newInstance(); - this.random.setSeed(seed); - } catch (Exception e) { - // Fall back to the simple case - log.error(sm.getString("managerBase.random", - randomClass), e); - this.random = new java.util.Random(); - this.random.setSeed(seed); - } - if(log.isDebugEnabled()) { - long t2=System.currentTimeMillis(); - if( (t2-t1) > 100 ) - log.debug(sm.getString("managerBase.seeding", - randomClass) + " " + (t2-t1)); - } - } + protected Random createRandom() { + if (randomSeed == null) { + createRandomSeed(); + } + + Random result = null; + + long t1 = System.currentTimeMillis(); + try { + // Construct and seed a new random number generator + Class<?> clazz = Class.forName(randomClass); + result = (Random) clazz.newInstance(); + } catch (Exception e) { + // Fall back to the simple case + log.error(sm.getString("managerBase.random", + randomClass), e); + result = new java.util.Random(); + } + byte[] seedBytes = randomSeed.generateSeed(64); + ByteArrayInputStream bais = new ByteArrayInputStream(seedBytes); + DataInputStream dis = new DataInputStream(bais); + for (int i = 0; i < 8; i++) { + try { + result.setSeed(dis.readLong()); + } catch (IOException e) { + // Should never happen + log.error(sm.getString("managerBase.seedFailed", + result.getClass().getName()), e); } } - return this.random; + if(log.isDebugEnabled()) { + long t2=System.currentTimeMillis(); + if( (t2-t1) > 100 ) + log.debug(sm.getString("managerBase.createRandom", + Long.valueOf(t2-t1))); + } + return result; + } + + + /** + * Create the random number generator that will be used to seed the random + * number generators that will create session IDs. + */ + protected synchronized void createRandomSeed() { + if (randomSeed != null) { + return; + } + + long seed = System.currentTimeMillis(); + long t1 = seed; + char entropy[] = getEntropy().toCharArray(); + for (int i = 0; i < entropy.length; i++) { + long update = ((byte) entropy[i]) << ((i % 8) * 8); + seed ^= update; + } + + // Construct and seed a new random number generator + SecureRandom result = new SecureRandom(); + result.setSeed(seed); + + if(log.isDebugEnabled()) { + long t2=System.currentTimeMillis(); + if( (t2-t1) > 100 ) + log.debug(sm.getString("managerBase.createRandomSeed", + Long.valueOf(t2-t1))); + } + randomSeed = result; } @@ -983,7 +1031,12 @@ public abstract class ManagerBase extend devRandomSourceIsValid = false; closeRandomFile(); } - getRandom().nextBytes(bytes); + Random random = randoms.poll(); + if (random == null) { + random = createRandom(); + } + random.nextBytes(bytes); + randoms.add(random); } Modified: tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java?rev=1036019&r1=1036018&r2=1036019&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java (original) +++ tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java Wed Nov 17 12:59:06 2010 @@ -870,7 +870,7 @@ public abstract class PersistentManagerB ((Lifecycle)getStore()).stop(); // Require a new random number generator if we are restarted - this.random = null; + this.randoms.clear(); } Modified: tomcat/trunk/java/org/apache/catalina/session/StandardManager.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/StandardManager.java?rev=1036019&r1=1036018&r2=1036019&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/catalina/session/StandardManager.java (original) +++ tomcat/trunk/java/org/apache/catalina/session/StandardManager.java Wed Nov 17 12:59:06 2010 @@ -518,7 +518,7 @@ public class StandardManager extends Man } // Require a new random number generator if we are restarted - this.random = null; + this.randoms.clear(); } Modified: tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java?rev=1036019&r1=1036018&r2=1036019&view=diff ============================================================================== --- tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java (original) +++ tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java Wed Nov 17 12:59:06 2010 @@ -31,12 +31,12 @@ public class Benchmarks extends TestCase /* * Results on markt's 4-core Windows dev box - * 1 thread - ~270ms - * 2 threads - ~350ms - * 4 threads - ~870ms - * 16 threads - ~3,500ms + * 1 thread - ~300ms + * 2 threads - ~330ms + * 4 threads - ~480ms + * 16 threads - ~2,200ms * - * Results on markt's 2-core OSX dev box + * Results on markt's 2-core OSX dev box (out of date) * 1 thread - ~720ms * 2 threads - ~1,700ms * 4 threads - ~3,100ms @@ -114,12 +114,12 @@ public class Benchmarks extends TestCase /* * Results on markt's 4-core Windows dev box - * 1 thread - ~670ms + * 1 thread - ~625ms * 2 threads - ~690ms * 4 threads - ~1,100ms - * 16 threads - ~5,000ms + * 16 threads - ~4,300ms * - * Results on markt's 2-core OSX dev box + * Results on markt's 2-core OSX dev box (out of date) * 1 thread - ~1,500ms * 2 threads - ~2,500ms * 4 threads - ~4,500ms --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org