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