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 to...@apache.org on 2017/10/05 14:33:00 UTC
svn commit: r1811211 - in /jackrabbit/oak/trunk/oak-core/src:
main/java/org/apache/jackrabbit/oak/
main/java/org/apache/jackrabbit/oak/plugins/index/counter/
main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/
main/java/org/apache/jackrabbit...
Author: tomekr
Date: Thu Oct 5 14:32:59 2017
New Revision: 1811211
URL: http://svn.apache.org/viewvc?rev=1811211&view=rev
Log:
OAK-6579: Define how the counter index works in a composite setup
Added:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditorOld.java
- copied, changed from r1811207, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounterOld.java
- copied, changed from r1811207, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounter.java
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/counter/MountsNodeCounterTest.java
Modified:
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditorProvider.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounter.java
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/Multiplexers.java
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java?rev=1811211&r1=1811210&r2=1811211&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/Oak.java Thu Oct 5 14:32:59 2017
@@ -76,6 +76,7 @@ import org.apache.jackrabbit.oak.plugins
import org.apache.jackrabbit.oak.plugins.index.IndexUpdateProvider;
import org.apache.jackrabbit.oak.plugins.index.counter.jmx.NodeCounter;
import org.apache.jackrabbit.oak.plugins.index.counter.jmx.NodeCounterMBean;
+import org.apache.jackrabbit.oak.plugins.index.counter.jmx.NodeCounterOld;
import org.apache.jackrabbit.oak.plugins.index.property.jmx.PropertyIndexAsyncReindex;
import org.apache.jackrabbit.oak.plugins.index.property.jmx.PropertyIndexAsyncReindexMBean;
import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
@@ -694,8 +695,13 @@ public class Oak {
PropertyIndexAsyncReindexMBean.TYPE, "async"));
}
- regs.add(registerMBean(whiteboard, NodeCounterMBean.class,
- new NodeCounter(store), NodeCounterMBean.TYPE, "nodeCounter"));
+ if (NodeCounter.USE_OLD_COUNTER) {
+ regs.add(registerMBean(whiteboard, NodeCounterMBean.class,
+ new NodeCounterOld(store), NodeCounterMBean.TYPE, "nodeCounter"));
+ } else {
+ regs.add(registerMBean(whiteboard, NodeCounterMBean.class,
+ new NodeCounter(store), NodeCounterMBean.TYPE, "nodeCounter"));
+ }
regs.add(registerMBean(whiteboard, QueryEngineSettingsMBean.class,
queryEngineSettings, QueryEngineSettingsMBean.TYPE, "settings"));
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java?rev=1811211&r1=1811210&r2=1811211&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java Thu Oct 5 14:32:59 2017
@@ -23,13 +23,20 @@ import javax.annotation.CheckForNull;
import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback;
import org.apache.jackrabbit.oak.plugins.index.counter.jmx.NodeCounter;
+import org.apache.jackrabbit.oak.plugins.index.property.Multiplexers;
import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.mount.Mount;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.apache.jackrabbit.oak.commons.hash.SipHash;
+import java.util.HashMap;
+import java.util.Map;
+
/**
* An approximate descendant node counter mechanism.
*/
@@ -48,16 +55,40 @@ public class NodeCounterEditor implement
private final NodeCounterRoot root;
private final NodeCounterEditor parent;
private final String name;
- private long countOffset;
+ private final MountInfoProvider mountInfoProvider;
+ private final Map<Mount, Integer> countOffsets;
+ private final Mount currentMount;
+ private final boolean mountCanChange;
private SipHash hash;
-
- public NodeCounterEditor(NodeCounterRoot root, NodeCounterEditor parent, String name, SipHash hash) {
+
+
+ NodeCounterEditor(NodeCounterRoot root, MountInfoProvider mountInfoProvider) {
+ this.root = root;
+ this.name = "/";
+ this.parent = null;
+ this.mountInfoProvider = mountInfoProvider;
+ this.currentMount = mountInfoProvider.getDefaultMount();
+ this.mountCanChange = true;
+ this.countOffsets = new HashMap<>();
+ }
+
+ private NodeCounterEditor(NodeCounterRoot root, NodeCounterEditor parent, String name, SipHash hash, MountInfoProvider mountInfoProvider) {
this.parent = parent;
this.root = root;
this.name = name;
this.hash = hash;
- }
-
+ this.mountInfoProvider = mountInfoProvider;
+ this.countOffsets = new HashMap<>();
+ if (parent.mountCanChange) {
+ String path = getPath();
+ this.currentMount = mountInfoProvider.getMountByPath(path);
+ this.mountCanChange = currentMount.isDefault() && supportMounts(path);
+ } else {
+ this.currentMount = this.parent.currentMount;
+ this.mountCanChange = false;
+ }
+ }
+
private SipHash getHash() {
if (hash != null) {
return hash;
@@ -90,59 +121,78 @@ public class NodeCounterEditor implement
private void leaveOld(NodeState before, NodeState after)
throws CommitFailedException {
- long offset = ApproximateCounter.calculateOffset(
- countOffset, root.resolution);
- if (offset == 0) {
+ if (countOffsets.isEmpty()) {
return;
}
- // only read the value of the property if really needed
- NodeBuilder builder = getBuilder();
- PropertyState p = builder.getProperty(COUNT_PROPERTY_NAME);
- long count = p == null ? 0 : p.getValue(Type.LONG);
- offset = ApproximateCounter.adjustOffset(count,
- offset, root.resolution);
- if (offset == 0) {
- return;
- }
- count += offset;
- root.callback.indexUpdate();
- if (count == 0) {
- if (builder.getChildNodeCount(1) >= 0) {
- builder.removeProperty(COUNT_PROPERTY_NAME);
+ boolean updated = false;
+ for (Map.Entry<Mount, Integer> e : countOffsets.entrySet()) {
+ long offset = ApproximateCounter.calculateOffset(e.getValue(), root.resolution);
+ if (offset == 0) {
+ continue;
+ }
+ // only read the value of the property if really needed
+ NodeBuilder builder = getBuilder(e.getKey());
+ PropertyState p = builder.getProperty(COUNT_PROPERTY_NAME);
+ long count = p == null ? 0 : p.getValue(Type.LONG);
+ offset = ApproximateCounter.adjustOffset(count, offset, root.resolution);
+ if (offset == 0) {
+ continue;
+ }
+ updated = true;
+ count += offset;
+ if (count == 0) {
+ if (builder.getChildNodeCount(1) >= 0) {
+ builder.removeProperty(COUNT_PROPERTY_NAME);
+ } else {
+ builder.remove();
+ }
} else {
- builder.remove();
+ builder.setProperty(COUNT_PROPERTY_NAME, count);
}
- } else {
- builder.setProperty(COUNT_PROPERTY_NAME, count);
+ }
+ if (updated) {
+ root.callback.indexUpdate();
}
}
-
- public void leaveNew(NodeState before, NodeState after)
- throws CommitFailedException {
- if (countOffset == 0) {
+
+ public void leaveNew(NodeState before, NodeState after) throws CommitFailedException {
+ if (countOffsets.isEmpty()) {
return;
}
- NodeBuilder builder = getBuilder();
- PropertyState p = builder.getProperty(COUNT_HASH_PROPERTY_NAME);
- long count = p == null ? 0 : p.getValue(Type.LONG);
- count += countOffset;
root.callback.indexUpdate();
- if (count <= 0) {
- if (builder.getChildNodeCount(1) >= 0) {
- builder.removeProperty(COUNT_HASH_PROPERTY_NAME);
+ for (Map.Entry<Mount, Integer> e : countOffsets.entrySet()) {
+ NodeBuilder builder = getBuilder(e.getKey());
+ int countOffset = e.getValue();
+
+ PropertyState p = builder.getProperty(COUNT_HASH_PROPERTY_NAME);
+ long count = p == null ? 0 : p.getValue(Type.LONG);
+ count += countOffset;
+ if (count <= 0) {
+ if (builder.getChildNodeCount(1) >= 0) {
+ builder.removeProperty(COUNT_HASH_PROPERTY_NAME);
+ } else {
+ builder.remove();
+ }
} else {
- builder.remove();
+ builder.setProperty(COUNT_HASH_PROPERTY_NAME, count);
}
+ }
+ }
+
+ private NodeBuilder getBuilder(Mount mount) {
+ if (parent == null) {
+ return root.definition.child(Multiplexers.getNodeForMount(mount, DATA_NODE_NAME));
} else {
- builder.setProperty(COUNT_HASH_PROPERTY_NAME, count);
+ return parent.getBuilder(mount).child(name);
}
}
- private NodeBuilder getBuilder() {
+ private String getPath() {
if (parent == null) {
- return root.definition.child(DATA_NODE_NAME);
+ return name;
+ } else {
+ return PathUtils.concat(parent.getPath(), name);
}
- return parent.getBuilder().child(name);
}
@Override
@@ -178,11 +228,11 @@ public class NodeCounterEditor implement
// with bitMask=1024: with a probability of 1:1024,
if ((h.hashCode() & root.bitMask) == 0) {
// add 1024
- count(root.bitMask + 1);
+ count(root.bitMask + 1, currentMount);
}
- return getChildIndexEditor(name, h);
+ return getChildIndexEditor(name, h);
}
- count(1);
+ count(1, currentMount);
return getChildIndexEditor(name, null);
}
@@ -194,26 +244,33 @@ public class NodeCounterEditor implement
SipHash h = new SipHash(getHash(), name.hashCode());
// with bitMask=1024: with a probability of 1:1024,
if ((h.hashCode() & root.bitMask) == 0) {
- // subtract 1024
- count(-(root.bitMask + 1));
+ // subtract 1024
+ count(-(root.bitMask + 1), currentMount);
}
return getChildIndexEditor(name, h);
}
- count(-1);
+ count(-1, currentMount);
return getChildIndexEditor(name, null);
}
- private void count(int offset) {
- countOffset += offset;
+ private void count(int offset, Mount mount) {
+ countOffsets.compute(mount, (m, v) -> v == null ? offset : v + offset);
if (parent != null) {
- parent.count(offset);
+ parent.count(offset, mount);
}
}
private Editor getChildIndexEditor(String name, SipHash hash) {
- return new NodeCounterEditor(root, this, name, hash);
+ return new NodeCounterEditor(root, this, name, hash, mountInfoProvider);
}
-
+
+ private boolean supportMounts(String path) {
+ return mountInfoProvider
+ .getNonDefaultMounts()
+ .stream()
+ .anyMatch(m -> m.isSupportFragmentUnder(path) || m.isUnder(path));
+ }
+
public static class NodeCounterRoot {
final int resolution;
final long seed;
Copied: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditorOld.java (from r1811207, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditorOld.java?p2=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditorOld.java&p1=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java&r1=1811207&r2=1811211&rev=1811211&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditorOld.java Thu Oct 5 14:32:59 2017
@@ -33,31 +33,32 @@ import org.apache.jackrabbit.oak.commons
/**
* An approximate descendant node counter mechanism.
*/
-public class NodeCounterEditor implements Editor {
+@Deprecated
+public class NodeCounterEditorOld implements Editor {
public static final String DATA_NODE_NAME = ":index";
-
+
// the property that is used with the "old" (pseudo-random number generator based) method
public static final String COUNT_PROPERTY_NAME = ":count";
-
+
// the property that is used with the "new" (hash of the path based) method
public static final String COUNT_HASH_PROPERTY_NAME = ":cnt";
-
+
public static final int DEFAULT_RESOLUTION = 1000;
-
+
private final NodeCounterRoot root;
- private final NodeCounterEditor parent;
+ private final NodeCounterEditorOld parent;
private final String name;
private long countOffset;
private SipHash hash;
-
- public NodeCounterEditor(NodeCounterRoot root, NodeCounterEditor parent, String name, SipHash hash) {
+
+ public NodeCounterEditorOld(NodeCounterRoot root, NodeCounterEditorOld parent, String name, SipHash hash) {
this.parent = parent;
this.root = root;
this.name = name;
this.hash = hash;
- }
-
+ }
+
private SipHash getHash() {
if (hash != null) {
return hash;
@@ -87,7 +88,7 @@ public class NodeCounterEditor implement
}
leaveOld(before, after);
}
-
+
private void leaveOld(NodeState before, NodeState after)
throws CommitFailedException {
long offset = ApproximateCounter.calculateOffset(
@@ -116,9 +117,9 @@ public class NodeCounterEditor implement
builder.setProperty(COUNT_PROPERTY_NAME, count);
}
}
-
+
public void leaveNew(NodeState before, NodeState after)
- throws CommitFailedException {
+ throws CommitFailedException {
if (countOffset == 0) {
return;
}
@@ -161,7 +162,7 @@ public class NodeCounterEditor implement
throws CommitFailedException {
// nothing to do
}
-
+
@Override
@CheckForNull
public Editor childNodeChanged(String name, NodeState before, NodeState after)
@@ -180,7 +181,7 @@ public class NodeCounterEditor implement
// add 1024
count(root.bitMask + 1);
}
- return getChildIndexEditor(name, h);
+ return getChildIndexEditor(name, h);
}
count(1);
return getChildIndexEditor(name, null);
@@ -202,18 +203,18 @@ public class NodeCounterEditor implement
count(-1);
return getChildIndexEditor(name, null);
}
-
+
private void count(int offset) {
countOffset += offset;
if (parent != null) {
parent.count(offset);
}
}
-
+
private Editor getChildIndexEditor(String name, SipHash hash) {
- return new NodeCounterEditor(root, this, name, hash);
+ return new NodeCounterEditorOld(root, this, name, hash);
}
-
+
public static class NodeCounterRoot {
final int resolution;
final long seed;
@@ -221,7 +222,7 @@ public class NodeCounterEditor implement
final NodeBuilder definition;
final NodeState root;
final IndexUpdateCallback callback;
-
+
NodeCounterRoot(int resolution, long seed, NodeBuilder definition, NodeState root, IndexUpdateCallback callback) {
this.resolution = resolution;
this.seed = seed;
@@ -233,4 +234,4 @@ public class NodeCounterEditor implement
}
}
-}
+}
\ No newline at end of file
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditorProvider.java?rev=1811211&r1=1811210&r2=1811211&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditorProvider.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/NodeCounterEditorProvider.java Thu Oct 5 14:32:59 2017
@@ -28,12 +28,14 @@ import org.apache.jackrabbit.oak.api.Pro
import org.apache.jackrabbit.oak.api.Type;
import org.apache.jackrabbit.oak.plugins.index.IndexEditorProvider;
import org.apache.jackrabbit.oak.plugins.index.IndexUpdateCallback;
-import org.apache.jackrabbit.oak.plugins.index.counter.NodeCounterEditor.NodeCounterRoot;
import org.apache.jackrabbit.oak.plugins.index.counter.jmx.NodeCounter;
import org.apache.jackrabbit.oak.spi.commit.Editor;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
@Component(service = IndexEditorProvider.class)
public class NodeCounterEditorProvider implements IndexEditorProvider {
@@ -44,6 +46,9 @@ public class NodeCounterEditorProvider i
public static final String SEED = "seed";
+ @Reference
+ private MountInfoProvider mountInfoProvider = Mounts.defaultMountInfoProvider();
+
@Override
@CheckForNull
public Editor getIndexEditor(@Nonnull String type,
@@ -71,9 +76,20 @@ public class NodeCounterEditorProvider i
definition.setProperty(SEED, seed);
}
}
- NodeCounterRoot rootData = new NodeCounterRoot(
- resolution, seed, definition, root, callback);
- return new NodeCounterEditor(rootData, null, "/", null);
+
+ if (NodeCounter.USE_OLD_COUNTER) {
+ NodeCounterEditorOld.NodeCounterRoot rootData = new NodeCounterEditorOld.NodeCounterRoot(
+ resolution, seed, definition, root, callback);
+ return new NodeCounterEditorOld(rootData, null, "/", null);
+ } else {
+ NodeCounterEditor.NodeCounterRoot rootData = new NodeCounterEditor.NodeCounterRoot(
+ resolution, seed, definition, root, callback);
+ return new NodeCounterEditor(rootData, mountInfoProvider);
+ }
}
+ public NodeCounterEditorProvider with(MountInfoProvider mountInfoProvider) {
+ this.mountInfoProvider = mountInfoProvider;
+ return this;
+ }
}
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounter.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounter.java?rev=1811211&r1=1811210&r2=1811211&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounter.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounter.java Thu Oct 5 14:32:59 2017
@@ -21,6 +21,9 @@ package org.apache.jackrabbit.oak.plugin
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
+import java.util.Objects;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Type;
@@ -28,6 +31,7 @@ import org.apache.jackrabbit.oak.commons
import org.apache.jackrabbit.oak.commons.jmx.AnnotatedStandardMBean;
import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
import org.apache.jackrabbit.oak.plugins.index.counter.NodeCounterEditor;
+import org.apache.jackrabbit.oak.plugins.index.property.Multiplexers;
import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.apache.jackrabbit.oak.spi.state.NodeStore;
@@ -45,6 +49,8 @@ public class NodeCounter extends Annotat
public final static boolean COUNT_HASH =
Boolean.parseBoolean(System.getProperty("oak.countHashed", "true"));
+ public static final boolean USE_OLD_COUNTER = Boolean.getBoolean("oak.index.useCounterOld");
+
private final NodeStore store;
public NodeCounter(NodeStore store) {
@@ -85,6 +91,14 @@ public class NodeCounter extends Annotat
* (maximum) estimated number of descendant nodes
*/
public static long getEstimatedNodeCount(NodeState root, String path, boolean max) {
+ if (USE_OLD_COUNTER) {
+ return NodeCounterOld.getEstimatedNodeCount(root, path, max);
+ } else {
+ return doGetEstimatedNodeCount(root, path, max);
+ }
+ }
+
+ private static long doGetEstimatedNodeCount(NodeState root, String path, boolean max) {
// check if there is a property in the node itself
// (for property index nodes)
NodeState s = child(root,
@@ -124,36 +138,22 @@ public class NodeCounter extends Annotat
// no index
return -1;
}
- s = child(s, NodeCounterEditor.DATA_NODE_NAME);
- if (!s.exists()) {
+
+ if (!dataNodeExists(s)) {
// no index data (not yet indexed, or very few nodes)
return -1;
- }
- s = child(s, PathUtils.elements(path));
- if (s == null || !s.exists()) {
- // we have an index, but no data
- long x = 0;
- if (max) {
- // in the index, the resolution is lower
- x += ApproximateCounter.COUNT_RESOLUTION * 20;
- }
- return x;
}
- p = s.getProperty(NodeCounterEditor.COUNT_PROPERTY_NAME);
- if (p == null) {
- // we have an index, but no data
- long x = 0;
- if (max) {
- // in the index, the resolution is lower
- x += ApproximateCounter.COUNT_RESOLUTION * 20;
- }
- return x;
+
+ long sum = getIndexingData(s, path)
+ .map(n -> n.getProperty(NodeCounterEditor.COUNT_PROPERTY_NAME))
+ .filter(Objects::nonNull)
+ .mapToLong(v -> v.getValue(Type.LONG))
+ .sum();
+ if (sum == 0) {
+ return max ? ApproximateCounter.COUNT_RESOLUTION * 20 : 0;
+ } else {
+ return sum + (max ? ApproximateCounter.COUNT_RESOLUTION : 0);
}
- long x = p.getValue(Type.LONG);
- if (max) {
- x += ApproximateCounter.COUNT_RESOLUTION;
- }
- return x;
}
private static long getCombinedCount(NodeState root, String path, NodeState s, boolean max) {
@@ -169,20 +169,22 @@ public class NodeCounter extends Annotat
// no index
return -1;
}
- s = child(s, NodeCounterEditor.DATA_NODE_NAME);
- if (!s.exists()) {
+
+ if (!dataNodeExists(s)) {
// no index data (not yet indexed, or very few nodes)
return -1;
}
- s = child(s, PathUtils.elements(path));
- if (s != null && s.exists()) {
- value = getCombinedCountIfAvailable(s);
- if (value != null) {
- return value + (max ? ApproximateCounter.COUNT_RESOLUTION : 0);
- }
+
+ long sum = getIndexingData(s, path)
+ .map(NodeCounter::getCombinedCountIfAvailable)
+ .filter(Objects::nonNull)
+ .mapToLong(Long::longValue)
+ .sum();
+ if (sum == 0) {
+ return max ? ApproximateCounter.COUNT_RESOLUTION * 20 : 0;
+ } else {
+ return sum + (max ? ApproximateCounter.COUNT_RESOLUTION : 0);
}
- // we have an index, but no data
- return max ? ApproximateCounter.COUNT_RESOLUTION * 20 : 0;
}
private static Long getCombinedCountIfAvailable(NodeState s) {
@@ -236,4 +238,27 @@ public class NodeCounter extends Annotat
}
}
+ private static Stream<NodeState> getIndexingData(NodeState indexDefinition, String path) {
+ Iterable<String> pathElements = PathUtils.elements(path);
+
+ return StreamSupport.stream(indexDefinition.getChildNodeEntries().spliterator(), false)
+ .filter(NodeCounter::isDataNodeName)
+ .map(ChildNodeEntry::getNodeState)
+ .map(n -> child(n, pathElements))
+ .filter(Objects::nonNull)
+ .filter(NodeState::exists);
+ }
+
+ private static boolean isDataNodeName(ChildNodeEntry childNodeEntry) {
+ String name = childNodeEntry.getName();
+ return NodeCounterEditor.DATA_NODE_NAME.equals(name)
+ || (name.startsWith(":") && name.endsWith("-" + Multiplexers.stripStartingColon(NodeCounterEditor.DATA_NODE_NAME)));
+ }
+
+ private static boolean dataNodeExists(NodeState indexDefinition) {
+ return StreamSupport
+ .stream(indexDefinition.getChildNodeEntries().spliterator(), false)
+ .anyMatch(NodeCounter::isDataNodeName);
+ }
+
}
Copied: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounterOld.java (from r1811207, jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounter.java)
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounterOld.java?p2=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounterOld.java&p1=jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounter.java&r1=1811207&r2=1811211&rev=1811211&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounter.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/counter/jmx/NodeCounterOld.java Thu Oct 5 14:32:59 2017
@@ -36,22 +36,23 @@ import org.apache.jackrabbit.oak.plugins
/**
* A mechanism to retrieve node counter data.
*/
-public class NodeCounter extends AnnotatedStandardMBean implements NodeCounterMBean {
-
+@Deprecated
+public class NodeCounterOld extends AnnotatedStandardMBean implements NodeCounterMBean {
+
/**
* Approximate count using the hashed name (deterministically, so that after
* adding a removing all nodes the count goes back to zero).
*/
- public final static boolean COUNT_HASH =
+ public final static boolean COUNT_HASH =
Boolean.parseBoolean(System.getProperty("oak.countHashed", "true"));
-
+
private final NodeStore store;
-
- public NodeCounter(NodeStore store) {
+
+ public NodeCounterOld(NodeStore store) {
super(NodeCounterMBean.class);
this.store = store;
}
-
+
private static NodeState child(NodeState n, String... path) {
return child(n, Arrays.asList(path));
}
@@ -67,7 +68,7 @@ public class NodeCounter extends Annotat
}
return n;
}
-
+
@Override
public long getEstimatedNodeCount(String path) {
return getEstimatedNodeCount(store.getRoot(), path, false);
@@ -75,7 +76,7 @@ public class NodeCounter extends Annotat
/**
* Get the estimated number of nodes for a given path.
- *
+ *
* @param root the root
* @param path the path
* @param max whether to get the maximum expected number of nodes (the
@@ -104,7 +105,7 @@ public class NodeCounter extends Annotat
}
return getEstimatedNodeCountOld(root, s, path, max);
}
-
+
private static long getEstimatedNodeCountOld(NodeState root, NodeState s, String path, boolean max) {
// old code from here
PropertyState p = s.getProperty(NodeCounterEditor.COUNT_PROPERTY_NAME);
@@ -128,7 +129,7 @@ public class NodeCounter extends Annotat
if (!s.exists()) {
// no index data (not yet indexed, or very few nodes)
return -1;
- }
+ }
s = child(s, PathUtils.elements(path));
if (s == null || !s.exists()) {
// we have an index, but no data
@@ -152,10 +153,10 @@ public class NodeCounter extends Annotat
long x = p.getValue(Type.LONG);
if (max) {
x += ApproximateCounter.COUNT_RESOLUTION;
- }
+ }
return x;
}
-
+
private static long getCombinedCount(NodeState root, String path, NodeState s, boolean max) {
Long value = getCombinedCountIfAvailable(s);
if (value != null) {
@@ -184,7 +185,7 @@ public class NodeCounter extends Annotat
// we have an index, but no data
return max ? ApproximateCounter.COUNT_RESOLUTION * 20 : 0;
}
-
+
private static Long getCombinedCountIfAvailable(NodeState s) {
boolean found = false;
long x = 0;
@@ -200,14 +201,14 @@ public class NodeCounter extends Annotat
}
return found ? x : null;
}
-
+
@Override
public String getEstimatedChildNodeCounts(String path, int level) {
StringBuilder buff = new StringBuilder();
collectCounts(buff, path, level);
return buff.toString();
}
-
+
private void collectCounts(StringBuilder buff, String path, int level) {
long count = getEstimatedNodeCount(path);
if (count > 0) {
@@ -236,4 +237,4 @@ public class NodeCounter extends Annotat
}
}
-}
+}
\ No newline at end of file
Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/Multiplexers.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/Multiplexers.java?rev=1811211&r1=1811210&r2=1811211&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/Multiplexers.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/Multiplexers.java Thu Oct 5 14:32:59 2017
@@ -165,7 +165,7 @@ public class Multiplexers {
return "-" + stripStartingColon(name);
}
- private static String stripStartingColon(String name) {
+ public static String stripStartingColon(String name) {
if (name.startsWith(":")) {
return name.substring(1);
}
Added: jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/counter/MountsNodeCounterTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/counter/MountsNodeCounterTest.java?rev=1811211&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/counter/MountsNodeCounterTest.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/index/counter/MountsNodeCounterTest.java Thu Oct 5 14:32:59 2017
@@ -0,0 +1,174 @@
+/*
+ * 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.counter;
+
+import com.google.common.base.Predicate;
+import org.apache.jackrabbit.oak.InitialContent;
+import org.apache.jackrabbit.oak.Oak;
+import org.apache.jackrabbit.oak.api.CommitFailedException;
+import org.apache.jackrabbit.oak.api.ContentRepository;
+import org.apache.jackrabbit.oak.api.ContentSession;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Root;
+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.plugins.index.AsyncIndexUpdate;
+import org.apache.jackrabbit.oak.plugins.index.property.Multiplexers;
+import org.apache.jackrabbit.oak.plugins.index.property.PropertyIndexEditorProvider;
+import org.apache.jackrabbit.oak.plugins.memory.MemoryNodeStore;
+import org.apache.jackrabbit.oak.spi.mount.Mount;
+import org.apache.jackrabbit.oak.spi.mount.MountInfoProvider;
+import org.apache.jackrabbit.oak.spi.mount.Mounts;
+import org.apache.jackrabbit.oak.spi.security.OpenSecurityProvider;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
+import org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.annotation.Nullable;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+public class MountsNodeCounterTest {
+
+ private NodeStore nodeStore;
+
+ private Root root;
+
+ private MountInfoProvider mip;
+
+ private Whiteboard wb;
+
+ @Before
+ public void before() throws Exception {
+ ContentSession session = createRepository().login(null, null);
+ root = session.getLatestRoot();
+ }
+
+ @Test
+ public void testMultipleMounts() throws CommitFailedException {
+ root.getTree("/oak:index/counter").setProperty("resolution", 1);
+ root.commit();
+
+ Tree rootTree = root.getTree("/");
+
+ Tree apps = rootTree.addChild("apps");
+ Tree libs = rootTree.addChild("libs");
+ Tree content = rootTree.addChild("content");
+ Tree nested = rootTree.addChild("nested");
+ Tree nestedMount = nested.addChild("mount");
+ Tree fragments = rootTree.addChild("var").addChild("fragments").addChild("oak:mount-libs");
+
+ addChildren(apps, 100);
+ addChildren(libs, 200);
+ addChildren(content, 400);
+ addChildren(nested, 800);
+ addChildren(nestedMount, 1600);
+ addChildren(fragments, 3200);
+
+ root.commit();
+ runAsyncIndex();
+
+ // leaves:
+ Mount defaultMount = mip.getDefaultMount();
+ Mount libsMount = mip.getMountByName("libs");
+
+ assertCountEquals(100, libsMount, "apps");
+ assertCountEquals(200, libsMount, "libs");
+ assertCountEquals(400, defaultMount, "content");
+ assertCountEquals(800, defaultMount, "nested");
+ assertCountEquals(1600, libsMount, "nested/mount");
+ assertCountEquals(3200, libsMount, "var");
+ assertCountEquals(3200, libsMount, "var/fragments");
+ assertCountEquals(3200, libsMount, "var/fragments/oak:mount-libs");
+ assertCountEquals(0, defaultMount, "var");
+ assertCountEquals(0, defaultMount, "var/fragments");
+
+ assertCountEquals(100 + 200 + 1600 + 3200, libsMount, "");
+ assertCountEquals(1600, libsMount, "nested");
+ }
+
+ private void assertCountEquals(int expectedCount, Mount mount, String path) {
+ String p = PathUtils.concat("/oak:index/counter", Multiplexers.getNodeForMount(mount, ":index"), path);
+ NodeState s = nodeStore.getRoot();
+ for (String element : PathUtils.elements(p)) {
+ s = s.getChildNode(element);
+ if (s == null) {
+ if (expectedCount == 0) {
+ return;
+ }
+ fail("Can't find node " + p);
+ }
+ }
+ PropertyState ps = s.getProperty(":cnt");
+ if (ps == null) {
+ if (expectedCount == 0) {
+ return;
+ }
+ fail("There's no :cnt property on " + p);
+ }
+ long v = ps.getValue(Type.LONG);
+
+
+ assertTrue("expected:<" + expectedCount + "> but was:<" + v + ">", Math.abs(expectedCount - v) < 10);
+ }
+
+ private static void addChildren(Tree tree, int count) {
+ for (int i = 0; i < count; i++) {
+ tree.addChild("n-" + i);
+ }
+ }
+
+ protected ContentRepository createRepository() {
+ Mounts.Builder builder = Mounts.newBuilder();
+ builder.mount("libs", false, Arrays.asList("/var/fragments"), Arrays.asList("/apps", "/libs", "/nested/mount"));
+ mip = builder.build();
+
+ nodeStore = new MemoryNodeStore();
+ Oak oak = new Oak(nodeStore)
+ .with(new InitialContent())
+ .with(new OpenSecurityProvider())
+ .with(new PropertyIndexEditorProvider().with(mip))
+ .with(new NodeCounterEditorProvider().with(mip))
+ //Effectively disable async indexing auto run
+ //such that we can control run timing as per test requirement
+ .withAsyncIndexing("async", TimeUnit.DAYS.toSeconds(1));
+
+ wb = oak.getWhiteboard();
+ return oak.createContentRepository();
+ }
+
+ private void runAsyncIndex() {
+ Runnable async = WhiteboardUtils.getService(wb, Runnable.class, new Predicate<Runnable>() {
+ @Override
+ public boolean apply(@Nullable Runnable input) {
+ return input instanceof AsyncIndexUpdate;
+ }
+ });
+ assertNotNull(async);
+ async.run();
+ root.refresh();
+ }
+
+}