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 2020/12/17 14:39:33 UTC

svn commit: r1884554 - in /jackrabbit/oak/trunk: oak-lucene/src/test/java/org/apache/jackrabbit/oak/composite/blueGreen/ oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/

Author: thomasm
Date: Thu Dec 17 14:39:33 2020
New Revision: 1884554

URL: http://svn.apache.org/viewvc?rev=1884554&view=rev
Log:
OAK-9301 Automatically pick a merged index

Modified:
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/composite/blueGreen/CustomizedIndexTest.java
    jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/composite/blueGreen/IndexUtils.java
    jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/IndexName.java

Modified: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/composite/blueGreen/CustomizedIndexTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/composite/blueGreen/CustomizedIndexTest.java?rev=1884554&r1=1884553&r2=1884554&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/composite/blueGreen/CustomizedIndexTest.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/composite/blueGreen/CustomizedIndexTest.java Thu Dec 17 14:39:33 2020
@@ -21,6 +21,8 @@ package org.apache.jackrabbit.oak.compos
 import java.io.File;
 import java.io.IOException;
 
+import javax.jcr.Node;
+
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.TemporaryFolder;
@@ -34,13 +36,13 @@ public class CustomizedIndexTest {
 
     @Rule
     public TemporaryFolder tempDir = new TemporaryFolder(new File("target"));
-    
+
     private File globalDir;
     private File libs1Dir;
     private File libs2Dir;
     private File datastoreDir;
     private File indexDir;
-    
+
     @Test
     public void test() throws Exception {
         createFolders();
@@ -51,73 +53,120 @@ public class CustomizedIndexTest {
         compositeLibs1();
         initLibs2();
         compositeLibs2();
+        compositeWithMergedIndex();
     }
-    
+
     private void initLibs1() throws Exception {
         Persistence p = Persistence.open(libs1Dir, config);
         p.session.getRootNode().addNode("libs").addNode("test").setProperty("foo", "a");
         p.session.save();
         IndexUtils.createIndex(p, "test-1", "foo", 10);
-        IndexUtils.assertQueryUsesIndexAndReturns(p, 
-                "/jcr:root//*[@foo]", 
-                "test-1", 
+        IndexUtils.assertQueryUsesIndexAndReturns(p,
+                "/jcr:root//*[@foo]",
+                "test-1",
                 "[/libs/test]");
         p.close();
     }
-    
+
     private void initGlobal() throws Exception {
         Persistence p = Persistence.open(globalDir, config);
         p.session.getRootNode().addNode("content").addNode("test").setProperty("foo", "a");
-        p.session.save(); 
+        p.session.save();
         p.close();
     }
-    
+
     private void compositeLibs1() throws Exception {
         Persistence p = Persistence.openComposite(globalDir, libs1Dir, config);
         IndexUtils.checkLibsIsReadOnly(p);
         IndexUtils.createIndex(p, "test-1", "foo", 10);
-        IndexUtils.assertQueryUsesIndexAndReturns(p, 
-                "/jcr:root//*[@foo] order by @jcr:path", 
-                "test-1", 
+        IndexUtils.assertQueryUsesIndexAndReturns(p,
+                "/jcr:root//*[@foo] order by @jcr:path",
+                "test-1",
                 "[/content/test, /libs/test]");
         p.close();
     }
-    
+
     private void compositeLibs2() throws Exception {
         Persistence p = Persistence.openComposite(globalDir, libs2Dir, config);
         IndexUtils.checkLibsIsReadOnly(p);
+
         IndexUtils.createIndex(p, "test-2", "foo", 20);
-        IndexUtils.assertQueryUsesIndexAndReturns(p, 
-                "/jcr:root//*[@foo] order by @jcr:path", 
-                "test-2", 
+
+        // the new index must be used in the new version (with libs2)
+        IndexUtils.assertQueryUsesIndexAndReturns(p,
+                "/jcr:root//*[@foo] order by @jcr:path",
+                "test-2",
                 "[/content/test, /libs/test2]");
         p.close();
-        // the new index must not be used in the old version (wiht libs1)
+
+        // the new index must not be used in the old version (with libs1)
         p = Persistence.openComposite(globalDir, libs1Dir, config);
-        IndexUtils.assertQueryUsesIndexAndReturns(p, 
-                "/jcr:root//*[@foo] order by @jcr:path", 
-                "test-1", 
+        IndexUtils.assertQueryUsesIndexAndReturns(p,
+                "/jcr:root//*[@foo] order by @jcr:path",
+                "test-1",
                 "[/content/test, /libs/test]");
         p.close();
     }
-    
+
+    private void compositeWithMergedIndex() throws Exception {
+        // a new _merged_ index must be used by the new version (with libs2)
+        // but it won't contain the libs part (as that's not indexed)
+        Persistence p = Persistence.openComposite(globalDir, libs2Dir, config);
+        Node n = IndexUtils.createIndex(p, "test-2-custom-1", "foo", 10);
+        n.setProperty("merges", new String[]{"/oak:index/test-2"});
+        n.getSession().save();
+        IndexUtils.assertQueryUsesIndexAndReturns(p,
+                "/jcr:root//*[@foo] order by @jcr:path",
+                "test-2-custom-1",
+                "[/content/test]");
+        // no let it point to a non-existing node: the index should be used
+        n.setProperty("merges", new String[]{"/oak:index/test-does-not-exist"});
+        n.getSession().save();
+        IndexUtils.assertQueryUsesIndexAndReturns(p,
+                "/jcr:root//*[@foo] order by @jcr:path",
+                "test-2-custom-1",
+                "[/content/test]");
+        p.close();
+
+        // the merged index is not used in the old version (with libs1)
+        // because the index test-2 is not active
+        p = Persistence.openComposite(globalDir, libs1Dir, config);
+        n = p.session.getNode("/oak:index/test-2-custom-1");
+        n.setProperty("merges", new String[]{"/oak:index/test-2"});
+        n.getSession().save();
+        IndexUtils.assertQueryUsesIndexAndReturns(p,
+                "/jcr:root//*[@foo] order by @jcr:path",
+                "test-1",
+                "[/content/test, /libs/test]");
+        // no let it point to a non-existing node: the index should be used
+        n.setProperty("merges", new String[]{"/oak:index/test-does-not-exist"});
+        n.getSession().save();
+        IndexUtils.assertQueryUsesIndexAndReturns(p,
+                "/jcr:root//*[@foo] order by @jcr:path",
+                "test-2-custom-1",
+                "[/content/test]");
+        p.close();
+
+
+    }
+
     private void initLibs2() throws Exception {
         Persistence p = Persistence.open(libs2Dir, config);
         p.session.getRootNode().addNode("libs").addNode("test2").setProperty("foo", "a");
         p.session.save();
         IndexUtils.createIndex(p, "test-1", "foo", 10);
-        IndexUtils.assertQueryUsesIndexAndReturns(p, 
-                "/jcr:root//*[@foo]", 
-                "test-1", 
+        IndexUtils.assertQueryUsesIndexAndReturns(p,
+                "/jcr:root//*[@foo]",
+                "test-1",
                 "[/libs/test2]");
         IndexUtils.createIndex(p, "test-2", "foo", 20);
-        IndexUtils.assertQueryUsesIndexAndReturns(p, 
-                "/jcr:root//*[@foo]", 
-                "test-2", 
+        IndexUtils.assertQueryUsesIndexAndReturns(p,
+                "/jcr:root//*[@foo]",
+                "test-2",
                 "[/libs/test2]");
-        p.close();        
+        p.close();
     }
-    
+
     private void createFolders() throws IOException {
         globalDir = tempDir.newFolder("global");
         libs1Dir = tempDir.newFolder("libs1");
@@ -125,5 +174,5 @@ public class CustomizedIndexTest {
         datastoreDir = tempDir.newFolder("datastore");
         indexDir = tempDir.newFolder("index");
     }
-    
+
 }

Modified: jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/composite/blueGreen/IndexUtils.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/composite/blueGreen/IndexUtils.java?rev=1884554&r1=1884553&r2=1884554&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/composite/blueGreen/IndexUtils.java (original)
+++ jackrabbit/oak/trunk/oak-lucene/src/test/java/org/apache/jackrabbit/oak/composite/blueGreen/IndexUtils.java Thu Dec 17 14:39:33 2020
@@ -47,19 +47,20 @@ public class IndexUtils {
 
     /**
      * Create an index and wait until it is ready.
-     * 
+     *
      * @param p the persistence
      * @param indexName the name of the index
      * @param propertyName the property to index (on nt:base)
      * @param cost the cost per execution (high means the index isn't used if possible)
+     * @return the index definition node
      */
-    public static void createIndex(Persistence p, String indexName, String propertyName, double cost) throws RepositoryException {
+    public static Node createIndex(Persistence p, String indexName, String propertyName, double cost) throws RepositoryException {
         Node indexDef = p.session.getRootNode().getNode("oak:index");
         Node index = indexDef.addNode(indexName, INDEX_DEFINITIONS_NODE_TYPE);
         index.setProperty(TYPE_PROPERTY_NAME, LuceneIndexConstants.TYPE_LUCENE);
         index.setProperty(IndexConstants.REINDEX_PROPERTY_NAME, true);
         index.setProperty(FulltextIndexConstants.COMPAT_MODE, IndexFormatVersion.V2.getVersion());
-        index.setProperty(IndexConstants.ASYNC_PROPERTY_NAME, 
+        index.setProperty(IndexConstants.ASYNC_PROPERTY_NAME,
                 new String[] { "async", "nrt" });
         index.setProperty(FulltextIndexConstants.COST_PER_EXECUTION, cost);
         // index.setProperty("excludedPaths", "/jcr:system");
@@ -81,11 +82,12 @@ public class IndexUtils {
                 // ignore
             }
         }
+        return index;
     }
-    
+
     /**
      * Run a query and return which index was used.
-     * 
+     *
      * @param p the persistence
      * @param xpath the xpath query
      * @param expectedIndex the index that is expected to be used
@@ -111,10 +113,10 @@ public class IndexUtils {
         }
         Assert.assertEquals(expectedResult, list.toString());
     }
-    
+
     /**
      * Utility method for debugging.
-     * 
+     *
      * @param node the node to print
      */
     public static void debugPrintProperties(Node node) throws RepositoryException {
@@ -131,7 +133,7 @@ public class IndexUtils {
 
     /**
      * Check if the /libs node is read-only in this repository.
-     * 
+     *
      * @param p the persistence
      */
     public static void checkLibsIsReadOnly(Persistence p) throws RepositoryException {

Modified: jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/IndexName.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/IndexName.java?rev=1884554&r1=1884553&r2=1884554&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/IndexName.java (original)
+++ jackrabbit/oak/trunk/oak-search/src/main/java/org/apache/jackrabbit/oak/plugins/index/search/spi/query/IndexName.java Thu Dec 17 14:39:33 2020
@@ -24,7 +24,10 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 
+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.IndexConstants;
 import org.apache.jackrabbit.oak.spi.state.NodeState;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -32,19 +35,19 @@ import org.slf4j.LoggerFactory;
 /**
  * An index name, which possibly contains two version numbers: the product
  * version number, and the customer version number.
- * 
- * The format of an index node name is: 
- * - The name of the index, 
- * - optionally a dash ('-') and the product version number, 
+ *
+ * The format of an index node name is:
+ * - The name of the index,
+ * - optionally a dash ('-') and the product version number,
  * - optionally "-custom-" and the customer version number.
- * 
+ *
  * If the node name doesn't contain version numbers / dashes, then version 0 is
  * assumed (for both the product version number and customer version number).
  */
 public class IndexName implements Comparable<IndexName> {
 
     private final static Logger LOG = LoggerFactory.getLogger(IndexName.class);
-    
+
     // already logged index names
     private static final HashSet<String> LOGGED_WARN = new HashSet<>();
     // when LOGGED_WARN will be cleared
@@ -56,11 +59,11 @@ public class IndexName implements Compar
     private final int productVersion;
     private final int customerVersion;
     private final boolean isLegal;
-    
+
     /**
      * Parse the node name. Both node names with version and without version are
      * supported.
-     * 
+     *
      * @param nodeName the node name (starting from root; e.g. "/oak:index/lucene")
      * @return the index name object
      */
@@ -77,7 +80,7 @@ public class IndexName implements Compar
             if (!baseName.endsWith("-custom")) {
                 return new IndexName(nodeName, baseName, v1, 0);
             }
-            baseName = baseName.substring(0, 
+            baseName = baseName.substring(0,
                     baseName.length() - "-custom".length());
             index = baseName.lastIndexOf('-');
             if (index < 0) {
@@ -100,7 +103,7 @@ public class IndexName implements Compar
             return new IndexName(nodeName, false);
         }
     }
-    
+
     private IndexName(String nodeName, boolean isLegal) {
         // not versioned
         this.nodeName = nodeName;
@@ -120,16 +123,16 @@ public class IndexName implements Compar
         this.customerVersion = customerVersion;
         this.isLegal = true;
     }
-    
+
     public String toString() {
-        return nodeName + 
-                " base=" + baseName + 
-                (isVersioned ? " versioned": "") + 
-                " product=" + productVersion + 
+        return nodeName +
+                " base=" + baseName +
+                (isVersioned ? " versioned": "") +
+                " product=" + productVersion +
                 " custom=" + customerVersion +
                 (isLegal ? "" : " illegal");
     }
-    
+
     @Override
     public int compareTo(IndexName o) {
         int comp = baseName.compareTo(o.baseName);
@@ -187,13 +190,13 @@ public class IndexName implements Compar
     /**
      * Filter out index that are replaced by another index with the same base
      * name but newer version.
-     * 
+     *
      * Indexes without a version number in the name are always used, except if
      * there is an active index with the same base name but a newer version.
-     * 
+     *
      * Active indexes have a hidden ":oak:mount-" node, which means they are
      * indexed in the read-only node store.
-     * 
+     *
      * @param indexPaths the set of index paths
      * @param rootState the root node state (used to find hidden nodes)
      * @return the filtered list
@@ -228,6 +231,8 @@ public class IndexName implements Compar
     }
 
     private static boolean isIndexActive(String indexPath, NodeState rootState) {
+        // An index is active if it has a hidden child node that starts with ":oak:mount-",
+        // OR if it is an active merged index
         NodeState indexNode = rootState;
         for(String e : PathUtils.elements(indexPath)) {
             indexNode = indexNode.getChildNode(e);
@@ -237,7 +242,39 @@ public class IndexName implements Compar
                 return true;
             }
         }
-        return false;
+        return isIndexActiveMerged(indexNode, rootState);
+    }
+
+    private static boolean isIndexActiveMerged(NodeState indexNode, NodeState rootState) {
+        // An index is an active merged index if it has the property "merges",
+        // and that property points to index definitions,
+        // and each of those indexes is either active, disabled, or removed.
+        PropertyState ps = indexNode.getProperty("merges");
+        if (ps == null) {
+            return false;
+        }
+        if (ps.getType() != Type.STRING && ps.getType() != Type.STRINGS) {
+            return false;
+        }
+        for (int i = 0; i < ps.count(); i++) {
+            String merges = ps.getValue(Type.STRING, i);
+            NodeState mergeNode = rootState;
+            for (String e : PathUtils.elements(merges)) {
+                mergeNode = mergeNode.getChildNode(e);
+            }
+            if (!mergeNode.exists()) {
+                continue;
+            }
+            String indexType = mergeNode.getString(IndexConstants.TYPE_PROPERTY_NAME);
+            if (IndexConstants.TYPE_DISABLED.equals(indexType)) {
+                continue;
+            }
+            if (isIndexActive(merges, rootState)) {
+                continue;
+            }
+            return false;
+        }
+        return true;
     }
 
     public String getNodeName() {
@@ -263,5 +300,5 @@ public class IndexName implements Compar
     public boolean isLegal() {
         return isLegal;
     }
-    
+
 }
\ No newline at end of file