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 th...@apache.org on 2013/10/01 15:52:58 UTC

svn commit: r1528094 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property: PropertyIndexEditor.java PropertyIndexLookup.java strategy/UniqueEntryStoreStrategy.java

Author: thomasm
Date: Tue Oct  1 13:52:58 2013
New Revision: 1528094

URL: http://svn.apache.org/r1528094
Log:
OAK-1059 Property index: faster unique indexes using new storage strategy

Added:
    jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java
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/index/property/PropertyIndexLookup.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=1528094&r1=1528093&r2=1528094&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 Tue Oct  1 13:52:58 2013
@@ -45,6 +45,7 @@ import org.apache.jackrabbit.oak.plugins
 import org.apache.jackrabbit.oak.plugins.index.IndexEditor;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.UniqueEntryStoreStrategy;
 import org.apache.jackrabbit.oak.spi.commit.Editor;
 import org.apache.jackrabbit.oak.spi.query.PropertyValues;
 import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
@@ -59,9 +60,13 @@ import org.apache.jackrabbit.oak.spi.sta
 class PropertyIndexEditor implements IndexEditor {
 
     /** Index storage strategy */
-    private static final IndexStoreStrategy STORE =
+    private static final IndexStoreStrategy MIRROR =
             new ContentMirrorStoreStrategy();
 
+    /** Index storage strategy */
+    private static final IndexStoreStrategy UNIQUE =
+            new UniqueEntryStoreStrategy();
+
     /** Parent editor, or {@code null} if this is the root editor. */
     private final PropertyIndexEditor parent;
 
@@ -79,6 +84,8 @@ class PropertyIndexEditor implements Ind
     private final Set<String> primaryTypes;
 
     private final Set<String> mixinTypes;
+    
+    private final boolean isUnique;
 
     private final Set<String> keysToCheckForUniqueness;
 
@@ -133,8 +140,10 @@ class PropertyIndexEditor implements Ind
 
         // keep track of modified keys for uniqueness checks
         if (definition.getBoolean(IndexConstants.UNIQUE_PROPERTY_NAME)) {
+            isUnique = true;
             this.keysToCheckForUniqueness = newHashSet();
         } else {
+            isUnique = false;
             this.keysToCheckForUniqueness = null;
         }
     }
@@ -148,6 +157,7 @@ class PropertyIndexEditor implements Ind
         this.primaryTypes = parent.primaryTypes;
         this.mixinTypes = parent.mixinTypes;
         this.keysToCheckForUniqueness = parent.keysToCheckForUniqueness;
+        this.isUnique = parent.isUnique;
     }
 
     /**
@@ -217,9 +227,11 @@ class PropertyIndexEditor implements Ind
 
             if (!beforeKeys.isEmpty() || !afterKeys.isEmpty()) {
                 NodeBuilder index = definition.child(INDEX_CONTENT_NODE_NAME);
-                STORE.update(index, getPath(), beforeKeys, afterKeys);
-
-                if (keysToCheckForUniqueness != null) {
+                
+                if (keysToCheckForUniqueness == null) {
+                    MIRROR.update(index, getPath(), beforeKeys, afterKeys);
+                } else {
+                    UNIQUE.update(index, getPath(), beforeKeys, afterKeys);
                     keysToCheckForUniqueness.addAll(afterKeys);
                 }
             }
@@ -234,7 +246,7 @@ class PropertyIndexEditor implements Ind
                     && !keysToCheckForUniqueness.isEmpty()) {
                 NodeState indexMeta = definition.getNodeState();
                 for (String key : keysToCheckForUniqueness) {
-                    if (STORE.count(indexMeta, singleton(key), 2) > 1) {
+                    if (UNIQUE.count(indexMeta, singleton(key), 2) > 1) {
                         throw new CommitFailedException(
                                 CONSTRAINT, 30,
                                 "Uniqueness constraint violated for key " + key);

Modified: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java?rev=1528094&r1=1528093&r2=1528094&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java (original)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/PropertyIndexLookup.java Tue Oct  1 13:52:58 2013
@@ -34,8 +34,10 @@ import org.apache.jackrabbit.oak.api.Pro
 import org.apache.jackrabbit.oak.api.PropertyValue;
 import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.plugins.index.IndexConstants;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.ContentMirrorStoreStrategy;
 import org.apache.jackrabbit.oak.plugins.index.property.strategy.IndexStoreStrategy;
+import org.apache.jackrabbit.oak.plugins.index.property.strategy.UniqueEntryStoreStrategy;
 import org.apache.jackrabbit.oak.spi.query.Filter;
 import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
@@ -60,7 +62,13 @@ public class PropertyIndexLookup {
 
     private static final int MAX_COST = 100;
 
-    private final IndexStoreStrategy store = new ContentMirrorStoreStrategy();
+    /** Index storage strategy */
+    private static final IndexStoreStrategy MIRROR =
+            new ContentMirrorStoreStrategy();
+
+    /** Index storage strategy */
+    private static final IndexStoreStrategy UNIQUE =
+            new UniqueEntryStoreStrategy();
 
     private final NodeState root;
 
