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 } }; }