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 ju...@apache.org on 2013/12/12 00:24:31 UTC

svn commit: r1550314 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins: index/property/PropertyIndexEditor.java nodetype/TypePredicate.java

Author: jukka
Date: Wed Dec 11 23:24:31 2013
New Revision: 1550314

URL: http://svn.apache.org/r1550314
Log:
OAK-1273: Reduce overhead when handling many parallel property indices

Use lazy evaluation of node type restrictions in the property index

Modified:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypePredicate.java

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java?rev=1550314&r1=1550313&r2=1550314&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexEditor.java Wed Dec 11 23:24:31 2013
@@ -18,6 +18,8 @@ package org.apache.jackrabbit.oak.plugin
 
 import static com.google.common.collect.Sets.newHashSet;
 import static java.util.Collections.singleton;
+import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
 import static org.apache.jackrabbit.oak.api.CommitFailedException.CONSTRAINT;
 import static org.apache.jackrabbit.oak.api.Type.NAME;
 import static org.apache.jackrabbit.oak.api.Type.NAMES;
@@ -76,27 +78,26 @@ class PropertyIndexEditor implements Ind
 
     private final Set<String> propertyNames;
 
+    /** Type predicate, or {@code null} if there are no type restrictions */
     private final Predicate<NodeState> typePredicate;
 
-    private final boolean unique;
-
+    /**
+     * Keys to check for uniqueness, or {@code null} for no uniqueness checks.
+     */
     private final Set<String> keysToCheckForUniqueness;
 
     /**
-     * Flag to indicate whether individual property changes should
-     * be tracked for this node.
+     * Flag to indicate whether the type of this node may have changed.
      */
-    private boolean trackChanges;
+    private boolean typeChanged;
 
     /**
-     * Matching property value keys from the before state,
-     * or {@code null} if this node is not indexed.
+     * Matching property value keys from the before state. Lazily initialized.
      */
     private Set<String> beforeKeys;
 
     /**
-     * Matching property value keys from the after state,
-     * or {@code null} if this node is not indexed.
+     * Matching property value keys from the after state. Lazily initialized.
      */
     private Set<String> afterKeys;
 
@@ -120,15 +121,13 @@ class PropertyIndexEditor implements Ind
             this.typePredicate = new TypePredicate(
                     root, definition.getNames(DECLARING_NODE_TYPES));
         } else {
-            this.typePredicate = NodeState.EXISTS;
+            this.typePredicate = null;
         }
 
         // keep track of modified keys for uniqueness checks
         if (definition.getBoolean(IndexConstants.UNIQUE_PROPERTY_NAME)) {
-            unique = true;
             this.keysToCheckForUniqueness = newHashSet();
         } else {
-            unique = false;
             this.keysToCheckForUniqueness = null;
         }
     }
@@ -140,7 +139,6 @@ class PropertyIndexEditor implements Ind
         this.definition = parent.definition;
         this.propertyNames = parent.propertyNames;
         this.typePredicate = parent.typePredicate;
-        this.unique = parent.unique;
         this.keysToCheckForUniqueness = parent.keysToCheckForUniqueness;
     }
 
@@ -154,42 +152,38 @@ class PropertyIndexEditor implements Ind
         return path;
     }
 
