You are viewing a plain text version of this content. The canonical link for it is here.
Posted to oak-commits@jackrabbit.apache.org by ch...@apache.org on 2016/04/01 08:36:16 UTC

svn commit: r1737309 - in /jackrabbit/oak/trunk/oak-core/src: main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/ test/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/

Author: chetanm
Date: Fri Apr  1 06:36:16 2016
New Revision: 1737309

URL: http://svn.apache.org/viewvc?rev=1737309&view=rev
Log:
OAK-4144 - Expose PropertyIndex stats

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStats.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStatsMBean.java   (with props)
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/
    jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStatsTest.java   (with props)

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStats.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStats.java?rev=1737309&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStats.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStats.java Fri Apr  1 06:36:16 2016
@@ -0,0 +1,241 @@
+/*
+ * 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.plugins.index.property.jmx;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.Nonnull;
+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 javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.common.collect.TreeTraverser;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.commons.jmx.AnnotatedStandardMBean;
+import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard;
+import org.apache.jackrabbit.oak.plugins.tree.TreeFactory;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStateUtils;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
+import org.osgi.framework.BundleContext;
+
+import static org.apache.jackrabbit.oak.commons.PathUtils.concat;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.UNIQUE_PROPERTY_NAME;
+import static org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
+
+@Component
+public class PropertyIndexStats extends AnnotatedStandardMBean implements PropertyIndexStatsMBean {
+
+    @Reference
+    private NodeStore store;
+
+    private Registration reg;
+
+    public PropertyIndexStats() {
+        super(PropertyIndexStatsMBean.class);
+    }
+
+    @Activate
+    private void activate(BundleContext context) {
+        reg = registerMBean(new OsgiWhiteboard(context),
+                PropertyIndexStatsMBean.class,
+                this,
+                PropertyIndexStatsMBean.TYPE,
+                "Property Index statistics");
+    }
+
+    @Deactivate
+    private void deactivate() {
+        if (reg != null) {
+            reg.unregister();
+        }
+    }
+
+    @Override
+    public TabularData getStatsForAllIndexes(String path, int maxValueCount, int maxDepth, int maxPathCount)
+            throws OpenDataException {
+        String indexRootPath = concat(path, "oak:index");
+        NodeState idxRoot = NodeStateUtils.getNode(store.getRoot(), indexRootPath);
+        TabularType tt = new TabularType(PropertyIndexStats.class.getName(),
+                "Property Index Stats", getType(), new String[]{"path"});
+        TabularDataSupport tds = new TabularDataSupport(tt);
+        for(ChildNodeEntry cne : idxRoot.getChildNodeEntries()){
+            if ("property".equals(cne.getNodeState().getString("type"))) {
+                CompositeData stats = getStatsForIndex(concat(indexRootPath, cne.getName()), cne.getNodeState(),
+                        maxValueCount, maxDepth, maxPathCount);
+                tds.put(stats);
+            }
+        }
+        return tds;
+    }
+
+
+    @Override
+    public CompositeData getStatsForSpecificIndex(String path, int maxValueCount, int maxDepth, int maxPathCount) throws
+            OpenDataException {
+        NodeState idx = NodeStateUtils.getNode(store.getRoot(), path);
+        return getStatsForIndex(path, idx, maxValueCount, maxDepth, maxPathCount);
+    }
+
+    private CompositeData getStatsForIndex(String path, NodeState idx, int maxValueCount, int maxDepth, int maxPathCount)
+            throws OpenDataException {
+        Map<String, Object> result = new HashMap<String, Object>();
+
+        //Add placeholder
+        result.put("path", path);
+        result.put("values", new String[0]);
+        result.put("paths", new String[0]);
+        result.put("valueCount", -1L);
+        result.put("pathCount", -1);
+        result.put("maxPathCount", maxPathCount);
+        result.put("maxDepth", maxDepth);
+        result.put("maxValueCount", maxValueCount);
+
+        String status = "No index found at path " + path;
+        NodeState data = idx.getChildNode(INDEX_CONTENT_NODE_NAME);
+        if (data.exists()) {
+            if (idx.getBoolean(UNIQUE_PROPERTY_NAME)) {
+                status = "stats not supported for unique indexes";
+            } else {
+                long childNodeCount = data.getChildNodeCount(maxValueCount);
+                if (childNodeCount == Long.MAX_VALUE || childNodeCount > maxValueCount) {
+                    status = String.format("stats cannot be determined as number of values exceed the max limit of " +
+                            "[%d]. Estimated value count [%d]", maxValueCount, childNodeCount);
+                } else {
+                    String[] values = Iterables.toArray(
+                            Iterables.limit(data.getChildNodeNames(), maxValueCount),
+                            String.class
+                    );
+
+                    String[] paths = determineIndexedPaths(data.getChildNodeEntries(), maxDepth, maxPathCount);
+                    result.put("values", values);
+                    result.put("paths", paths);
+                    result.put("pathCount", paths.length);
+                    status = "Result determined and above path list can be safely used based on current indexed data";
+                }
+                result.put("valueCount", childNodeCount);
+            }
+        }
+
+        result.put("status", status);
+        return new CompositeDataSupport(getType(), result);
+    }
+
+    private String[] determineIndexedPaths(Iterable<? extends ChildNodeEntry> values,
+                                           final int maxDepth, int maxPathCount) {
+        Set<String> paths = Sets.newHashSet();
+        Set<String> intermediatePaths = Sets.newHashSet();
+        int maxPathLimitBreachedAtLevel = -1;
+        topLevel:
+        for (ChildNodeEntry cne : values) {
+            Tree t = TreeFactory.createReadOnlyTree(cne.getNodeState());
+            TreeTraverser<Tree> traverser = new TreeTraverser<Tree>() {
+                @Override
+                public Iterable<Tree> children(@Nonnull  Tree root) {
+                    //Break at maxLevel
+                    if (PathUtils.getDepth(root.getPath()) >= maxDepth) {
+                        return Collections.emptyList();
+                    }
+                    return root.getChildren();
+                }
+            };
+            for (Tree node : traverser.breadthFirstTraversal(t)) {
+                PropertyState matchState = node.getProperty("match");
+                boolean match = matchState == null ? false : matchState.getValue(Type.BOOLEAN);
+                int depth = PathUtils.getDepth(node.getPath());
+
+                //Intermediate nodes which are not leaf are not to be included
+                if (depth < maxDepth && !match) {
+                    intermediatePaths.add(node.getPath());
+                    continue;
+                }
+
+                if (paths.size() < maxPathCount) {
+                    paths.add(node.getPath());
+                } else {
+                    maxPathLimitBreachedAtLevel = depth;
+                    break topLevel;
+                }
+            }
+        }
+
+        if (maxPathLimitBreachedAtLevel < 0) {
+            return Iterables.toArray(paths, String.class);
+        }
+
+        //If max limit for path is reached then we can safely
+        //say about includedPaths upto depth = level at which limit reached - 1
+        //As for that level we know *all* the path roots
+        Set<String> result = Sets.newHashSet();
+        int safeDepth = maxPathLimitBreachedAtLevel - 1;
+        if (safeDepth > 0) {
+            for (String path : intermediatePaths) {
+                int pathDepth = PathUtils.getDepth(path);
+                if (pathDepth == safeDepth) {
+                    result.add(path);
+                }
+            }
+        }
+        return Iterables.toArray(result, String.class);
+    }
+
+    @SuppressWarnings("unchecked")
+    private static CompositeType getType() throws OpenDataException {
+        return new CompositeType("PropertyIndexStats", "Property index related stats",
+                new String[]{"path", "values", "paths", "valueCount", "status", "pathCount", "maxPathCount",
+                        "maxDepth", "maxValueCount"},
+                new String[]{"path", "values", "paths", "valueCount", "status", "pathCount", "maxPathCount",
+                        "maxDepth", "maxValueCount"},
+                new OpenType[]{
+                        SimpleType.STRING,
+                        new ArrayType(SimpleType.STRING, false),
+                        new ArrayType(SimpleType.STRING, false),
+                        SimpleType.LONG,
+                        SimpleType.STRING,
+                        SimpleType.INTEGER,
+                        SimpleType.INTEGER,
+                        SimpleType.INTEGER,
+                        SimpleType.INTEGER,
+                });
+
+    }
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStats.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStatsMBean.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStatsMBean.java?rev=1737309&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStatsMBean.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStatsMBean.java Fri Apr  1 06:36:16 2016
@@ -0,0 +1,77 @@
+/*
+ * 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.plugins.index.property.jmx;
+
+import javax.management.openmbean.CompositeData;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.TabularData;
+
+import org.apache.jackrabbit.oak.commons.jmx.Description;
+import org.apache.jackrabbit.oak.commons.jmx.Name;
+
+public interface PropertyIndexStatsMBean {
+    String TYPE = "PropertyIndexStats";
+
+    @Description("Determines statistics related to specific property index which can be used to optimize property index " +
+            "definition. Various limits below are provided to ensure that estimation logic does not consume too much " +
+            "resources. If any limits are reached then report would not be considered conclusive and would not have " +
+            "paths set determined")
+    CompositeData getStatsForSpecificIndex(@Name("indexPath")
+                           @Description("Index path for which stats are to be determined")
+                           String path,
+                           @Name("maxValueCount")
+                           @Description("Maximum number of values to examine. E.g. 100. Stats calculation would " +
+                                   "break out after this limit")
+                           int maxValueCount,
+                           @Description("Maximum depth to examine. E.g. 5. Stats calculation would " +
+                                   "break out after this limit")
+                           @Name("maxDepth")
+                           int maxDepth,
+                           @Description("Maximum number of unique paths to examine. E.g. 100. Stats " +
+                                   "calculation would break out after this limit")
+                           @Name("maxPathCount")
+                           int maxPathCount
+    ) throws OpenDataException;
+
+    @Description("Determines statistics related to property index for all property index under given path. Various limits " +
+            "below are provided to ensure that estimation logic does not consume too much " +
+            "resources. If any limits are reached then report would not be considered conclusive and would not have " +
+            "paths set determined")
+    TabularData getStatsForAllIndexes(
+                           @Name("path")
+                           @Description("Path under which all indexes are to be examined like '/'. Path should not " +
+                                   "include oak:index")
+                           String path,
+                           @Name("maxValueCount")
+                           @Description("Maximum number of values to examine. E.g. 100. Stats calculation would " +
+                                   "break out after this limit")
+                           int maxValueCount,
+                           @Description("Maximum depth to examine. E.g. 5. Stats calculation would " +
+                                   "break out after this limit")
+                           @Name("maxDepth")
+                           int maxDepth,
+                           @Description("Maximum number of unique paths to examine. E.g. 100. Stats " +
+                                   "calculation would break out after this limit")
+                           @Name("maxPathCount")
+                           int maxPathCount
+    ) throws OpenDataException;
+
+
+}

Propchange: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStatsMBean.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStatsTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStatsTest.java?rev=1737309&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStatsTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStatsTest.java Fri Apr  1 06:36:16 2016
@@ -0,0 +1,144 @@
+/*
+ * 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.plugins.index.property.jmx;
+
+import java.util.HashSet;
+import java.util.List;
+
+import javax.management.openmbean.CompositeData;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
+import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.plugins.nodetype.write.InitialContent;
+import org.apache.jackrabbit.oak.spi.commit.CommitInfo;
+import org.apache.jackrabbit.oak.spi.commit.EditorHook;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.sling.testing.mock.osgi.MockOsgi;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static java.util.Arrays.asList;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.IndexUtils.createIndexDefinition;
+import static org.junit.Assert.*;
+
+public class PropertyIndexStatsTest {
+    @Rule
+    public final OsgiContext context = new OsgiContext();
+
+    private static final EditorHook HOOK = new EditorHook(
+            new IndexUpdateProvider(new PropertyIndexEditorProvider()));
+
+    private NodeStore store = new MemoryNodeStore();
+
+    private PropertyIndexStats mbean = new PropertyIndexStats();
+
+    @Test
+    public void jmxReg() throws Exception{
+        activateMBean();
+
+        assertNotNull(context.getService(PropertyIndexStatsMBean.class));
+
+        MockOsgi.deactivate(mbean);
+        assertNull(context.getService(PropertyIndexStatsMBean.class));
+    }
+
+
+    @Test
+    public void statsForSpecificIndex() throws Exception{
+        prepareStore();
+
+        NodeBuilder builder = store.getRoot().builder();
+        createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME), "foo",
+                true, false, ImmutableSet.of("foo"), null);
+
+        setProperty(builder, "/a/b/c", "foo", "x");
+        setProperty(builder, "/a/e/c/d", "foo", "y");
+
+        store.merge(builder, HOOK, CommitInfo.EMPTY);
+
+        CompositeData cd = mbean.getStatsForSpecificIndex("/oak:index/foo", 5, 2, 100);
+        assertEquals(2L, cd.get("valueCount"));
+        assertArray(cd, "values", asList("x", "y"));
+        assertArray(cd, "paths", asList("/a/b", "/a/e"));
+
+        cd = mbean.getStatsForSpecificIndex("/oak:index/foo", 5, 3, 100);
+        assertArray(cd, "paths", asList("/a/b/c", "/a/e/c"));
+
+        cd = mbean.getStatsForSpecificIndex("/oak:index/foo", 5, 5, 100);
+        assertArray(cd, "paths", asList("/a/b/c", "/a/e/c/d"));
+    }
+
+    @Test
+    public void statsForSpecificIndexWithPathLimit() throws Exception{
+        prepareStore();
+
+        NodeBuilder builder = store.getRoot().builder();
+        createIndexDefinition(builder.child(INDEX_DEFINITIONS_NAME), "foo",
+                true, false, ImmutableSet.of("foo"), null);
+
+        for (int i = 0; i < 10; i++) {
+            setProperty(builder, "/a/b/c/d" + i, "foo", "x");
+        }
+
+        store.merge(builder, HOOK, CommitInfo.EMPTY);
+
+        CompositeData cd = mbean.getStatsForSpecificIndex("/oak:index/foo", 5, 7, 3);
+        assertEquals(1L, cd.get("valueCount"));
+        assertArray(cd, "values", asList("x"));
+        assertArray(cd, "paths", asList("/a/b/c"));
+    }
+
+    private static void assertArray(CompositeData cd, String prop, List<String> values){
+        String[] a = (String[])cd.get(prop);
+        assertEquals(new HashSet<String>(values), new HashSet<String>(Lists.newArrayList(a)));
+    }
+
+    private static void setProperty(NodeBuilder builder, String path, String name, String value){
+        for (String p : PathUtils.elements(path)){
+            builder = builder.child(p);
+        }
+        builder.setProperty(name, value);
+    }
+
+    private void prepareStore() throws CommitFailedException {
+        activateMBean();
+        NodeState root = store.getRoot();
+
+        NodeBuilder builder = root.builder();
+        new InitialContent().initialize(builder);
+
+        store.merge(builder, HOOK, CommitInfo.EMPTY);
+    }
+
+    private void activateMBean() {
+        context.registerService(NodeStore.class, store);
+        context.registerInjectActivateService(mbean);
+    }
+
+}
\ No newline at end of file

Propchange: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/property/jmx/PropertyIndexStatsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native