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;
+        }
     }
 
 }