Author: tomekr
Date: Tue Sep 19 10:02:55 2017
New Revision: 1808851

URL: http://svn.apache.org/viewvc?rev=1808851&view=rev
Log:
OAK-6675: Implement CompositeNodeStoreStatsMBean

Added:
    
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreMonitor.java
    
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStats.java
    
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStatsMBean.java
    
jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStatsTest.java
Modified:
    
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java
    
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java
    
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStore.java
    
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreService.java
    
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java
    
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/StringCache.java
    
jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositionContextTest.java

Modified: 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java?rev=1808851&r1=1808850&r2=1808851&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeBuilder.java
 Tue Sep 19 10:02:55 2017
@@ -47,6 +47,7 @@ class CompositeNodeBuilder implements No
     CompositeNodeBuilder(NodeMap<NodeBuilder> nodeBuilders, CompositionContext 
ctx) {
         this.ctx = ctx;
         this.nodeBuilders = nodeBuilders;
+        ctx.getNodeBuilderMonitor().onCreateNodeObject(getPath());
     }
 
     NodeBuilder getNodeBuilder(MountedNodeStore mns) {
@@ -208,7 +209,9 @@ class CompositeNodeBuilder implements No
     public NodeBuilder getChildNode(final String name) {
         String childPath = simpleConcat(getPath(), name);
         if (!ctx.shouldBeComposite(childPath)) {
-            return 
nodeBuilders.get(ctx.getOwningStore(childPath)).getChildNode(name);
+            MountedNodeStore mns = ctx.getOwningStore(childPath);
+            ctx.getNodeBuilderMonitor().onSwitchNodeToNative(mns.getMount());
+            return nodeBuilders.get(mns).getChildNode(name);
         }
         return new CompositeNodeBuilder(nodeBuilders.lazyApply((mns, b) -> 
b.getChildNode(name)), ctx);
     }

Modified: 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java?rev=1808851&r1=1808850&r2=1808851&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeState.java
 Tue Sep 19 10:02:55 2017
@@ -61,6 +61,7 @@ class CompositeNodeState extends Abstrac
         this.path = ctx.getPathCache().get(path);
         this.ctx = ctx;
         this.nodeStates = nodeStates;
+        ctx.getNodeStateMonitor().onCreateNodeObject(path);
     }
 
     NodeState getNodeState(MountedNodeStore mns) {
@@ -105,7 +106,9 @@ class CompositeNodeState extends Abstrac
     public NodeState getChildNode(final String name) {
         String childPath = simpleConcat(getPath(), name);
         if (!ctx.shouldBeComposite(childPath)) {
-            return 
nodeStates.get(ctx.getOwningStore(childPath)).getChildNode(name);
+            MountedNodeStore mns = ctx.getOwningStore(childPath);
+            ctx.getNodeStateMonitor().onSwitchNodeToNative(mns.getMount());
+            return nodeStates.get(mns).getChildNode(name);
         }
         NodeMap<NodeState> newNodeStates = nodeStates.lazyApply((mns, n) -> 
n.getChildNode(name));
         return new CompositeNodeState(childPath, newNodeStates, ctx);

Modified: 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStore.java?rev=1808851&r1=1808850&r2=1808851&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStore.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStore.java
 Tue Sep 19 10:02:55 2017
@@ -107,11 +107,11 @@ public class CompositeNodeStore implemen
 
     // visible for testing only
     CompositeNodeStore(MountInfoProvider mip, NodeStore globalStore, 
List<MountedNodeStore> nonDefaultStore) {
-        this(mip, globalStore, nonDefaultStore, 
Collections.<String>emptyList());
+        this(mip, globalStore, nonDefaultStore, 
Collections.<String>emptyList(), CompositeNodeStoreMonitor.EMPTY_INSTANCE, 
CompositeNodeStoreMonitor.EMPTY_INSTANCE);
     }
 
-    CompositeNodeStore(MountInfoProvider mip, NodeStore globalStore, 
List<MountedNodeStore> nonDefaultStore, List<String> ignoreReadOnlyWritePaths) {
-        this.ctx = new CompositionContext(mip, globalStore, nonDefaultStore);
+    CompositeNodeStore(MountInfoProvider mip, NodeStore globalStore, 
List<MountedNodeStore> nonDefaultStore, List<String> ignoreReadOnlyWritePaths, 
CompositeNodeStoreMonitor nodeStateMonitor, CompositeNodeStoreMonitor 
nodeBuilderMonitor) {
+        this.ctx = new CompositionContext(mip, globalStore, nonDefaultStore, 
nodeStateMonitor, nodeBuilderMonitor);
         this.ignoreReadOnlyWritePaths = new 
TreeSet<>(ignoreReadOnlyWritePaths);
         this.mergeLock = new ReentrantLock();
     }
@@ -470,6 +470,10 @@ public class CompositeNodeStore implemen
 
         private final List<String> ignoreReadOnlyWritePaths = 
Lists.newArrayList();
 
+        private CompositeNodeStoreMonitor nodeStateMonitor = 
CompositeNodeStoreMonitor.EMPTY_INSTANCE;
+
+        private CompositeNodeStoreMonitor nodeBuilderMonitor = 
CompositeNodeStoreMonitor.EMPTY_INSTANCE;
+
         private boolean partialReadOnly = true;
 
         private NodeStoreChecks checks;
@@ -484,6 +488,12 @@ public class CompositeNodeStore implemen
             return this;
         }
 
+        public Builder with(CompositeNodeStoreMonitor nodeStateMonitor, 
CompositeNodeStoreMonitor nodeBuilderMonitor) {
+            this.nodeStateMonitor = nodeStateMonitor;
+            this.nodeBuilderMonitor = nodeBuilderMonitor;
+            return this;
+        }
+
         public Builder addMount(String mountName, NodeStore store) {
             checkNotNull(store, "store");
             checkNotNull(mountName, "mountName");
@@ -511,7 +521,7 @@ public class CompositeNodeStore implemen
             if ( checks != null ) {
                 nonDefaultStores.forEach( s -> checks.check(globalStore, s));
             }
-            return new CompositeNodeStore(mip, globalStore, nonDefaultStores, 
ignoreReadOnlyWritePaths);
+            return new CompositeNodeStore(mip, globalStore, nonDefaultStores, 
ignoreReadOnlyWritePaths, nodeStateMonitor, nodeBuilderMonitor);
         }
 
         public void assertPartialMountsAreReadOnly() {

Added: 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreMonitor.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreMonitor.java?rev=1808851&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreMonitor.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreMonitor.java
 Tue Sep 19 10:02:55 2017
@@ -0,0 +1,44 @@
+/*
+ * 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.oak.composite;
+
+import org.apache.jackrabbit.oak.spi.mount.Mount;
+import org.osgi.annotation.versioning.ProviderType;
+
+@ProviderType
+public interface CompositeNodeStoreMonitor {
+
+    void onCreateNodeObject(String path);
+
+    void onSwitchNodeToNative(Mount mount);
+
+    void onAddStringCacheEntry();
+
+    CompositeNodeStoreMonitor EMPTY_INSTANCE = new CompositeNodeStoreMonitor() 
{
+        @Override
+        public void onCreateNodeObject(String path) {
+        }
+
+        @Override
+        public void onSwitchNodeToNative(Mount mount) {
+        }
+
+        @Override
+        public void onAddStringCacheEntry() {
+        }
+    };
+}

Modified: 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreService.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreService.java?rev=1808851&r1=1808850&r2=1808851&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreService.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreService.java
 Tue Sep 19 10:02:55 2017
@@ -16,6 +16,7 @@
  */
 package org.apache.jackrabbit.oak.composite;
 
+import com.google.common.io.Closer;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.ConfigurationPolicy;
@@ -37,6 +38,8 @@ import org.apache.jackrabbit.oak.spi.sta
 import org.apache.jackrabbit.oak.spi.state.NodeStoreProvider;
 import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
 import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
+import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
 import org.osgi.framework.Constants;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.component.ComponentContext;
@@ -54,7 +57,6 @@ import java.util.Map;
 import java.util.Set;
 
 import static com.google.common.collect.Sets.newIdentityHashSet;
-import static 
org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
 
 @Component(policy = ConfigurationPolicy.REQUIRE)
 public class CompositeNodeStoreService {
@@ -74,6 +76,9 @@ public class CompositeNodeStoreService {
     @Reference
     private NodeStoreChecks checks;
 
+    @Reference
+    private StatisticsProvider statisticsProvider = StatisticsProvider.NOOP;
+
     @Property(label = "Ignore read only writes",
             unbounded = PropertyUnbounded.ARRAY,
             description = "Writes to these read-only paths won't fail the 
commit"
@@ -91,13 +96,19 @@ public class CompositeNodeStoreService {
     )
     private static final String PROP_SEED_MOUNT = "seedMount";
 
+    @Property(label = "Gather path statistics",
+            description = "Whether the CompositeNodeStoreStatsMBean should 
gather information about the most popular paths (may be expensive)",
+            boolValue = false
+    )
+    private static final String PATH_STATS = "pathStats";
+
     private ComponentContext context;
 
     private Set<NodeStoreProvider> nodeStoresInUse = newIdentityHashSet();
 
     private ServiceRegistration nsReg;
 
-    private Registration checkpointReg;
+    private Closer mbeanRegistrations;
 
     private ObserverTracker observerTracker;
 
@@ -107,17 +118,20 @@ public class CompositeNodeStoreService {
 
     private String seedMount;
 
+    private boolean pathStats;
+
     @Activate
     protected void activate(ComponentContext context, Map<String, ?> config) 
throws IOException, CommitFailedException {
         this.context = context;
         ignoreReadOnlyWritePaths = 
PropertiesUtil.toStringArray(config.get(PROP_IGNORE_READ_ONLY_WRITES), new 
String[0]);
         partialReadOnly = 
PropertiesUtil.toBoolean(config.get(PROP_PARTIAL_READ_ONLY), true);
         seedMount = PropertiesUtil.toString(config.get(PROP_SEED_MOUNT), null);
+        pathStats = PropertiesUtil.toBoolean(config.get(PATH_STATS), false);
         registerCompositeNodeStore();
     }
 
     @Deactivate
-    protected void deactivate() {
+    protected void deactivate() throws IOException {
         unregisterCompositeNodeStore();
     }
 
@@ -178,6 +192,10 @@ public class CompositeNodeStoreService {
             }
         }
 
+        CompositeNodeStoreStats nodeStateStats = new 
CompositeNodeStoreStats(statisticsProvider, "NODE_STATE", pathStats);
+        CompositeNodeStoreStats nodeBuilderStats = new 
CompositeNodeStoreStats(statisticsProvider, "NODE_BUILDER", pathStats);
+        builder.with(nodeStateStats, nodeBuilderStats);
+
         Dictionary<String, Object> props = new Hashtable<String, Object>();
         props.put(Constants.SERVICE_PID, CompositeNodeStore.class.getName());
         props.put("oak.nodestore.description", new String[] { 
"nodeStoreType=compositeStore" } );
@@ -188,11 +206,23 @@ public class CompositeNodeStoreService {
         observerTracker.start(context.getBundleContext());
 
         Whiteboard whiteboard = new OsgiWhiteboard(context.getBundleContext());
-        checkpointReg = registerMBean(whiteboard,
+
+        mbeanRegistrations = Closer.create();
+        registerMBean(whiteboard,
                 CheckpointMBean.class,
                 new CompositeCheckpointMBean(store),
                 CheckpointMBean.TYPE,
                 "Composite node store checkpoint management");
+        registerMBean(whiteboard,
+                CompositeNodeStoreStatsMBean.class,
+                nodeStateStats,
+                CompositeNodeStoreStatsMBean.TYPE,
+                "Composite node store statistics (node state)");
+        registerMBean(whiteboard,
+                CompositeNodeStoreStatsMBean.class,
+                nodeBuilderStats,
+                CompositeNodeStoreStatsMBean.TYPE,
+                "Composite node store statistics (node builder)");
 
         LOG.info("Registering the composite node store");
         nsReg = context.getBundleContext().registerService(
@@ -203,6 +233,12 @@ public class CompositeNodeStoreService {
                 props);
     }
 
+    private <T> void registerMBean(Whiteboard whiteboard,
+                          Class<T> iface, T bean, String type, String name) {
+        Registration reg = WhiteboardUtils.registerMBean(whiteboard, iface, 
bean, type, name);
+        mbeanRegistrations.register(() -> reg.unregister());
+    }
+
     private boolean isGlobalNodeStore(NodeStoreWithProps ns) {
         return GLOBAL_ROLE.equals(ns.getRole());
     }
@@ -218,15 +254,15 @@ public class CompositeNodeStoreService {
         return role.substring(MOUNT_ROLE_PREFIX.length());
     }
 
-    private void unregisterCompositeNodeStore() {
+    private void unregisterCompositeNodeStore() throws IOException {
         if (nsReg != null) {
             LOG.info("Unregistering the composite node store");
             nsReg.unregister();
             nsReg = null;
         }
-        if (checkpointReg != null) {
-            checkpointReg.unregister();
-            checkpointReg = null;
+        if (mbeanRegistrations != null) {
+            mbeanRegistrations.close();
+            mbeanRegistrations = null;
         }
         if (observerTracker != null) {
             observerTracker.stop();
@@ -249,7 +285,7 @@ public class CompositeNodeStoreService {
         }
     }
 
-    protected void unbindNodeStore(NodeStoreProvider ns) {
+    protected void unbindNodeStore(NodeStoreProvider ns) throws IOException {
         Iterator<NodeStoreWithProps> it = nodeStores.iterator();
         while (it.hasNext()) {
             if (it.next().getNodeStoreProvider() == ns) {

Added: 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStats.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStats.java?rev=1808851&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStats.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStats.java
 Tue Sep 19 10:02:55 2017
@@ -0,0 +1,223 @@
+/*
+ * 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.oak.composite;
+
+import org.apache.jackrabbit.api.stats.TimeSeries;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.spi.mount.Mount;
+import org.apache.jackrabbit.oak.stats.CounterStats;
+import org.apache.jackrabbit.oak.stats.HistogramStats;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.apache.jackrabbit.oak.stats.StatsOptions;
+
+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 javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import static com.google.common.collect.Maps.newHashMap;
+import static org.apache.jackrabbit.stats.TimeSeriesStatsUtil.asCompositeData;
+
+public class CompositeNodeStoreStats implements CompositeNodeStoreStatsMBean, 
CompositeNodeStoreMonitor {
+
+    public static final String STRING_CACHE_SIZE = "STRING_CACHE_SIZE";
+
+    public static final String NODE_PATH_DEPTH = "_PATH_DEPTH";
+
+    public static final String NODE_SWITCH_TO_DEFAULT_MOUNT = 
"_SWITCH_TO_DEFAULT_MOUNT";
+
+    public static final String NODE_SWITCH_TO_NON_DEFAULT_MOUNT = 
"_SWITCH_TO_NON_DEFAULT_MOUNT";
+
+    private final StatisticsProvider statisticsProvider;
+
+    private final CounterStats stringCacheSize;
+
+    private final HistogramStats nodePathDepths;
+
+    private final CounterStats nodeSwitchToDefaultMount;
+
+    private final CounterStats nodeSwitchToNonDefaultMount;
+
+    private final Map<String, Long> nodePathCounts;
+
+    private long maxNodePathCount;
+
+    private final long nodePathCountSizeLimit;
+
+    private final long nodePathCountValueLimit;
+
+    private final boolean countPaths;
+
+    private final String prefix;
+
+    public CompositeNodeStoreStats(StatisticsProvider statisticsProvider, 
String prefix, boolean countPaths) {
+        this(statisticsProvider, prefix, countPaths, 100, Long.MAX_VALUE / 2);
+    }
+
+    public CompositeNodeStoreStats(StatisticsProvider statisticsProvider, 
String prefix, boolean countPaths, long nodePathCountSizeLimit, long 
nodePathCountValueLimit) {
+        this.statisticsProvider = statisticsProvider;
+
+        this.stringCacheSize = statisticsProvider.getCounterStats(prefix + 
STRING_CACHE_SIZE, StatsOptions.DEFAULT);
+        this.nodePathDepths = statisticsProvider.getHistogram(prefix + 
NODE_PATH_DEPTH, StatsOptions.METRICS_ONLY);
+
+        this.nodeSwitchToDefaultMount = 
statisticsProvider.getCounterStats(prefix + NODE_SWITCH_TO_DEFAULT_MOUNT, 
StatsOptions.DEFAULT);
+        this.nodeSwitchToNonDefaultMount = 
statisticsProvider.getCounterStats(prefix + NODE_SWITCH_TO_NON_DEFAULT_MOUNT, 
StatsOptions.DEFAULT);
+
+        this.nodePathCounts = newHashMap();
+        this.maxNodePathCount = 0;
+
+        this.countPaths = countPaths;
+        this.nodePathCountSizeLimit = nodePathCountSizeLimit;
+        this.nodePathCountValueLimit = nodePathCountValueLimit;
+
+        this.prefix = prefix;
+    }
+
+    @Override
+    public void onCreateNodeObject(String path) {
+        nodePathDepths.update(PathUtils.getDepth(path));
+        if (countPaths) {
+            updatePathCount(path);
+        }
+    }
+
+    @Override
+    public void onSwitchNodeToNative(Mount mount) {
+        if (mount.isDefault()) {
+            nodeSwitchToDefaultMount.inc();
+        } else {
+            nodeSwitchToNonDefaultMount.inc();
+        }
+    }
+
+    @Override
+    public void onAddStringCacheEntry() {
+        stringCacheSize.inc();
+    }
+
+    @Override
+    public CompositeData getStringCacheSize() {
+        return getCompositeData(STRING_CACHE_SIZE);
+    }
+
+    @Override
+    public CompositeData getNodeSwitchToDefaultMount() {
+        return getCompositeData(NODE_SWITCH_TO_DEFAULT_MOUNT);
+    }
+
+    @Override
+    public CompositeData getNodeSwitchToNonDefaultMount() {
+        return getCompositeData(NODE_SWITCH_TO_NON_DEFAULT_MOUNT);
+    }
+
+    private CompositeData getCompositeData(String name) {
+        return asCompositeData(getTimeSeries(prefix + name), prefix + name);
+    }
+
+    @Override
+    public TabularData getNodePathCounts() throws OpenDataException {
+        return pathsTable(nodePathCounts, "popularNodeStatePaths", "Popular 
node state paths");
+    }
+
+    private TimeSeries getTimeSeries(String name) {
+        return statisticsProvider.getStats().getTimeSeries(name, true);
+    }
+
+    private synchronized void updatePathCount(String path) {
+        long newValue = nodePathCounts.compute(path, (p, v) -> v == null ? 1 : 
v + 1);
+        boolean removeZeros = false;
+        if (newValue == 1) {
+            if (nodePathCounts.size() >= nodePathCountSizeLimit) {
+                nodePathCounts.entrySet().stream().forEach(e -> 
nodePathCounts.put(e.getKey(), e.getValue() - 1));
+                maxNodePathCount--;
+                removeZeros = true;
+            }
+        }
+        if (maxNodePathCount < newValue) {
+            maxNodePathCount = newValue;
+            if (maxNodePathCount >= nodePathCountValueLimit) {
+                nodePathCounts.entrySet().stream().forEach(e -> 
nodePathCounts.put(e.getKey(), e.getValue() / 2));
+                maxNodePathCount /= 2;
+                removeZeros = true;
+            }
+        }
+
+        if (removeZeros) {
+            Iterator<Long> it = nodePathCounts.values().iterator();
+            while (it.hasNext()) {
+                if (it.next() <= 0) {
+                    it.remove();
+                }
+            }
+        }
+    }
+
+    private TabularData pathsTable(Map<String, Long> paths, String name, 
String description) throws OpenDataException {
+        CompositeType pathRowType = new CompositeType("compositePath", "Path",
+                new String[]{"count", "path"},
+                new String[]{"count", "path"},
+                new OpenType[]{SimpleType.LONG, SimpleType.STRING});
+
+
+        TabularDataSupport tabularData = new TabularDataSupport(
+                new TabularType(name,
+                        description,
+                        pathRowType,
+                        new String[]{"path"}
+                ));
+
+        paths.entrySet()
+                .stream()
+                .sorted(Comparator.<Entry<String, 
Long>>comparingLong(Entry::getValue).reversed())
+                .map(e -> {
+                    Map<String, Object> m = newHashMap();
+                    m.put("count", e.getValue());
+                    m.put("path", e.getKey());
+                    return m;
+                })
+                .map(d -> mapToCompositeData(pathRowType, d))
+                .forEach(tabularData::put);
+
+        return tabularData;
+    }
+
+    private static CompositeData mapToCompositeData(CompositeType 
compositeType, Map<String, Object> data) {
+        try {
+            return new CompositeDataSupport(compositeType, data);
+        } catch (OpenDataException e) {
+            throw new IllegalArgumentException(e);
+        }
+    }
+
+    Map<String, Long> getNodePathCountsMap() {
+        return nodePathCounts;
+    }
+
+    long getMaxNodePathCount() {
+        return maxNodePathCount;
+    }
+}

Added: 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStatsMBean.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStatsMBean.java?rev=1808851&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStatsMBean.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStatsMBean.java
 Tue Sep 19 10:02:55 2017
@@ -0,0 +1,37 @@
+/*
+ * 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.oak.composite;
+
+import org.osgi.annotation.versioning.ProviderType;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.TabularData;
+
+@ProviderType
+public interface CompositeNodeStoreStatsMBean {
+
+    String TYPE = "CompositeNodeStoreStats";
+
+    CompositeData getStringCacheSize();
+
+    CompositeData getNodeSwitchToDefaultMount();
+
+    CompositeData getNodeSwitchToNonDefaultMount();
+
+    TabularData getNodePathCounts() throws OpenDataException;
+}

Modified: 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java?rev=1808851&r1=1808850&r2=1808851&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/CompositionContext.java
 Tue Sep 19 10:02:55 2017
@@ -52,8 +52,12 @@ class CompositionContext {
 
     private final StringCache pathCache;
 
-    CompositionContext(MountInfoProvider mip, NodeStore globalStore, 
List<MountedNodeStore> nonDefaultStores) {
-        this.pathCache = new StringCache();
+    private final CompositeNodeStoreMonitor nodeStateMonitor;
+
+    private final CompositeNodeStoreMonitor nodeBuilderMonitor;
+
+    CompositionContext(MountInfoProvider mip, NodeStore globalStore, 
List<MountedNodeStore> nonDefaultStores, CompositeNodeStoreMonitor 
nodeStateMonitor, CompositeNodeStoreMonitor nodeBuilderMonitor) {
+        this.pathCache = new StringCache().withMonitor(nodeStateMonitor);
         this.mip = mip;
         this.globalStore = new MountedNodeStore(mip.getDefaultMount(), 
globalStore);
         this.nonDefaultStores = nonDefaultStores;
@@ -64,7 +68,8 @@ class CompositionContext {
         allStores = b.build();
 
         this.nodeStoresByMount = 
allStores.stream().collect(Collectors.toMap(MountedNodeStore::getMount, 
Function.identity()));
-
+        this.nodeStateMonitor = nodeStateMonitor;
+        this.nodeBuilderMonitor = nodeBuilderMonitor;
     }
 
     MountedNodeStore getGlobalStore() {
@@ -182,4 +187,12 @@ class CompositionContext {
         return pathCache;
     }
 
+    CompositeNodeStoreMonitor getNodeStateMonitor() {
+        return nodeStateMonitor;
+    }
+
+    CompositeNodeStoreMonitor getNodeBuilderMonitor() {
+        return nodeBuilderMonitor;
+    }
+
 }

Modified: 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/StringCache.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/StringCache.java?rev=1808851&r1=1808850&r2=1808851&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/StringCache.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/main/java/org/apache/jackrabbit/oak/composite/StringCache.java
 Tue Sep 19 10:02:55 2017
@@ -36,11 +36,23 @@ public class StringCache {
 
     private final ConcurrentMap<String, String> cache = new 
ConcurrentHashMap<>(CACHE_SIZE);
 
+    private CompositeNodeStoreMonitor monitor;
+
     public String get(String path) {
         if (cache.size() >= CACHE_SIZE && !cache.containsKey(path)) {
             LOG.debug("Cache size too big. Revise your mount setup.");
             return path;
         }
-        return cache.computeIfAbsent(path, (k) -> path);
+        return cache.computeIfAbsent(path, (k) -> {
+            if (monitor != null) {
+                monitor.onAddStringCacheEntry();
+            }
+            return path;
+        });
+    }
+
+    public StringCache withMonitor(CompositeNodeStoreMonitor monitor) {
+        this.monitor = monitor;
+        return this;
     }
 }

Added: 
jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStatsTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStatsTest.java?rev=1808851&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStatsTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositeNodeStoreStatsTest.java
 Tue Sep 19 10:02:55 2017
@@ -0,0 +1,73 @@
+/*
+ * 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.oak.composite;
+
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class CompositeNodeStoreStatsTest {
+
+    @Test
+    public void testNodeCountsMaxSizeLimit() {
+        CompositeNodeStoreStats stats = new 
CompositeNodeStoreStats(StatisticsProvider.NOOP, "NODE", true, 6, 20);
+        for (int i = 0; i < 5; i++) {
+            for (int j = 0; j < i + 5; j++) {
+                stats.onCreateNodeObject("/xyz" + i);
+            }
+        }
+
+        assertEquals(9, stats.getMaxNodePathCount());
+        assertEquals(5, stats.getNodePathCountsMap().size());
+
+        stats.onCreateNodeObject("/xyz" + 6);
+
+        assertEquals(8, stats.getMaxNodePathCount());
+        assertEquals(5, stats.getNodePathCountsMap().size());
+        for (int i = 0; i < 5; i++) {
+            assertEquals("Invalid count for /xyz" + i, (long) i + 4, (long) 
stats.getNodePathCountsMap().get("/xyz" + i));
+        }
+
+        for (int i = 0; i < 3; i++) {
+            stats.onCreateNodeObject("/xyz" + 6);
+        }
+        assertEquals(5, stats.getMaxNodePathCount());
+        assertEquals(5, stats.getNodePathCountsMap().size());
+
+        stats.onCreateNodeObject("/xyz" + 6); // this should remove the 
smallest value
+        assertEquals(4, stats.getNodePathCountsMap().size());
+
+        stats.onCreateNodeObject("/xyz" + 6); // this should add the new value
+        assertEquals(5, stats.getNodePathCountsMap().size());
+    }
+
+    @Test
+    public void testNodeCountsMaxValueLimit() {
+        CompositeNodeStoreStats stats = new 
CompositeNodeStoreStats(StatisticsProvider.NOOP, "NODE", true, 5, 20);
+        for (int i = 0; i < 19; i++) {
+            stats.onCreateNodeObject("/xyz");
+        }
+        assertEquals(19, stats.getMaxNodePathCount());
+        assertEquals(19l, (long) stats.getNodePathCountsMap().get("/xyz"));
+
+        stats.onCreateNodeObject("/xyz");
+        assertEquals(10, stats.getMaxNodePathCount());
+        assertEquals(10l, (long) stats.getNodePathCountsMap().get("/xyz"));
+
+    }
+}

Modified: 
jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositionContextTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositionContextTest.java?rev=1808851&r1=1808850&r2=1808851&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositionContextTest.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-store-composite/src/test/java/org/apache/jackrabbit/oak/composite/CompositionContextTest.java
 Tue Sep 19 10:02:55 2017
@@ -41,7 +41,7 @@ public class CompositionContextTest {
                                 
"/jcr:system/rep:permissionStore/oak:mount-libs-crx.default")
                         )
                 .build();
-        CompositionContext ctx = new CompositionContext(mip, null, 
Collections.emptyList());
+        CompositionContext ctx = new CompositionContext(mip, null, 
Collections.emptyList(), CompositeNodeStoreMonitor.EMPTY_INSTANCE, 
CompositeNodeStoreMonitor.EMPTY_INSTANCE);
 
         assertTrue(ctx.shouldBeComposite("/"));
         assertTrue(ctx.shouldBeComposite("/oak:index"));


Reply via email to