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