@@ -99,7 +107,14 @@ public class PropertyIndexLookup {
         if (indexMeta == null) {
             throw new IllegalArgumentException("No index for " + propertyName);
         }
-        return store.query(filter, propertyName, indexMeta, encode(value));
+        return getStrategy(indexMeta).query(filter, propertyName, indexMeta, encode(value));
+    }
+        
+    private static IndexStoreStrategy getStrategy(NodeState indexMeta) {
+        if (indexMeta.getBoolean(IndexConstants.UNIQUE_PROPERTY_NAME)) {
+            return UNIQUE;
+        }
+        return MIRROR;
     }
 
     public double getCost(Filter filter, String propertyName, PropertyValue value) {
@@ -107,7 +122,7 @@ public class PropertyIndexLookup {
         if (indexMeta == null) {
             return Double.POSITIVE_INFINITY;
         }
-        return store.count(indexMeta, encode(value), MAX_COST);
+        return getStrategy(indexMeta).count(indexMeta, encode(value), MAX_COST);
     }
 
     /**

Added: jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java?rev=1528094&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java (added)
+++ jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property/strategy/UniqueEntryStoreStrategy.java Tue Oct  1 13:52:58 2013
@@ -0,0 +1,143 @@
+/*
+ * 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.strategy;
+
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.ENTRY_COUNT_PROPERTY_NAME;
+import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_CONTENT_NODE_NAME;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.spi.query.Filter;
+import org.apache.jackrabbit.oak.spi.state.ChildNodeEntry;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An IndexStoreStrategy implementation that saves the unique node in a single property.<br>
+ * This should reduce the number of nodes in the repository, and speed up access.<br>
+ * <br>
+ * For example for a node that is under {@code /test/node}, the index
+ * structure will be {@code /oak:index/index/@key}:
+ */
+public class UniqueEntryStoreStrategy implements IndexStoreStrategy {
+
+    static final Logger LOG = LoggerFactory.getLogger(UniqueEntryStoreStrategy.class);
+
+    @Override
+    public void update(
+            NodeBuilder index, String path,
+            Set<String> beforeKeys, Set<String> afterKeys) {
+        for (String key : beforeKeys) {
+            remove(index, key, path);
+        }
+        for (String key : afterKeys) {
+            insert(index, key, path);
+        }
+    }
+
+    private static void remove(NodeBuilder index, String key, String value) {
+        NodeBuilder builder = index.getChildNode(key);
+        if (builder.exists()) {
+            builder.remove();
+        }
+    }
+
+    private static void insert(NodeBuilder index, String key, String value) {
+        NodeBuilder k = index.child(key);
+        if (k.hasProperty("entry")) {
+            // duplicate key (only temporarily set, to detect duplicate entries)
+            k.setProperty("entry2", value);
+        } else {
+            k.setProperty("entry", value);
+        }
+    }
+
+    @Override
+    public Iterable<String> query(final Filter filter, final String indexName, 
+            final NodeState indexMeta, final Iterable<String> values) {
+        final NodeState index = indexMeta.getChildNode(INDEX_CONTENT_NODE_NAME);
+        return new Iterable<String>() {
+            @Override
+            public Iterator<String> iterator() {
+                if (values == null) {
+                    return new Iterator<String>() {
+                        
+                        Iterator<? extends ChildNodeEntry> it = index.getChildNodeEntries().iterator();
+
+                        @Override
+                        public boolean hasNext() {
+                            return it.hasNext();
+                        }
+
+                        @Override
+                        public String next() {
+                            return it.next().getNodeState().getProperty("entry").getValue(Type.STRING);
+                        }
+
+                        @Override
+                        public void remove() {
+                            it.remove();
+                        }
+                        
+                    };
+                }
+                ArrayList<String> list = new ArrayList<String>();
+                for (String p : values) {
+                    NodeState key = index.getChildNode(p);
+                    if (key.exists()) {
+                        // we have an entry for this value, so use it
+                        String v = key.getProperty("entry").getValue(Type.STRING);
+                        list.add(v);
+                    }
+                }
+                return list.iterator();
+            }
+        };
+    }
+
+    @Override
+    public long count(NodeState indexMeta, Set<String> values, int max) {
+        NodeState index = indexMeta.getChildNode(INDEX_CONTENT_NODE_NAME);
+        long count = 0;
+        if (values == null) {
+            PropertyState ec = indexMeta.getProperty(ENTRY_COUNT_PROPERTY_NAME);
+            if (ec != null) {
+                return ec.getValue(Type.LONG);
+            }
+            count = index.getChildNodeCount(max);
+            // "is not null" queries typically read more data
+            count *= 10;
+        } else if (values.size() == 1) {
+            NodeState k = index.getChildNode(values.iterator().next());
+            if (k.exists()) {
+                count = k.hasProperty("entry2") ? 2 : 1;
+            } else {
+                count = 0;
+            }
+        } else {
+            count = values.size();
+        }
+        return count;
+    }
+
+}



Re: svn commit: r1528094 - in /jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/index/property: PropertyIndexEditor.java PropertyIndexLookup.java strategy/UniqueEntryStoreStrategy.java

Posted by Jukka Zitting <ju...@gmail.com>.
Hi,

On Tue, Oct 1, 2013 at 9:52 AM,  <th...@apache.org> wrote:
> URL: http://svn.apache.org/r1528094
> Log:
> OAK-1059 Property index: faster unique indexes using new storage strategy

This commit was causing the integration test failures (bot locally and
on our CI builds) shown at the end of this message, so in revision
1528295 I reverted the change.

BR,

Jukka Zitting

----

Failed tests:
testRestoreWithUUIDConflictJcr2(org.apache.jackrabbit.test.api.version.RestoreTest):
Node.restore( Version, boolean ): An ItemExistsException must be
thrown if the node to be restored already exsits and removeExisting
was set to false.

  testRestoreWithUUIDConflictJcr2_2(org.apache.jackrabbit.test.api.version.RestoreTest):
Node.restore( Version, boolean ): An ItemExistsException must be
thrown if the node to be restored already exsits and removeExisting
was set to false.

  testRestoreWithUUIDConflictJcr2_3(org.apache.jackrabbit.test.api.version.RestoreTest):
Node.restore( Version, boolean ): An ItemExistsException must be
thrown if the node to be restored already exsits and removeExisting
was set to false.

  testRestoreWithUUIDConflictJcr2_4(org.apache.jackrabbit.test.api.version.RestoreTest):
Node.restore( Version, boolean ): An ItemExistsException must be
thrown if the node to be restored already exsits and removeExisting
was set to false.

Tests in error:

  testRestoreWithUUIDConflict(org.apache.jackrabbit.test.api.version.RestoreTest):
org.apache.jackrabbit.oak.api.CommitFailedException: OakVersion0004:
VersionSelector did not select any version from history:
/jcr:system/jcr:versionStorage/20/f9/8c/20f98c44-155d-485f-bce3-cda29c8e1056