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

Reply via email to