You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2015/03/12 13:49:56 UTC

svn commit: r1666170 - in /jackrabbit/trunk: jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jmx/ jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/observation/ jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/

Author: mduerig
Date: Thu Mar 12 12:49:56 2015
New Revision: 1666170

URL: http://svn.apache.org/r1666170
Log:
JCR-3859: Extend EventListenerMBean to report time series for observation processing
Initial implementation of the additional EventListenerMBean methods

Added:
    jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java
    jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesStatsUtil.java
Modified:
    jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jmx/EventListenerMBean.java
    jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/observation/ListenerTracker.java

Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jmx/EventListenerMBean.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jmx/EventListenerMBean.java?rev=1666170&r1=1666169&r2=1666170&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jmx/EventListenerMBean.java (original)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/jmx/EventListenerMBean.java Thu Mar 12 12:49:56 2015
@@ -16,6 +16,8 @@
  */
 package org.apache.jackrabbit.api.jmx;
 
+import javax.management.openmbean.CompositeData;
+
 /**
  * MBean interface for exposing information about a registered observation
  * listener.
@@ -81,4 +83,28 @@ public interface EventListenerMBean {
     /** Is date information accessed from an external event? */
     boolean isDateAccessedFromExternalEvent();
 
+    /**
+     * {@link org.apache.jackrabbit.api.stats.TimeSeries time series} of the number of
+     * items related to generating observation events that are currently queued by the
+     * system. The exact nature of these items is implementation specific and might not
+     * be in a one to one relation with the number of pending JCR events.
+     * @return  time series of the queue length
+     */
+    CompositeData getQueueLength();
+
+    /**
+     * @return  time series of the number of JCR events
+     */
+    CompositeData getEventCount();
+
+    /**
+     * @return  time series of the time it took an event listener to process JCR events.
+     */
+    CompositeData getEventConsumerTime();
+
+    /**
+     * @return  time series of the time it took the system to produce JCR events.
+     */
+    CompositeData getEventProducerTime();
+
 }

Modified: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/observation/ListenerTracker.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/observation/ListenerTracker.java?rev=1666170&r1=1666169&r2=1666170&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/observation/ListenerTracker.java (original)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/commons/observation/ListenerTracker.java Thu Mar 12 12:49:56 2015
@@ -20,6 +20,7 @@ package org.apache.jackrabbit.commons.ob
 
 import static java.lang.System.currentTimeMillis;
 import static java.lang.System.nanoTime;
+import static org.apache.jackrabbit.stats.TimeSeriesStatsUtil.asCompositeData;
 
 import java.io.PrintWriter;
 import java.io.StringWriter;
@@ -30,10 +31,13 @@ import java.util.concurrent.atomic.Atomi
 import javax.jcr.observation.Event;
 import javax.jcr.observation.EventIterator;
 import javax.jcr.observation.EventListener;
+import javax.management.openmbean.CompositeData;
 
 import org.apache.jackrabbit.api.jmx.EventListenerMBean;
 import org.apache.jackrabbit.api.observation.JackrabbitEvent;
 import org.apache.jackrabbit.commons.iterator.EventIteratorAdapter;