-    private static void addValueKeys(Set<String> keys, PropertyState property) {
-        if (property.getType().tag() != PropertyType.BINARY) {
+    /**
+     * Adds the encoded values of the given property to the given set.
+     * If the given set is uninitialized, i.e. {@code null}, then a new
+     * set is created for any values to be added. The set, possibly newly
+     * initialized, is returned.
+     *
+     * @param keys set of encoded values, or {@code null}
+     * @param property property whose values are to be added to the set
+     * @return set of encoded values, possibly initialized
+     */
+    private static Set<String> addValueKeys(
+            Set<String> keys, PropertyState property) {
+        if (property.getType().tag() != PropertyType.BINARY
+                && property.count() > 0) {
+            if (keys == null) {
+                keys = newHashSet();
+            }
             keys.addAll(encode(PropertyValues.create(property)));
         }
+        return keys;
     }
 
-    private static void addMatchingKeys(
-            Set<String> keys, NodeState state, Iterable<String> propertyNames) {
+    private static Set<String> getMatchingKeys(
+            NodeState state, Iterable<String> propertyNames) {
+        Set<String> keys = null;
         for (String propertyName : propertyNames) {
             PropertyState property = state.getProperty(propertyName);
             if (property != null) {
-                addValueKeys(keys, property);
+                keys = addValueKeys(keys, property);
             }
         }
-    }
-
-    @Override
-    public void enter(NodeState before, NodeState after)
-            throws CommitFailedException {
-        boolean beforeMatches = typePredicate.apply(before);
-        boolean afterMatches  = typePredicate.apply(after);
-
-        if (beforeMatches || afterMatches) {
-            beforeKeys = newHashSet();
-            afterKeys = newHashSet();
-        }
- 
-        if (beforeMatches && afterMatches) {
-            trackChanges = true;
-        } else if (beforeMatches) {
-            // all matching values should be removed from the index
-            addMatchingKeys(beforeKeys, before, propertyNames);
-        } else if (afterMatches) {
-            // all matching values should be added to the index
-            addMatchingKeys(afterKeys, after, propertyNames);
-        }
+        return keys;
     }
 
     private static IndexStoreStrategy getStrategy(boolean unique) {
@@ -197,20 +191,54 @@ class PropertyIndexEditor implements Ind
     }
 
     @Override
+    public void enter(NodeState before, NodeState after) {
+        typeChanged = (typePredicate == null); // disables property name checks
+        beforeKeys = null;
+        afterKeys = null;
+    }
+
+    @Override
     public void leave(NodeState before, NodeState after)
             throws CommitFailedException {
-        if (beforeKeys != null) {
-            Set<String> sharedKeys = newHashSet(beforeKeys);
-            sharedKeys.retainAll(afterKeys);
+        // apply the type restrictions
+        if (typePredicate != null) {
+            if (typeChanged) {
+                // possible type change, so ignore diff results and
+                // just load all matching values from both states
+                beforeKeys = getMatchingKeys(before, propertyNames);
+                afterKeys = getMatchingKeys(after, propertyNames);
+            }
+            if (beforeKeys != null && !typePredicate.apply(before)) {
+                // the before state doesn't match the type, so clear its values
+                beforeKeys = null;
+            }
+            if (afterKeys != null && !typePredicate.apply(after)) {
+                // the after state doesn't match the type, so clear its values
+                afterKeys = null;
+            }
+        }
 
-            beforeKeys.removeAll(sharedKeys);
-            afterKeys.removeAll(sharedKeys);
+        // if any changes were detected, update the index accordingly
+        if (beforeKeys != null || afterKeys != null) {
+            // first make sure that both the before and after sets are non-null
+            if (beforeKeys == null
+                    || (typePredicate != null && !typePredicate.apply(before))) {
+                beforeKeys = newHashSet();
+            } else if (afterKeys == null) {
+                afterKeys = newHashSet();
+            } else {
+                // both before and after matches found, remove duplicates
+                Set<String> sharedKeys = newHashSet(beforeKeys);
+                sharedKeys.retainAll(afterKeys);
+                beforeKeys.removeAll(sharedKeys);
+                afterKeys.removeAll(sharedKeys);
+            }
 
             if (!beforeKeys.isEmpty() || !afterKeys.isEmpty()) {
                 NodeBuilder index = definition.child(INDEX_CONTENT_NODE_NAME);
-
-                getStrategy(unique).update(index, getPath(), beforeKeys, afterKeys);
-                if (unique) {
+                getStrategy(keysToCheckForUniqueness != null).update(
+                        index, getPath(), beforeKeys, afterKeys);
+                if (keysToCheckForUniqueness != null) {
                     keysToCheckForUniqueness.addAll(afterKeys);
                 }
             }
@@ -221,9 +249,10 @@ class PropertyIndexEditor implements Ind
             definition.child(INDEX_CONTENT_NODE_NAME);
 
             // check uniqueness constraints when leaving the root
-            if (unique && !keysToCheckForUniqueness.isEmpty()) {
+            if (keysToCheckForUniqueness != null
+                    && !keysToCheckForUniqueness.isEmpty()) {
                 NodeState indexMeta = definition.getNodeState();
-                IndexStoreStrategy s = getStrategy(unique);
+                IndexStoreStrategy s = getStrategy(true);
                 for (String key : keysToCheckForUniqueness) {
                     if (s.count(indexMeta, singleton(key), 2) > 1) {
                         throw new CommitFailedException(
@@ -235,25 +264,35 @@ class PropertyIndexEditor implements Ind
         }
     }
 
+    private boolean isTypeProperty(String name) {
+        return JCR_PRIMARYTYPE.equals(name) || JCR_MIXINTYPES.equals(name);
+    }
+
     @Override
     public void propertyAdded(PropertyState after) {
-        if (trackChanges && propertyNames.contains(after.getName())) {
-            addValueKeys(afterKeys, after);
+        String name = after.getName();
+        typeChanged = typeChanged || isTypeProperty(name);
+        if (propertyNames.contains(name)) {
+            afterKeys = addValueKeys(afterKeys, after);
         }
     }
 
     @Override
     public void propertyChanged(PropertyState before, PropertyState after) {
-        if (trackChanges && propertyNames.contains(after.getName())) {
-            addValueKeys(beforeKeys, before);
-            addValueKeys(afterKeys, after);
+        String name = after.getName();
+        typeChanged = typeChanged || isTypeProperty(name);
+        if (propertyNames.contains(name)) {
+            beforeKeys = addValueKeys(beforeKeys, before);
+            afterKeys = addValueKeys(afterKeys, after);
         }
     }
 
     @Override
     public void propertyDeleted(PropertyState before) {
-        if (trackChanges && propertyNames.contains(before.getName())) {
-            addValueKeys(beforeKeys, before);
+        String name = before.getName();
+        typeChanged = typeChanged || isTypeProperty(name);
+        if (propertyNames.contains(name)) {
+            beforeKeys = addValueKeys(beforeKeys, before);
         }
     }
 

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypePredicate.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypePredicate.java?rev=1550314&r1=1550313&r2=1550314&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypePredicate.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/nodetype/TypePredicate.java Wed Dec 11 23:24:31 2013
@@ -21,7 +21,7 @@ import static com.google.common.base.Pre
 import static com.google.common.collect.Iterables.addAll;
 import static com.google.common.collect.Iterables.any;
 import static com.google.common.collect.Sets.newHashSet;
-import static com.google.common.collect.Sets.union;
+import static java.util.Collections.singleton;
 import static org.apache.jackrabbit.JcrConstants.JCR_ISMIXIN;
 import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
 import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
@@ -37,6 +37,7 @@ import javax.annotation.Nonnull;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 
 import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
 
 /**
  * Inheritance-aware node type predicate for {@link NodeState node states}.
@@ -45,9 +46,13 @@ import com.google.common.base.Predicate;
  */
 public class TypePredicate implements Predicate<NodeState> {
 
-    private final Set<String> primaryTypes = newHashSet();
+    private final NodeState root;
 
-    private final Set<String> mixinTypes = newHashSet();
+    private final Iterable<String> names;
+
+    private Set<String> primaryTypes = null;
+
+    private Set<String> mixinTypes = null;
 
     /**
      * Creates a predicate for checking whether a node state is an instance of
@@ -58,10 +63,7 @@ public class TypePredicate implements Pr
      * @param name Oak name of the node type to check for
      */
     public TypePredicate(@Nonnull NodeState root, @Nonnull String name) {
-        NodeState types = checkNotNull(root)
-                .getChildNode(JCR_SYSTEM)
-                .getChildNode(JCR_NODE_TYPES);
-        addNodeType(types, checkNotNull(name));
+        this(root, singleton(name));
     }
 
     /**
@@ -74,12 +76,8 @@ public class TypePredicate implements Pr
      */
     public TypePredicate(
             @Nonnull NodeState root, @Nonnull Iterable<String> names) {
-        NodeState types = checkNotNull(root)
-                .getChildNode(JCR_SYSTEM)
-                .getChildNode(JCR_NODE_TYPES);
-        for (String name : checkNotNull(names)) {
-            addNodeType(types, name);
-        }
+        this.root = root;
+        this.names = names;
     }
 
     private void addNodeType(NodeState types, String name) {
@@ -101,6 +99,18 @@ public class TypePredicate implements Pr
 
     @Override
     public boolean apply(NodeState input) {
+        if (primaryTypes == null) {
+            // lazy initialization of the sets of matching type names
+            primaryTypes = newHashSet();
+            mixinTypes = newHashSet();
+            NodeState types = checkNotNull(root)
+                    .getChildNode(JCR_SYSTEM)
+                    .getChildNode(JCR_NODE_TYPES);
+            for (String name : checkNotNull(names)) {
+                addNodeType(types, name);
+            }
+        }
+
         return primaryTypes.contains(input.getName(JCR_PRIMARYTYPE))
                 || any(input.getNames(JCR_MIXINTYPES), in(mixinTypes));
     }
@@ -109,7 +119,7 @@ public class TypePredicate implements Pr
 
     @Override
     public String toString() {
-        return union(primaryTypes, mixinTypes).toString();
+        return Iterables.toString(names);
     }
 
 }