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

joerghoh pushed a commit to branch OAK-11672-query-resultset
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git

commit 6d33cf4b75b76696016668b8a099dd5805b93a29
Author: Joerg Hoh <j...@adobe.com>
AuthorDate: Mon Jul 14 10:01:38 2025 +0200

    OAK-11672 log warnings when fetching large result sets
---
 .../jackrabbit/oak/jcr/query/QueryManagerImpl.java |  2 +-
 .../oak/jcr/query/QueryResultDebugIterator.java    | 83 ++++++++++++++++++++++
 .../jackrabbit/oak/jcr/query/QueryResultImpl.java  | 15 +++-
 3 files changed, 96 insertions(+), 4 deletions(-)

diff --git 
a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryManagerImpl.java
 
b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryManagerImpl.java
index b169e3ebdb..afd8744367 100644
--- 
a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryManagerImpl.java
+++ 
b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryManagerImpl.java
@@ -142,7 +142,7 @@ public class QueryManagerImpl implements QueryManager {
             queryOpsLogger.debug("Executed query [{}] in [{}] ms", statement, 
millis);
             sessionContext.getStatisticManager()
                     .logQueryEvaluationTime(language, statement, millis);
-            return new QueryResultImpl(sessionContext, r);
+            return new QueryResultImpl(sessionContext, r, statement, language);
         } catch (IllegalArgumentException e) {
             throw new InvalidQueryException(e);
         } catch (ParseException e) {
diff --git 
a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryResultDebugIterator.java
 
b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryResultDebugIterator.java
new file mode 100644
index 0000000000..ac0505cdd4
--- /dev/null
+++ 
b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryResultDebugIterator.java
@@ -0,0 +1,83 @@
+/*
+ * 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.jcr.query;
+
+import java.util.Iterator;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * An iterator which prints warnings when a certain number of elements were 
read from it.
+ * @param <K>
+ */
+
+public class QueryResultDebugIterator<K> implements Iterator<K> {
+
+    private static final Logger LOG = 
LoggerFactory.getLogger(QueryResultDebugIterator.class);
+
+    private Iterator<K> iter;
+    private int count;
+    private String query;
+    private String queryLanguage;
+
+    private static final int FIRST_LOG_THRESHOLD = 1_000;
+    private static final int SECOND_LOG_THRESHOLD = 10_000;
+
+
+    public QueryResultDebugIterator (Iterator<K> it, String query, String 
queryLanguage) {
+        this.iter = it;
+        this.query = query;
+        this.queryLanguage = queryLanguage;
+    }
+
+    @Override
+    public boolean hasNext() {
+        return iter.hasNext();
+    }
+
+    @Override
+    public K next() {
+        mark();
+        if (count >= FIRST_LOG_THRESHOLD) {
+            potentiallyLog();
+        }
+        return iter.next();
+    }
+
+    protected void mark() {
+        count++;
+    }
+
+    private void potentiallyLog() {
+        if (count == FIRST_LOG_THRESHOLD) {
+            LOG.warn("Read {} results from result set of query='{}', query 
language='{}')", count, query, queryLanguage);
+            return;
+        }
+        if (count == SECOND_LOG_THRESHOLD) {
+            LOG.warn("Read {} results from result set of query='{}', query 
language='{}')", count, query, queryLanguage);
+            return;
+        }
+        if (count > SECOND_LOG_THRESHOLD && count % 1000 == 0) {
+            LOG.trace("Read {} results from result set of query='{}', query 
language='{}')", count, query, queryLanguage);
+        }
+    }
+
+
+}
diff --git 
a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryResultImpl.java
 
b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryResultImpl.java
index 3d5de8380e..0ece0831c2 100644
--- 
a/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryResultImpl.java
+++ 
b/oak-jcr/src/main/java/org/apache/jackrabbit/oak/jcr/query/QueryResultImpl.java
@@ -61,11 +61,16 @@ public class QueryResultImpl implements QueryResult {
 
     private final PartialValueFactory valueFactory;
 
-    public QueryResultImpl(SessionContext sessionContext, Result result) {
+    private final String queryStatement;
+    private final String queryLanguage;
+
+    public QueryResultImpl(SessionContext sessionContext, Result result, 
String query, String queryLanguage) {
         this.sessionContext = sessionContext;
         this.sessionDelegate = sessionContext.getSessionDelegate();
         this.result = result;
         this.valueFactory = new PartialValueFactory(sessionContext, 
sessionContext.getBlobAccessProvider());
+        this.queryStatement = query;
+        this.queryLanguage = queryLanguage;
     }
 
     @Override
@@ -231,10 +236,14 @@ public class QueryResultImpl implements QueryResult {
                                 fastSizeCallback = result;
                             }
                         });
-        return new NodeIteratorAdapter(prefIt) {
+        final QueryResultDebugIterator<NodeImpl<? extends NodeDelegate>> 
debugIt =
+                    new QueryResultDebugIterator<>(prefIt, queryStatement, 
queryLanguage);
+
+        return new NodeIteratorAdapter(debugIt) {
             @Override
             public long getSize() {
-                return prefIt.size();
+                return prefIt.size(); // bypass the debugIterator, as it does 
not influence
+                                      // the result set
             }
         };
     }

Reply via email to