This is an automated email from the ASF dual-hosted git repository.

baedke pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git


The following commit(s) were added to refs/heads/trunk by this push:
     new 0deaad5013 OAK-11697: Indexing / query limit / traversal error: 
improve diagnostics  (#2626)
0deaad5013 is described below

commit 0deaad501396efaabc5f245a6bfeefd9c75b84d2
Author: mbaedke <[email protected]>
AuthorDate: Wed Feb 18 17:17:32 2026 +0100

    OAK-11697: Indexing / query limit / traversal error: improve diagnostics  
(#2626)
    
    Extended logging and error message.
    
    ---------
    
    Co-authored-by: Thomas Mueller <[email protected]>
---
 .../org/apache/jackrabbit/oak/query/QueryImpl.java | 45 ++++++++++++++++++++--
 .../apache/jackrabbit/oak/jcr/query/QueryTest.java | 38 ++++++++++++++++++
 2 files changed, 80 insertions(+), 3 deletions(-)

diff --git 
a/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java 
b/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
index 3f08b4fae4..87747fbb7b 100644
--- a/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
+++ b/oak-core/src/main/java/org/apache/jackrabbit/oak/query/QueryImpl.java
@@ -14,6 +14,12 @@
 package org.apache.jackrabbit.oak.query;
 
 import static java.util.Objects.requireNonNull;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static 
org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+import static 
org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NODE_TYPE;
+import static 
org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
+import static 
org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_DISABLED;
+import static 
org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME;
 import static 
org.apache.jackrabbit.oak.query.ast.AstElementFactory.copyElementAndCheckReference;
 
 import java.math.BigInteger;
@@ -32,9 +38,11 @@ import java.util.Set;
 import java.util.stream.Collectors;
 
 import org.apache.commons.lang3.StringUtils;
+import org.apache.jackrabbit.oak.api.PropertyState;
 import org.apache.jackrabbit.oak.api.PropertyValue;
 import org.apache.jackrabbit.oak.api.Result.SizePrecision;
 import org.apache.jackrabbit.oak.api.Tree;
+import org.apache.jackrabbit.oak.api.Type;
 import org.apache.jackrabbit.oak.namepath.JcrPathParser;
 import org.apache.jackrabbit.oak.namepath.NamePathMapper;
 import org.apache.jackrabbit.oak.plugins.index.IndexUtils;
@@ -1257,7 +1265,7 @@ public class QueryImpl implements Query {
     @Override
     public void verifyNotPotentiallySlow() {
         if (potentiallySlowTraversalQuery) {
-            QueryOptions.Traversal traversal = queryOptions.traversal;
+            Traversal traversal = queryOptions.traversal;
             if (traversal == Traversal.DEFAULT) {
                 // use the (configured) default
                 traversal = settings.getFailTraversal() ? Traversal.FAIL : 
Traversal.WARN;
@@ -1265,8 +1273,7 @@ public class QueryImpl implements Query {
                 // explicitly set in the query
                 traversal = queryOptions.traversal;
             }
-            String caller = 
IndexUtils.getCaller(settings.getIgnoredClassNamesInCallTrace());
-            String message = "Traversal query (query without index): " + 
statement + "; called by " + caller + "; consider creating an index";
+            String message = createTraversalWarningMessage(traversal);
             switch (traversal) {
             case DEFAULT:
                 // not possible (changed to either FAIL or WARN above)
@@ -1285,6 +1292,38 @@ public class QueryImpl implements Query {
             }
         }
     }
+
+    private String createTraversalWarningMessage(Traversal traversal) {
+        String caller = 
IndexUtils.getCaller(settings.getIgnoredClassNamesInCallTrace());
+        String message = "Traversal query (query without index): " + statement 
+ "; called by " + caller + "; consider creating an index";
+        if (traversal == Traversal.FAIL || traversal == Traversal.WARN && 
!potentiallySlowTraversalQueryLogged) {
+            Set<String> reindex = getNamesOfReindexingIndexes();
+            message += "\n\nExecution plan:\n" + getPlan();
+            if (!reindex.isEmpty()) {
+                String reindexNames = reindex.stream().map(name -> name + 
",").collect(Collectors.joining());
+                message += "\n\nNote that the following indexes were 
re-indexing at query time:\n"
+                        + reindexNames.substring(0, reindexNames.length() - 1);
+            }
+        }
+        return message;
+    }
+
+    private Set<String> getNamesOfReindexingIndexes() {
+        Set<String> reindex = new HashSet<>();
+        Iterable<Tree> indexes = context.getRoot().getTree("/" + 
INDEX_DEFINITIONS_NAME).getChildren();
+        for (Tree index : indexes) {
+            String name = index.getName();
+            PropertyState primaryType = index.getProperty(JCR_PRIMARYTYPE);
+            if (primaryType != null && 
INDEX_DEFINITIONS_NODE_TYPE.equals(primaryType.getValue(Type.STRING))) {
+                PropertyState reindexProp = 
index.getProperty(REINDEX_PROPERTY_NAME);
+                PropertyState typeProp = index.getProperty(TYPE_PROPERTY_NAME);
+                if (reindexProp != null && reindexProp.getValue(Type.BOOLEAN) 
&& typeProp != null && !TYPE_DISABLED.equals(typeProp.getValue(Type.STRING))) {
+                    reindex.add(name);
+                }
+            }
+        }
+        return reindex;
+    }
     
     private List<OrderEntry> getSortOrder(FilterImpl filter) {
         if (orderings == null) {
diff --git 
a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java 
b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
index 04b383957c..214bd94d0a 100644
--- a/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
+++ b/oak-jcr/src/test/java/org/apache/jackrabbit/oak/jcr/query/QueryTest.java
@@ -18,6 +18,10 @@
  */
 package org.apache.jackrabbit.oak.jcr.query;
 
+import static 
org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
+import static 
org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME;
+import static 
org.apache.jackrabbit.oak.plugins.index.IndexUtils.createIndexDefinition;
+import static org.apache.jackrabbit.oak.spi.commit.CommitInfo.EMPTY;
 import static org.hamcrest.CoreMatchers.containsString;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.junit.Assert.assertEquals;
@@ -41,6 +45,7 @@ import javax.jcr.PropertyType;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.ValueFactory;
+import javax.jcr.Workspace;
 import javax.jcr.nodetype.NodeTypeManager;
 import javax.jcr.query.InvalidQueryException;
 import javax.jcr.query.Query;
@@ -60,6 +65,8 @@ import org.apache.jackrabbit.oak.commons.json.JsonObject;
 import org.apache.jackrabbit.oak.commons.json.JsopTokenizer;
 import org.apache.jackrabbit.oak.fixture.NodeStoreFixture;
 import org.apache.jackrabbit.oak.jcr.AbstractRepositoryTest;
+import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -138,6 +145,37 @@ public class QueryTest extends AbstractRepositoryTest {
         assertEquals(90, count);
     }
 
+    @Test
+    public void traversalExtendedDiagnosis() throws Exception {
+        Session session = getAdminSession();
+        Workspace workspace = session.getWorkspace();
+        NodeBuilder rootBuilder = getNodeStore().getRoot().builder();
+        createIndexDefinition(
+                rootBuilder.child(INDEX_DEFINITIONS_NAME), "foo", true, false,
+                Set.of("foo"), null);
+        getNodeStore().merge(rootBuilder, EmptyHook.INSTANCE, EMPTY);
+        session.refresh(true);
+        rootBuilder.child("a").setProperty("foo", "abc");
+        //set reindex=true without a CommitHook to simulate an ongoing 
re-indexing process.
+        
rootBuilder.child(INDEX_DEFINITIONS_NAME).child("foo").setProperty(REINDEX_PROPERTY_NAME,
 true);
+        getNodeStore().merge(rootBuilder, EmptyHook.INSTANCE, EMPTY);
+        session.refresh(true);
+        Node foo = session.getNode("/" + 
INDEX_DEFINITIONS_NAME).getNode("foo");
+        assertTrue(foo.getProperty(REINDEX_PROPERTY_NAME).getBoolean());
+        Query query = workspace.getQueryManager().createQuery("select * from 
[nt:base] where [x] = 1 or [y] = 2 option(traversal fail)", Query.JCR_SQL2);
+        try {
+            query.execute();
+            fail("traversing query should not succeed");
+        } catch (RepositoryException e) {
+            String message = e.getMessage();
+            assertTrue(message.contains("Traversal query (query without 
index)"));
+            assertTrue(message.contains("Execution plan"));
+            assertTrue(message.contains("[nt:base] as [nt:base] /* traverse"));
+            assertTrue(message.contains("Note that the following indexes were 
re-indexing at query time"));
+            assertTrue(message.contains("foo"));
+        }
+    }
+
     @Test
     public void traversalOption() throws Exception {
         Session session = getAdminSession();

Reply via email to