Author: markt
Date: Thu Nov 18 19:59:11 2010
New Revision: 1036595

URL: http://svn.apache.org/viewvc?rev=1036595&view=rev
Log:
Fix expiration statistics broken by r1036281
Add session creation and expiration rate statistics based on the 100 most 
recently created/expired sessions
Modify average session alive time to also use 100 most recently expired sessions
Update benchmarks - new statistics add overhead but not significant in overall 
processing chain

Modified:
    tomcat/trunk/java/org/apache/catalina/Manager.java
    tomcat/trunk/java/org/apache/catalina/ha/session/DeltaManager.java
    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/StandardSession.java
    tomcat/trunk/java/org/apache/catalina/session/mbeans-descriptors.xml
    tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java

Modified: tomcat/trunk/java/org/apache/catalina/Manager.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Manager.java?rev=1036595&r1=1036594&r2=1036595&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/Manager.java (original)
+++ tomcat/trunk/java/org/apache/catalina/Manager.java Thu Nov 18 19:59:11 2010
@@ -213,24 +213,30 @@ public interface Manager {
 
     /**
      * Gets the average time (in seconds) that expired sessions had been
-     * alive.
-     *
+     * alive. This may be based on sample data.
+     * 
      * @return Average time (in seconds) that expired sessions had been
      * alive.
      */
     public int getSessionAverageAliveTime();
 
-
+    
     /**
-     * Sets the average time (in seconds) that expired sessions had been
-     * alive.
-     *
-     * @param sessionAverageAliveTime Average time (in seconds) that expired
-     * sessions had been alive.
+     * Gets the current rate of session creation (in session per minute). This
+     * may be based on sample data.
+     * 
+     * @return  The current rate (in sessions per minute) of session creation
      */
-    public void setSessionAverageAliveTime(int sessionAverageAliveTime);
-
+    public int getSessionCreateRate();
+    
 
+    /**
+     * Gets the current rate of session expiration (in session per minute). 
This
+     * may be based on sample data
+     * 
+     * @return  The current rate (in sessions per minute) of session expiration
+     */
+    public int getSessionExpireRate();
     // --------------------------------------------------------- Public Methods
 
 
@@ -326,6 +332,15 @@ public interface Manager {
 
 
     /**
+     * Remove this Session from the active Sessions for this Manager.
+     *
+     * @param session   Session to be removed
+     * @param update    Should the expiration statistics be updated
+     */
+    public void remove(Session session, boolean update);
+
+
+    /**
      * Remove a property change listener from this component.
      *
      * @param listener The listener to remove

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=1036595&r1=1036594&r2=1036595&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/ha/session/DeltaManager.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/ha/session/DeltaManager.java Thu Nov 
18 19:59:11 2010
@@ -40,6 +40,7 @@ import org.apache.catalina.ha.CatalinaCl
 import org.apache.catalina.ha.ClusterManager;
 import org.apache.catalina.ha.ClusterMessage;
 import org.apache.catalina.ha.tcp.ReplicationValve;
+import org.apache.catalina.session.ManagerBase;
 import org.apache.catalina.tribes.Member;
 import org.apache.catalina.tribes.io.ReplicationStream;
 import org.apache.tomcat.util.ExceptionUtils;
@@ -1117,7 +1118,17 @@ public CatalinaCluster getCluster() {
      */
     public synchronized void resetStatistics() {
         processingTime = 0 ;
-        expiredSessions = 0 ;
+        expiredSessions.set(0);
+        sessionCreationTiming.clear();
+        while (sessionCreationTiming.size() <
+                ManagerBase.TIMING_STATS_CACHE_SIZE) {
+            sessionCreationTiming.add(null);
+        }
+        sessionExpirationTiming.clear();
+        while (sessionExpirationTiming.size() <
+                ManagerBase.TIMING_STATS_CACHE_SIZE) {
+            sessionExpirationTiming.add(null);
+        }
         rejectedSessions = 0 ;
         sessionReplaceCounter = 0 ;
         counterNoStateTransfered = 0 ;

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=1036595&r1=1036594&r2=1036595&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java (original)
+++ tomcat/trunk/java/org/apache/catalina/session/ManagerBase.java Thu Nov 18 
19:59:11 2010
@@ -34,15 +34,20 @@ import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.PrivilegedAction;
 import java.security.SecureRandom;
+import java.util.ArrayList;
 import java.util.Date;
+import java.util.Deque;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
 import java.util.Map;
 import java.util.Queue;
 import java.util.Random;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.atomic.AtomicLong;
 
 import org.apache.catalina.Container;
 import org.apache.catalina.Context;
@@ -183,16 +188,18 @@ public abstract class ManagerBase extend
     private final Object sessionMaxAliveTimeUpdateLock = new Object();
 
 
-    /**
-     * Average time (in seconds) that expired sessions had been alive.
-     */
-    protected int sessionAverageAliveTime;
+    protected static final int TIMING_STATS_CACHE_SIZE = 100;
 
+    protected Deque<SessionTiming> sessionCreationTiming =
+        new LinkedList<SessionTiming>();
+
+    protected Deque<SessionTiming> sessionExpirationTiming =
+        new LinkedList<SessionTiming>();
 
     /**
      * Number of sessions that have expired.
      */
-    protected long expiredSessions = 0;
+    protected AtomicLong expiredSessions = new AtomicLong(0);
 
 
     /**
@@ -760,7 +767,7 @@ public abstract class ManagerBase extend
      */
     @Override
     public long getExpiredSessions() {
-        return expiredSessions;
+        return expiredSessions.get();
     }
 
 
@@ -771,7 +778,7 @@ public abstract class ManagerBase extend
      */
     @Override
     public void setExpiredSessions(long expiredSessions) {
-        this.expiredSessions = expiredSessions;
+        this.expiredSessions.set(expiredSessions);
     }
 
     public long getProcessingTime() {
@@ -863,6 +870,15 @@ public abstract class ManagerBase extend
             randomInputStreams.add(is);
         }
 
+        // Ensure caches for timing stats are the right size by filling with
+        // nulls.
+        while (sessionCreationTiming.size() < TIMING_STATS_CACHE_SIZE) {
+            sessionCreationTiming.add(null);
+        }
+        while (sessionExpirationTiming.size() < TIMING_STATS_CACHE_SIZE) {
+            sessionExpirationTiming.add(null);
+        }
+
         // Force initialization of the random number generator
         if (log.isDebugEnabled())
             log.debug("Force random number initialization starting");
@@ -948,6 +964,11 @@ public abstract class ManagerBase extend
         session.setId(id);
         sessionCounter++;
 
+        SessionTiming timing = new SessionTiming(session.getCreationTime(), 0);
+        synchronized (sessionCreationTiming) {
+            sessionCreationTiming.add(timing);
+            sessionCreationTiming.poll();
+        }
         return (session);
 
     }
@@ -1004,20 +1025,29 @@ public abstract class ManagerBase extend
      */
     @Override
     public void remove(Session session) {
+        remove(session, false);
+    }
+    
+    /**
+     * Remove this Session from the active Sessions for this Manager.
+     *
+     * @param session   Session to be removed
+     * @param update    Should the expiration statistics be updated
+     */
+    @Override
+    public void remove(Session session, boolean update) {
         
         // If the session has expired - as opposed to just being removed from
         // the manager because it is being persisted - update the expired stats
-        if (!session.isValid()) {
+        if (update) {
             long timeNow = System.currentTimeMillis();
             int timeAlive = (int) ((timeNow - session.getCreationTime())/1000);
             updateSessionMaxAliveTime(timeAlive);
-            synchronized (this) {
-                long numExpired = getExpiredSessions();
-                numExpired++;
-                setExpiredSessions(numExpired);
-                int average = getSessionAverageAliveTime();
-                average = (int) (((average * (numExpired-1)) + 
timeAlive)/numExpired);
-                setSessionAverageAliveTime(average);
+            expiredSessions.incrementAndGet();
+            SessionTiming timing = new SessionTiming(timeNow, timeAlive);
+            synchronized (sessionExpirationTiming) {
+                sessionExpirationTiming.add(timing);
+                sessionExpirationTiming.poll();
             }
         }
 
@@ -1322,27 +1352,124 @@ public abstract class ManagerBase extend
 
     /**
      * Gets the average time (in seconds) that expired sessions had been
-     * alive.
-     *
+     * alive based on the last 100 sessions to expire. If less than
+     * 100 sessions have expired then all available data is used.
+     * 
      * @return Average time (in seconds) that expired sessions had been
      * alive.
      */
     @Override
     public int getSessionAverageAliveTime() {
-        return sessionAverageAliveTime;
+        // Copy current stats
+        List<SessionTiming> copy = new ArrayList<SessionTiming>();
+        synchronized (sessionExpirationTiming) {
+            copy.addAll(sessionExpirationTiming);
+        }
+        
+        // Init
+        int counter = 0;
+        int result = 0;
+        Iterator<SessionTiming> iter = copy.iterator();
+        
+        // Calculate average
+        while (iter.hasNext()) {
+            SessionTiming timing = iter.next();
+            if (timing != null) {
+                int timeAlive = timing.getDuration();
+                counter++;
+                // Very careful not to overflow - probably not necessary
+                result =
+                    (result * ((counter - 1)/counter)) + (timeAlive/counter);
+            }
+        }
+        return result;
     }
 
+    
+    /**
+     * Gets the current rate of session creation (in session per minute) based
+     * on the creation time of the previous 100 sessions created. If less than
+     * 100 sessions have been created then all available data is used.
+     * 
+     * @return  The current rate (in sessions per minute) of session creation
+     */
+    @Override
+    public int getSessionCreateRate() {
+        long now = System.currentTimeMillis();
+        // Copy current stats
+        List<SessionTiming> copy = new ArrayList<SessionTiming>();
+        synchronized (sessionCreationTiming) {
+            copy.addAll(sessionCreationTiming);
+        }
+        
+        // Init
+        long oldest = now;
+        int counter = 0;
+        int result = 0;
+        Iterator<SessionTiming> iter = copy.iterator();
+        
+        // Calculate rate
+        while (iter.hasNext()) {
+            SessionTiming timing = iter.next();
+            if (timing != null) {
+                counter++;
+                if (timing.getTimestamp() < oldest) {
+                    oldest = timing.getTimestamp();
+                }
+            }
+        }
+        if (counter > 0) {
+            if (oldest < now) {
+                result = (int) ((1000*60*counter)/(now - oldest));
+            } else {
+                result = Integer.MAX_VALUE;
+            }
+        }
+        return result;
+    }
+    
 
     /**
-     * Sets the average time (in seconds) that expired sessions had been
-     * alive.
-     *
-     * @param sessionAverageAliveTime Average time (in seconds) that expired
-     * sessions had been alive.
+     * Gets the current rate of session expiration (in session per minute) 
based
+     * on the expiry time of the previous 100 sessions expired. If less than
+     * 100 sessions have expired then all available data is used.
+     * 
+     * @return  The current rate (in sessions per minute) of session expiration
      */
     @Override
-    public void setSessionAverageAliveTime(int sessionAverageAliveTime) {
-        this.sessionAverageAliveTime = sessionAverageAliveTime;
+    public int getSessionExpireRate() {
+        long now = System.currentTimeMillis();
+        // Copy current stats
+        List<SessionTiming> copy = new ArrayList<SessionTiming>();
+        synchronized (sessionExpirationTiming) {
+            copy.addAll(sessionExpirationTiming);
+        }
+        
+        // Init
+        long oldest = now;
+        int counter = 0;
+        int result = 0;
+        Iterator<SessionTiming> iter = copy.iterator();
+        
+        // Calculate rate
+        while (iter.hasNext()) {
+            SessionTiming timing = iter.next();
+            if (timing != null) {
+                counter++;
+                if (timing.getTimestamp() < oldest) {
+                    oldest = timing.getTimestamp();
+                }
+            }
+        }
+        if (counter > 0) {
+            if (oldest < now) {
+                result = (int) ((1000*60*counter)/(now - oldest));
+            } else {
+                // Better than reporting zero
+                result = Integer.MAX_VALUE;
+            }
+        }
+        return result;
     }
 
 
@@ -1552,4 +1679,31 @@ public abstract class ManagerBase extend
             }
         }
     }
+    
+    // ----------------------------------------------------------- Inner 
classes
+    
+    protected static final class SessionTiming {
+        private long timestamp;
+        private int duration;
+        
+        public SessionTiming(long timestamp, int duration) {
+            this.timestamp = timestamp;
+            this.duration = duration;
+        }
+        
+        /**
+         * Time stamp associated with this piece of timing information in
+         * milliseconds.
+         */
+        public long getTimestamp() {
+            return timestamp;
+        }
+        
+        /**
+         * Duration associated with this piece of timing information in 
seconds.
+         */
+        public int getDuration() {
+            return duration;
+        }
+    }
 }

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=1036595&r1=1036594&r2=1036595&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/session/PersistentManagerBase.java 
Thu Nov 18 19:59:11 2010
@@ -438,7 +438,7 @@ public abstract class PersistentManagerB
              log.debug("Start expire sessions " + getName() + " at " + timeNow 
+ " sessioncount " + sessions.length);
         for (int i = 0; i < sessions.length; i++) {
             if (!sessions[i].isValid()) {
-                expiredSessions++;
+                expiredSessions.incrementAndGet();
                 expireHere++;
             }
         }

Modified: tomcat/trunk/java/org/apache/catalina/session/StandardSession.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/StandardSession.java?rev=1036595&r1=1036594&r2=1036595&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/session/StandardSession.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/session/StandardSession.java Thu Nov 
18 19:59:11 2010
@@ -836,7 +836,7 @@ public class StandardSession
             setValid(false);
 
             // Remove this session from our manager's active sessions
-            manager.remove(this);
+            manager.remove(this, true);
 
             // Notify interested session event listeners
             if (notify) {

Modified: tomcat/trunk/java/org/apache/catalina/session/mbeans-descriptors.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/session/mbeans-descriptors.xml?rev=1036595&r1=1036594&r2=1036595&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/session/mbeans-descriptors.xml 
(original)
+++ tomcat/trunk/java/org/apache/catalina/session/mbeans-descriptors.xml Thu 
Nov 18 19:59:11 2010
@@ -96,12 +96,23 @@
                
     <attribute   name="sessionAverageAliveTime"
           description="Average time an expired session had been alive"
-                 type="int" />
+                 type="int"
+            writeable="false" />
+
+    <attribute   name="sessionCreateRate"
+          description="Session creation rate in sessions per minute"
+                 type="int"
+            writeable="false" />
 
     <attribute   name="sessionCounter"
           description="Total number of sessions created by this manager"
                  type="long" />
                  
+    <attribute   name="sessionExpireRate"
+          description="Session expiration rate in sessions per minute"
+                 type="int"
+            writeable="false" />
+                 
     <attribute   name="sessionIdLength"
           description="The session id length (in bytes) of Sessions
                        created by this Manager"
@@ -300,12 +311,23 @@
                
     <attribute   name="sessionAverageAliveTime"
           description="Average time an expired session had been alive"
-                 type="int" />
+                 type="int"
+            writeable="false" />
+
+    <attribute   name="sessionCreateRate"
+          description="Session creation rate in sessions per minute"
+                 type="int"
+            writeable="false" />
 
     <attribute   name="sessionCounter"
           description="Total number of sessions created by this manager"
                  type="long" />
                  
+    <attribute   name="sessionExpireRate"
+          description="Session expiration rate in sessions per minute"
+                 type="int"
+            writeable="false" />
+
     <attribute   name="sessionIdLength"
           description="The session id length (in bytes) of Sessions
                        created by this Manager"

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=1036595&r1=1036594&r2=1036595&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java (original)
+++ tomcat/trunk/test/org/apache/catalina/session/Benchmarks.java Thu Nov 18 
19:59:11 2010
@@ -31,10 +31,10 @@ public class Benchmarks extends TestCase
 
     /*
      * Results on markt's 4-core Windows dev box
-     *  1 thread  -  ~1,900ms
+     *  1 thread  -  ~2,000ms
      *  2 threads -  ~3,300ms
-     *  4 threads -  ~4,800ms
-     * 16 threads - ~21,000ms
+     *  4 threads -  ~4,900ms
+     * 16 threads - ~21,300ms
      * 
      * Results on markt's 2-core OSX dev box
      *  1 thread  -   ~5,600ms
@@ -68,6 +68,14 @@ public class Benchmarks extends TestCase
         mgr.randomFileCurrent = mgr.randomFile;
         mgr.createRandomInputStream();
         mgr.generateSessionId();
+        while (mgr.sessionCreationTiming.size() <
+                ManagerBase.TIMING_STATS_CACHE_SIZE) {
+            mgr.sessionCreationTiming.add(null);
+        }
+        while (mgr.sessionExpirationTiming.size() <
+                ManagerBase.TIMING_STATS_CACHE_SIZE) {
+            mgr.sessionExpirationTiming.add(null);
+        }
 
         
         Thread[] threads = new Thread[threadCount];
@@ -128,10 +136,10 @@ public class Benchmarks extends TestCase
     
     /*
      * Results on markt's 4-core Windows dev box
-     *  1 thread  -  ~4,600ms
-     *  2 threads -  ~6,700ms
-     *  4 threads - ~10,400ms
-     * 16 threads - ~43,800ms
+     *  1 thread  -  ~4,300ms
+     *  2 threads -  ~7,600ms
+     *  4 threads - ~11,600ms
+     * 16 threads - ~49,000ms
      * 
      * Results on markt's 2-core OSX dev box
      *  1 thread  -  ~9,100ms
@@ -159,6 +167,14 @@ public class Benchmarks extends TestCase
         mgr.randomFileCurrent = mgr.randomFile;
         mgr.createRandomInputStream();
         mgr.generateSessionId();
+        while (mgr.sessionCreationTiming.size() <
+                ManagerBase.TIMING_STATS_CACHE_SIZE) {
+            mgr.sessionCreationTiming.add(null);
+        }
+        while (mgr.sessionExpirationTiming.size() <
+                ManagerBase.TIMING_STATS_CACHE_SIZE) {
+            mgr.sessionExpirationTiming.add(null);
+        }
 
         Thread[] threads = new Thread[threadCount];
         



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to