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: [email protected]
For additional commands, e-mail: [email protected]