+import org.apache.jackrabbit.stats.TimeSeriesMax;
+import org.apache.jackrabbit.stats.TimeSeriesRecorder;
 
 /**
  * Tracks event deliveries to an event listener and the way the listener
@@ -67,6 +71,14 @@ public class ListenerTracker {
 
     private final AtomicLong eventDeliveryTime = new AtomicLong();
 
+    private final TimeSeriesMax queueLength = new TimeSeriesMax();
+
+    private final TimeSeriesRecorder eventCount = new TimeSeriesRecorder(true);
+
+    private final TimeSeriesRecorder eventConsumerTime = new TimeSeriesRecorder(true);
+
+    private final TimeSeriesRecorder eventProducerTime = new TimeSeriesRecorder(true);
+
     final AtomicBoolean userInfoAccessedWithoutExternalsCheck =
             new AtomicBoolean();
 
@@ -80,11 +92,9 @@ public class ListenerTracker {
             new AtomicBoolean();
 
     public ListenerTracker(
-            EventListener listener,
-            int eventTypes, String absPath, boolean isDeep,
-            String[] uuid, String[] nodeTypeName, boolean noLocal) {
+            EventListener listener, int eventTypes, String absPath, boolean isDeep, String[] uuid,
+            String[] nodeTypeName, boolean noLocal) {
         this.listener = listener;
-
         this.eventTypes = eventTypes;
         this.absPath = absPath;
         this.isDeep = isDeep;
@@ -123,18 +133,46 @@ public class ListenerTracker {
         // do nothing
     }
 
+    /**
+     * Applications should call this to report the current queue length.
+     * @param length
+     */
+    public void recordQueueLength(long length) {
+        queueLength.recordValue(length);
+    }
+
+    /**
+     * Records the number of measured values over the past second and resets
+     * the counter. This method should be scheduled to be called once per
+     * second.
+     */
+    public void recordOneSecond() {
+        queueLength.recordOneSecond();
+        eventCount.recordOneSecond();
+        eventConsumerTime.recordOneSecond();
+        eventProducerTime.recordOneSecond();
+    }
+
     public EventListener getTrackedListener() {
         return new EventListener() {
             @Override
             public void onEvent(EventIterator events) {
                 eventDeliveries.incrementAndGet();
-                long start = nanoTime();
+                final long start = nanoTime();
                 try {
                     beforeEventDelivery();
                     listener.onEvent(new EventIteratorAdapter(events) {
+                        long t0 = start;
+
+                        private void recordTime(TimeSeriesRecorder recorder) {
+                            recorder.getCounter().addAndGet(-(t0 - (t0 = nanoTime())));
+                        }
+
                         @Override
                         public Object next() {
+                            recordTime(eventConsumerTime);
                             eventsDelivered.incrementAndGet();
+                            eventCount.getCounter().incrementAndGet();
                             Object object = super.next();
                             if (object instanceof JackrabbitEvent) {
                                 object = new JackrabbitEventTracker(
@@ -144,8 +182,18 @@ public class ListenerTracker {
                                 object = new EventTracker(
                                         ListenerTracker.this, (Event) object);
                             }
+                            recordTime(eventProducerTime);
                             return object;
                         }
+
+                        @Override
+                        public boolean hasNext() {
+                            recordTime(eventConsumerTime);
+                            boolean result = super.hasNext();
+                            t0 = nanoTime();
+                            recordTime(eventProducerTime);
+                            return result;
+                        }
                     });
                 } finally {
                     afterEventDelivery();
@@ -247,6 +295,22 @@ public class ListenerTracker {
             public synchronized boolean isDateAccessedFromExternalEvent() {
                 return dateAccessedFromExternalEvent.get();
             }
+            @Override
+            public CompositeData getQueueLength() {
+                return asCompositeData(queueLength, "queueLength");
+            }
+            @Override
+            public CompositeData getEventCount() {
+                return asCompositeData(eventCount, "eventCount");
+            }
+            @Override
+            public CompositeData getEventConsumerTime() {
+                return asCompositeData(eventConsumerTime, "eventConsumerTime");
+            }
+            @Override
+            public CompositeData getEventProducerTime() {
+                return asCompositeData(eventProducerTime, "eventProducerTime");
+            }
         };
     }
 

Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java?rev=1666170&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesMax.java Thu Mar 12 12:49:56 2015
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.jackrabbit.stats;
+
+import org.apache.jackrabbit.api.stats.TimeSeries;
+
+/**
+ * Time series of the maximum value recorded in a period
+ */
+public class TimeSeriesMax implements TimeSeries {
+    private final MaxValue max = new MaxValue(0);
+    private final long[] perSecond = new long[60];
+    private final long[] perMinute = new long[60];
+    private final long[] perHour = new long[7 * 24];
+    private final long[] perWeek = new long[3 * 52];
+
+    /** Current second (index in {@link #perSecond}) */
+    private int seconds;
+
+    /** Current minute (index in {@link #perMinute}) */
+    private int minutes;
+
+    /** Current hour (index in {@link #perHour}) */
+    private int hours;
+
+    /** Current week (index in {@link #perWeek}) */
+    private int weeks;
+
+    public void recordValue(long value) {
+        max.setIfMaximal(value);
+    }
+
+    /**
+     * Records the number of measured values over the past second and resets
+     * the counter. This method should be scheduled to be called once per
+     * second.
+     */
+    public synchronized void recordOneSecond() {
+        perSecond[seconds++] = max.getAndSetValue(0);
+        if (seconds == perSecond.length) {
+            seconds = 0;
+            perMinute[minutes++] = max(perSecond);
+        }
+        if (minutes == perMinute.length) {
+            minutes = 0;
+            perHour[hours++] = max(perMinute);
+        }
+        if (hours == perHour.length) {
+            hours = 0;
+            perWeek[weeks++] = max(perHour);
+        }
+        if (weeks == perWeek.length) {
+            weeks = 0;
+        }
+    }
+
+    @Override
+    public synchronized long[] getValuePerSecond() {
+        return cyclicCopyFrom(perSecond, seconds);
+    }
+
+    @Override
+    public synchronized long[] getValuePerMinute() {
+        return cyclicCopyFrom(perMinute, minutes);
+    }
+
+    @Override
+    public synchronized long[] getValuePerHour() {
+        return cyclicCopyFrom(perHour, hours);
+    }
+
+    @Override
+    public synchronized long[] getValuePerWeek() {
+        return cyclicCopyFrom(perWeek, weeks);
+    }
+
+    /**
+     * Returns the maximum of all entries in the given array.
+     */
+    private static long max(long[] array) {
+        long max = Long.MIN_VALUE;
+        for (long v : array) {
+            if (v > max) {
+                max = v;
+            }
+        }
+        return max;
+    }
+
+    /**
+     * Returns a copy of the given cyclical array, with the element at
+     * the given position as the first element of the returned array.
+     *
+     * @param array cyclical array
+     * @param pos position of the first element
+     * @return copy of the array
+     */
+    private static long[] cyclicCopyFrom(long[] array, int pos) {
+        long[] reverse = new long[array.length];
+        for (int i = 0; i < array.length; i++) {
+            reverse[i] = array[(pos + i) % array.length];
+        }
+        return reverse;
+    }
+
+    private static class MaxValue {
+        private long value;
+
+        public MaxValue(long value) {
+            this.value = value;
+        }
+
+        public synchronized long getAndSetValue(long value) {
+            long v = this.value;
+            this.value = value;
+            return v;
+        }
+
+        public synchronized void setIfMaximal(long value) {
+            if (value > this.value) {
+                this.value = value;
+            }
+        }
+    }
+}

Added: jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesStatsUtil.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesStatsUtil.java?rev=1666170&view=auto
==============================================================================
--- jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesStatsUtil.java (added)
+++ jackrabbit/trunk/jackrabbit-jcr-commons/src/main/java/org/apache/jackrabbit/stats/TimeSeriesStatsUtil.java Thu Mar 12 12:49:56 2015
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.jackrabbit.stats;
+
+import javax.management.openmbean.ArrayType;
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+
+import org.apache.jackrabbit.api.stats.TimeSeries;
+
+/**
+ * Utility class for retrieving {@link javax.management.openmbean.CompositeData} for
+ * {@link org.apache.jackrabbit.api.stats.TimeSeries}.
+ */
+public final class TimeSeriesStatsUtil {
+    public static final String[] ITEM_NAMES = new String[] {"per second", "per minute", "per hour", "per week"};
+
+    private TimeSeriesStatsUtil() {
+    }
+
+    public static CompositeData asCompositeData(TimeSeries timeSeries, String name) {
+        try {
+            long[][] values = new long[][] {timeSeries.getValuePerSecond(), timeSeries.getValuePerMinute(),
+                    timeSeries.getValuePerHour(), timeSeries.getValuePerWeek()};
+            return new CompositeDataSupport(getCompositeType(name), ITEM_NAMES, values);
+        } catch (Exception e) {
+            throw new IllegalArgumentException("Error creating CompositeData instance from TimeSeries", e);
+        }
+    }
+
+    private static CompositeType getCompositeType(String name) throws OpenDataException {
+        ArrayType<int[]> longArrayType = new ArrayType<int[]>(SimpleType.LONG, true);
+        OpenType<?>[] itemTypes = new OpenType[] {longArrayType, longArrayType, longArrayType, longArrayType};
+        return new CompositeType(name, name + " time series", ITEM_NAMES, ITEM_NAMES, itemTypes);
+    }
+}