Index: jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/SingleColumnQueryResultTest.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/SingleColumnQueryResultTest.java (revision ) +++ jackrabbit-core/src/test/java/org/apache/jackrabbit/core/query/lucene/SingleColumnQueryResultTest.java (revision ) @@ -0,0 +1,84 @@ +package org.apache.jackrabbit.core.query.lucene; + +import org.apache.jackrabbit.core.SessionImpl; +import org.apache.jackrabbit.core.id.ItemId; +import org.apache.jackrabbit.core.id.NodeId; +import org.apache.jackrabbit.core.security.AccessManager; +import org.apache.jackrabbit.core.session.SessionContext; +import org.apache.jackrabbit.spi.Path; +import org.apache.jackrabbit.spi.commons.query.qom.ColumnImpl; +import org.apache.lucene.search.Query; +import org.junit.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import java.util.Random; +import java.util.UUID; + +import static org.mockito.ArgumentMatchers.*; +import static org.mockito.Mockito.*; + +public class SingleColumnQueryResultTest { + + /* + * This test simulates a query result with 20000 hits and an offset of 12000, with sizeEstimate enabled. + * + * When using Jackrabbit 2.10.4 setting a breakpoint on the last line of the QueryResultImpl#collectScoreNodes + * method will show a list with 12000 offset nodes is created (and discarded immediately afterwards). You'll also + * see 12000 calls to QueryResultImpl#isAccessGranted. + * + * We have patched QueryResultImpl#getResults to not do this, which makes a big difference for performance. This + * test verifies that access checks are only performed for result nodes. + */ + @Test + public void gettingResultsWithSizeEstimateEnabledShouldNotCreateALargeOffsetNodesListAndShouldNotDoALotOfSecurityCalls() throws Exception { + /* Given */ + final AbstractQueryImpl queryImpl = mock(AbstractQueryImpl.class); + final Query query = mock(Query.class); + final Path[] orderProps = new Path[0]; + final boolean[] orderSpecs = new boolean[0]; + final String[] orderFuncs = new String[0]; + final int offset = 12000; + final boolean documentOrder = false; + final long limit = 20; + + final SessionImpl sessionImpl = mock(SessionImpl.class); + + final QueryHits queryHits = mock(QueryHits.class); + when(queryHits.nextScoreNode()).thenAnswer(new Answer() { + + private final Random random = new Random(); + private int hitCount = 20000; + private int docIdCount = 0; + + @Override + public ScoreNode answer(InvocationOnMock invocation) throws Throwable { + if (hitCount <= 0) { + return null; + } + + hitCount--; + return new ScoreNode(new NodeId(UUID.randomUUID()), random.nextFloat(), ++docIdCount); + } + }); + + final SearchIndex index = mock(SearchIndex.class); + when(index.getSizeEstimate()).thenReturn(true); + when(index.getResultFetchSize()).thenReturn(Integer.MAX_VALUE); + when(index.executeQuery(sessionImpl, queryImpl, query, orderProps, orderSpecs, orderFuncs, limit)) + .thenReturn(new FilterMultiColumnQueryHits(new QueryHitsAdapter(queryHits, QueryImpl.DEFAULT_SELECTOR_NAME))); + + final AccessManager accessManager = mock(AccessManager.class); + when(accessManager.canRead(isNull(Path.class), any(ItemId.class))).thenReturn(true); + + final SessionContext sessionContext = mock(SessionContext.class); + when(sessionContext.getAccessManager()).thenReturn(accessManager); + when(sessionContext.getSessionImpl()).thenReturn(sessionImpl); + + /* When */ + new SingleColumnQueryResult(index, sessionContext, queryImpl, query, mock(SpellSuggestion.class), new ColumnImpl[0], orderProps, orderSpecs, orderFuncs, documentOrder, offset, limit); + + /* Then */ + verify(accessManager, times((int)limit)).canRead(isNull(Path.class), any(ItemId.class)); // Was offset+limit before patching QueryResultImpl. + } +} Index: jackrabbit-core/pom.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- jackrabbit-core/pom.xml (revision 1769271) +++ jackrabbit-core/pom.xml (revision ) @@ -340,6 +340,12 @@ 1.3.149 test + + org.mockito + mockito-core + 2.2.12 + test +