You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2015/10/07 21:50:47 UTC
svn commit: r1707375 -
/sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/RequestProcessorMBeanImpl.java
Author: rombert
Date: Wed Oct 7 19:50:46 2015
New Revision: 1707375
URL: http://svn.apache.org/viewvc?rev=1707375&view=rev
Log:
SLING-4999 - Check synchronization in RequestProcessorMBeanImpl
Use a non-blocking approach for storing and computing request
statistics.
Modified:
sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/RequestProcessorMBeanImpl.java
Modified: sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/RequestProcessorMBeanImpl.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/RequestProcessorMBeanImpl.java?rev=1707375&r1=1707374&r2=1707375&view=diff
==============================================================================
--- sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/RequestProcessorMBeanImpl.java (original)
+++ sling/trunk/bundles/engine/src/main/java/org/apache/sling/engine/impl/RequestProcessorMBeanImpl.java Wed Oct 7 19:50:46 2015
@@ -16,6 +16,8 @@
*/
package org.apache.sling.engine.impl;
+import java.util.concurrent.atomic.AtomicReference;
+
import javax.management.NotCompliantMBeanException;
import javax.management.StandardMBean;
@@ -28,174 +30,253 @@ import org.apache.sling.engine.jmx.Reque
*/
class RequestProcessorMBeanImpl extends StandardMBean implements RequestProcessorMBean {
- // number of requests
- private volatile long n;
-
- // shortest request
- private volatile long durationMsecMin;
-
- // longest request
- private volatile long durationMsecMax;
-
- // sum of request durations
- private volatile double durationMsecSumX;
-
- // sum of squared request durations
- private volatile double durationMsecSumX2;
-
- private volatile int servletCallCountMin;
-
- private volatile int servletCallCountMax;
-
- private volatile double servletCallCountSumX;
-
- private volatile double servletCallCountSumX2;
-
- private volatile int peakRecursionDepthMin;
-
- private volatile int peakRecursionDepthMax;
-
- private volatile double peakRecursionDepthSumX;
-
- private volatile double peakRecursionDepthSumX2;
+ private final AtomicReference<Data> dataRef = new AtomicReference<Data>(new Data());
RequestProcessorMBeanImpl() throws NotCompliantMBeanException {
super(RequestProcessorMBean.class);
- resetStatistics();
}
- synchronized void addRequestData(final RequestData data) {
- this.n++;
+ void addRequestData(final RequestData data) {
- final long duration = data.getElapsedTimeMsec();
- final int servletCallCount = data.getServletCallCount();
- final int peakRecursionDepth = data.getPeakRecusionDepth();
-
- if (duration < this.durationMsecMin) {
- this.durationMsecMin = duration;
- }
- if (duration > this.durationMsecMax) {
- this.durationMsecMax = duration;
- }
-
- this.durationMsecSumX += duration;
- this.durationMsecSumX2 += (duration * duration);
-
- if (servletCallCount < this.servletCallCountMin) {
- this.servletCallCountMin = servletCallCount;
- }
- if (servletCallCount > this.servletCallCountMax) {
- this.servletCallCountMax = servletCallCount;
- }
- this.servletCallCountSumX += servletCallCount;
- this.servletCallCountSumX2 += (servletCallCount * servletCallCount);
-
- if (peakRecursionDepth < this.peakRecursionDepthMin) {
- this.peakRecursionDepthMin = peakRecursionDepth;
- }
- if (peakRecursionDepth > this.peakRecursionDepthMax) {
- this.peakRecursionDepthMax = peakRecursionDepth;
- }
- this.peakRecursionDepthSumX += peakRecursionDepth;
- this.peakRecursionDepthSumX2 += (peakRecursionDepth * peakRecursionDepth);
- }
+ // do a non-blocking busy loop and atomically set the new data
+ // the advantage of this algorithm is that there is no blocking
+ // involved
+ //
+ // there might be some memory churn under high contention, but that
+ // remains to be seen
+ for ( ;; ) {
+
+ Data oldVal = dataRef.get();
+ Data newVal = new Data(oldVal, data);
+
+ boolean success = dataRef.compareAndSet(oldVal, newVal);
+ if ( success ) {
+ break;
+ }
+ }
+ }
+
+ public void resetStatistics() {
+ dataRef.set(new Data());
+ }
public long getRequestsCount() {
- return this.n;
+ return dataRef.get().n;
}
public long getMinRequestDurationMsec() {
- return this.durationMsecMin;
+ return dataRef.get().durationMsecMin;
}
public long getMaxRequestDurationMsec() {
- return this.durationMsecMax;
- }
-
- public synchronized double getStandardDeviationDurationMsec() {
- if (this.n > 1) {
- // algorithm taken from
- // http://de.wikipedia.org/wiki/Standardabweichung section
- // "Berechnung fuer auflaufende Messwerte"
- return Math.sqrt((this.durationMsecSumX2 - this.durationMsecSumX * this.durationMsecSumX / this.n) / (this.n - 1));
- }
-
- // single data point has no deviation
- return 0;
+ return dataRef.get().durationMsecMax;
}
- public synchronized double getMeanRequestDurationMsec() {
- if (this.n > 0) {
- return this.durationMsecSumX / this.n;
- } else {
- return 0;
- }
+ public double getStandardDeviationDurationMsec() {
+ return dataRef.get().standardDeviationDurationMsec;
}
- public synchronized void resetStatistics() {
- this.durationMsecMin = Long.MAX_VALUE;
- this.durationMsecMax = 0;
- this.servletCallCountMin = Integer.MAX_VALUE;
- this.servletCallCountMax = 0;
- this.peakRecursionDepthMin = Integer.MAX_VALUE;
- this.peakRecursionDepthMax = 0;
- this.durationMsecSumX=0d;
- this.durationMsecSumX2=0d;
- this.peakRecursionDepthSumX=0d;
- this.peakRecursionDepthSumX2=0d;
- this.servletCallCountSumX=0d;
- this.servletCallCountSumX2=0d;
- this.n = 0;
+ public double getMeanRequestDurationMsec() {
+ return dataRef.get().meanRequestDurationMsec;
}
public int getMaxPeakRecursionDepth() {
- return peakRecursionDepthMax;
+ return dataRef.get().peakRecursionDepthMax;
}
public int getMinPeakRecursionDepth() {
- return peakRecursionDepthMin;
+ return dataRef.get().peakRecursionDepthMin;
}
public double getMeanPeakRecursionDepth() {
- if (this.n > 0) {
- return this.peakRecursionDepthSumX / this.n;
- } else {
- return 0;
- }
+ return dataRef.get().meanPeakRecursionDepth;
}
public double getStandardDeviationPeakRecursionDepth() {
- if (this.n > 1) {
- return Math.sqrt((this.peakRecursionDepthSumX2 - this.peakRecursionDepthSumX * this.peakRecursionDepthSumX / this.n) / (this.n - 1));
- }
-
- // single data point has no deviation
- return 0;
+ return dataRef.get().standardDeviationPeakRecursionDepth;
}
public int getMaxServletCallCount() {
- return this.servletCallCountMax;
+ return dataRef.get().servletCallCountMax;
}
public int getMinServletCallCount() {
- return this.servletCallCountMin;
+ return dataRef.get().servletCallCountMin;
}
public double getMeanServletCallCount() {
- if (this.n > 0) {
- return this.servletCallCountSumX / this.n;
- } else {
- return 0;
- }
+
+ return dataRef.get().meanServletCallCount;
}
public double getStandardDeviationServletCallCount() {
- if (this.n > 1) {
- return Math.sqrt((this.servletCallCountSumX2 - this.servletCallCountSumX * this.servletCallCountSumX / this.n) / (this.n - 1));
+
+ return dataRef.get().standardDeviationServletCallCount;
+ }
+
+ /**
+ * Helper class to atomically hold raw data and compute statistics
+ */
+ private static class Data {
+
+ // number of requests
+ private final long n;
+
+ // shortest request
+ private final long durationMsecMin;
+
+ // longest request
+ private final long durationMsecMax;
+
+ // sum of request durations
+ private final double durationMsecSumX;
+
+ // sum of squared request durations
+ private final double durationMsecSumX2;
+
+ private final int servletCallCountMin;
+
+ private final int servletCallCountMax;
+
+ private final double servletCallCountSumX;
+
+ private final double servletCallCountSumX2;
+
+ private final int peakRecursionDepthMin;
+
+ private final int peakRecursionDepthMax;
+
+ private final double peakRecursionDepthSumX;
+
+ private final double peakRecursionDepthSumX2;
+
+ private final double standardDeviationDurationMsec;
+
+ private final double meanRequestDurationMsec;
+
+ private final double meanPeakRecursionDepth;
+
+ private final double standardDeviationPeakRecursionDepth;
+
+ private final double meanServletCallCount;
+
+ private final double standardDeviationServletCallCount;
+
+ // computed fields
+
+ Data() {
+ n = 0;
+
+ durationMsecMin = Long.MAX_VALUE;
+ durationMsecMax = 0;
+ durationMsecSumX = 0;
+ durationMsecSumX2 = 0;
+
+ servletCallCountMin = Integer.MAX_VALUE;
+ servletCallCountMax = 0;
+ servletCallCountSumX = 0;
+ servletCallCountSumX2 = 0;
+
+ peakRecursionDepthMin = Integer.MAX_VALUE;
+ peakRecursionDepthMax = 0;
+ peakRecursionDepthSumX = 0;
+ peakRecursionDepthSumX2 = 0;
+
+ standardDeviationDurationMsec = computeStandardDeviationDurationMsec();
+ meanRequestDurationMsec = computeMeanRequestDurationMsec();
+ meanPeakRecursionDepth = computeMeanPeakRecursionDepth();
+ standardDeviationPeakRecursionDepth = computeStandardDeviationPeakRecursionDepth();
+ meanServletCallCount = computeMeanServletCallCount();
+ standardDeviationServletCallCount = computeStandardDeviationServletCallCount();
+ }
+
+ Data(Data other, RequestData data) {
+ if ( other == null || data == null ) {
+ throw new IllegalArgumentException("Neither 'other' nor 'data' may be null");
+ }
+
+ final long duration = data.getElapsedTimeMsec();
+ final int servletCallCount = data.getServletCallCount();
+ final int peakRecursionDepth = data.getPeakRecusionDepth();
+
+ n = other.n + 1;
+
+ durationMsecMin = Math.min(duration, other.durationMsecMin);
+ durationMsecMax = Math.max(duration, other.durationMsecMax);
+ durationMsecSumX = other.durationMsecSumX + duration;
+ durationMsecSumX2 = other.durationMsecSumX2 + (duration * duration);
+
+ servletCallCountMin = Math.min(servletCallCount, other.servletCallCountMin);
+ servletCallCountMax = Math.max(servletCallCount, other.servletCallCountMax);
+ servletCallCountSumX = other.servletCallCountSumX + servletCallCount;
+ servletCallCountSumX2 = other.servletCallCountSumX2 + (servletCallCount * servletCallCount);
+
+ peakRecursionDepthMin = Math.min(peakRecursionDepth , other.peakRecursionDepthMin);
+ peakRecursionDepthMax = Math.max(peakRecursionDepth , other.peakRecursionDepthMax);
+ peakRecursionDepthSumX = other.peakRecursionDepthSumX + peakRecursionDepth;
+ peakRecursionDepthSumX2 = other.peakRecursionDepthSumX2 + (peakRecursionDepth * peakRecursionDepth);
+
+ standardDeviationDurationMsec = computeStandardDeviationDurationMsec();
+ meanRequestDurationMsec = computeMeanRequestDurationMsec();
+ meanPeakRecursionDepth = computeMeanPeakRecursionDepth();
+ standardDeviationPeakRecursionDepth = computeStandardDeviationPeakRecursionDepth();
+ meanServletCallCount = computeMeanServletCallCount();
+ standardDeviationServletCallCount = computeStandardDeviationServletCallCount();
+ }
+
+ private double computeStandardDeviationDurationMsec() {
+ if (this.n > 1) {
+ // algorithm taken from
+ // http://de.wikipedia.org/wiki/Standardabweichung section
+ // "Berechnung fuer auflaufende Messwerte"
+ return Math.sqrt((this.durationMsecSumX2 - this.durationMsecSumX * this.durationMsecSumX / this.n) / (this.n - 1));
+ }
+
+ // single data point has no deviation
+ return 0;
+ }
+
+ private double computeMeanRequestDurationMsec() {
+ if (this.n > 0) {
+ return this.durationMsecSumX / this.n;
+ } else {
+ return 0;
+ }
+ }
+
+ private double computeMeanPeakRecursionDepth() {
+ if (this.n > 0) {
+ return this.peakRecursionDepthSumX / this.n;
+ } else {
+ return 0;
+ }
}
- // single data point has no deviation
- return 0;
+ private double computeStandardDeviationPeakRecursionDepth() {
+ if (this.n > 1) {
+ return Math.sqrt((this.peakRecursionDepthSumX2 - this.peakRecursionDepthSumX * this.peakRecursionDepthSumX / this.n) / (this.n - 1));
+ }
+
+ // single data point has no deviation
+ return 0;
+ }
+
+ private double computeMeanServletCallCount() {
+ if (this.n > 0) {
+ return this.servletCallCountSumX / this.n;
+ } else {
+ return 0;
+ }
+ }
+
+ private double computeStandardDeviationServletCallCount() {
+ if (this.n > 1) {
+ return Math.sqrt((this.servletCallCountSumX2 - this.servletCallCountSumX * this.servletCallCountSumX / this.n) / (this.n - 1));
+ }
+
+ // single data point has no deviation
+ return 0;
+ }
}
}