Merge "Preserve refs order in the GitBatchRefUpdateEvent event" into stable-3.6
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index a6c7da6..682239f 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -3253,6 +3253,8 @@
+
Index queries are repeated with a non-zero offset to obtain the
next set of results.
+_Note: Results may be inaccurate if the data-set is changing during the query
+execution._
+
* `SEARCH_AFTER`
+
@@ -3260,6 +3262,18 @@
backends can provide their custom implementations for search-after.
Note that, `SEARCH_AFTER` does not impact using offsets in Gerrit
query APIs.
+_Note: Depending on the index backend and its settings, results may be
+inaccurate if the data-set is changing during the query execution._
++
+* `NONE`
++
+Index queries are executed returning all results, without internal
+pagination.
+_Note: Since the entire set of indexing results is kept in memory
+during the API execution, this option may lead to higher memory utilisation
+and overall reduced performance.
+Bear in mind that some indexing backends may not support unbounded queries;
+therefore, the NONE option is unavailable._
+
Defaults to `OFFSET`.
@@ -3305,6 +3319,8 @@
For example, if the limit of the previous query was 500 and pageSizeMultiplier
is configured to 5, the next query will have a limit of 2500.
+
+_Note: ignored when paginationType is `NONE`_
++
Defaults to 1 which effectively turns this feature off.
[[index.maxPageSize]]index.maxPageSize::
@@ -3317,6 +3333,8 @@
configured to 5 and maxPageSize to 2000, the next query will have a limit of
2000 (instead of 2500).
+
+_Note: ignored when paginationType is `NONE`_
++
Defaults to no limit.
[[index.maxTerms]]index.maxTerms::
diff --git a/java/com/google/gerrit/index/PaginationType.java b/java/com/google/gerrit/index/PaginationType.java
index e7e34fd..f18f289 100644
--- a/java/com/google/gerrit/index/PaginationType.java
+++ b/java/com/google/gerrit/index/PaginationType.java
@@ -25,5 +25,8 @@
* <p>For example, Lucene implementation uses the last doc from the previous search as
* search-after object and uses the IndexSearcher.searchAfter API to get the next set of results.
*/
- SEARCH_AFTER
+ SEARCH_AFTER,
+
+ /** Index queries are executed returning all results, without internal pagination. */
+ NONE
}
diff --git a/java/com/google/gerrit/index/query/PaginatingSource.java b/java/com/google/gerrit/index/query/PaginatingSource.java
index b05c8f4..03d749a 100644
--- a/java/com/google/gerrit/index/query/PaginatingSource.java
+++ b/java/com/google/gerrit/index/query/PaginatingSource.java
@@ -63,7 +63,13 @@
pageResultSize++;
}
- if (last != null && source instanceof Paginated) {
+ if (last != null
+ && source instanceof Paginated
+ // TODO: this fix is only for the stable branches and the real refactoring would be to
+ // restore the logic
+ // for the filtering in AndSource (L58 - 64) as per
+ // https://gerrit-review.googlesource.com/c/gerrit/+/345634/9
+ && !indexConfig.paginationType().equals(PaginationType.NONE)) {
// Restart source and continue if we have not filled the
// full limit the caller wants.
//
diff --git a/java/com/google/gerrit/index/query/QueryProcessor.java b/java/com/google/gerrit/index/query/QueryProcessor.java
index 21d4c2e..b83c106 100644
--- a/java/com/google/gerrit/index/query/QueryProcessor.java
+++ b/java/com/google/gerrit/index/query/QueryProcessor.java
@@ -25,6 +25,7 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Ordering;
import com.google.common.flogger.FluentLogger;
+import com.google.common.primitives.Ints;
import com.google.gerrit.common.Nullable;
import com.google.gerrit.exceptions.StorageException;
import com.google.gerrit.index.Index;
@@ -265,7 +266,11 @@
start,
initialPageSize,
pageSizeMultiplier,
- limit,
+ // Always bump limit by 1, even if this results in exceeding the permitted
+ // max for this user. The only way to see if there are more entities is to
+ // ask for one more result from the query.
+ // NOTE: This is consistent to the behaviour before the introduction of pagination.`
+ Ints.saturatedCast((long) limit + 1),
getRequestedFields());
logger.atFine().log("Query options: %s", opts);
Predicate<T> pred = rewriter.rewrite(q, opts);
diff --git a/java/com/google/gerrit/index/testing/AbstractFakeIndex.java b/java/com/google/gerrit/index/testing/AbstractFakeIndex.java
index 4241828..8ecdae8 100644
--- a/java/com/google/gerrit/index/testing/AbstractFakeIndex.java
+++ b/java/com/google/gerrit/index/testing/AbstractFakeIndex.java
@@ -16,6 +16,7 @@
import static com.google.common.collect.ImmutableList.toImmutableList;
+import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
@@ -49,6 +50,7 @@
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import java.time.Instant;
+import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
@@ -74,6 +76,7 @@
private final String indexName;
private final Map<K, D> indexedDocuments;
private int queryCount;
+ private List<Integer> resultsSizes;
AbstractFakeIndex(Schema<V> schema, SitePaths sitePaths, String indexName) {
this.schema = schema;
@@ -81,6 +84,7 @@
this.indexName = indexName;
this.indexedDocuments = new HashMap<>();
this.queryCount = 0;
+ this.resultsSizes = new ArrayList<Integer>();
}
@Override
@@ -118,6 +122,16 @@
return queryCount;
}
+ @VisibleForTesting
+ public void resetQueryCount() {
+ queryCount = 0;
+ }
+
+ @VisibleForTesting
+ public List<Integer> getResultsSizes() {
+ return resultsSizes;
+ }
+
@Override
public DataSource<V> getSource(Predicate<V> p, QueryOptions opts) {
List<V> results;
@@ -141,6 +155,7 @@
results = valueStream.skip(opts.start()).limit(opts.pageSize()).collect(toImmutableList());
}
queryCount++;
+ resultsSizes.add(results.size());
}
return new DataSource<>() {
@Override
diff --git a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
index 586887b..ca81966 100644
--- a/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
+++ b/java/com/google/gerrit/lucene/AbstractLuceneIndex.java
@@ -25,6 +25,7 @@
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
+import com.google.common.primitives.Ints;
import com.google.common.util.concurrent.AbstractFuture;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
@@ -36,6 +37,7 @@
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.FieldType;
import com.google.gerrit.index.Index;
+import com.google.gerrit.index.PaginationType;
import com.google.gerrit.index.QueryOptions;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.Schema.Values;
@@ -420,6 +422,10 @@
return f.isStored() ? Field.Store.YES : Field.Store.NO;
}
+ static int getLimitBasedOnPaginationType(QueryOptions opts, int pagesize) {
+ return PaginationType.NONE == opts.config().paginationType() ? opts.limit() : pagesize;
+ }
+
private final class NrtFuture extends AbstractFuture<Void> {
private final long gen;
@@ -539,7 +545,9 @@
ScoreDoc scoreDoc = null;
try {
searcher = acquire();
- int realLimit = opts.start() + opts.pageSize();
+ int realLimit =
+ Ints.saturatedCast(
+ (long) getLimitBasedOnPaginationType(opts, opts.pageSize()) + opts.start());
TopFieldDocs docs =
opts.searchAfter() != null
? searcher.searchAfter(
diff --git a/java/com/google/gerrit/lucene/LuceneChangeIndex.java b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
index daa921c..f6f1eda 100644
--- a/java/com/google/gerrit/lucene/LuceneChangeIndex.java
+++ b/java/com/google/gerrit/lucene/LuceneChangeIndex.java
@@ -430,6 +430,7 @@
if (Integer.MAX_VALUE - opts.pageSize() < opts.start()) {
realPageSize = Integer.MAX_VALUE;
}
+ int queryLimit = AbstractLuceneIndex.getLimitBasedOnPaginationType(opts, realPageSize);
List<TopFieldDocs> hits = new ArrayList<>();
int searchAfterHitsCount = 0;
for (int i = 0; i < indexes.size(); i++) {
@@ -453,11 +454,10 @@
subIndex, Iterables.getLast(Arrays.asList(subIndexHits.scoreDocs), searchAfter));
}
} else {
- hits.add(searchers[i].search(query, realPageSize, sort));
+ hits.add(searchers[i].search(query, queryLimit, sort));
}
}
- TopDocs docs =
- TopDocs.merge(sort, realPageSize, hits.stream().toArray(TopFieldDocs[]::new));
+ TopDocs docs = TopDocs.merge(sort, queryLimit, hits.stream().toArray(TopFieldDocs[]::new));
List<Document> result = new ArrayList<>(docs.scoreDocs.length);
for (int i = opts.start(); i < docs.scoreDocs.length; i++) {
diff --git a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
index 081fe02..a820f86 100644
--- a/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/AbstractQueryChangesTest.java
@@ -95,6 +95,7 @@
import com.google.gerrit.httpd.raw.IndexPreloadingUtil;
import com.google.gerrit.index.FieldDef;
import com.google.gerrit.index.IndexConfig;
+import com.google.gerrit.index.PaginationType;
import com.google.gerrit.index.Schema;
import com.google.gerrit.index.query.IndexPredicate;
import com.google.gerrit.index.query.Predicate;
@@ -118,6 +119,7 @@
import com.google.gerrit.server.change.ChangeTriplet;
import com.google.gerrit.server.change.NotifyResolver;
import com.google.gerrit.server.change.PatchSetInserter;
+import com.google.gerrit.server.config.AllProjectsName;
import com.google.gerrit.server.config.AllUsersName;
import com.google.gerrit.server.experiments.ExperimentFeaturesConstants;
import com.google.gerrit.server.git.meta.MetaDataUpdate;
@@ -179,6 +181,7 @@
@Inject protected AccountManager accountManager;
@Inject protected AllUsersName allUsersName;
@Inject protected BatchUpdate.Factory updateFactory;
+ @Inject protected AllProjectsName allProjectsName;
@Inject protected ChangeInserter.Factory changeFactory;
@Inject protected Provider<ChangeQueryBuilder> queryBuilderProvider;
@Inject protected GerritApi gApi;
@@ -4635,4 +4638,8 @@
protected Schema<ChangeData> getSchema() {
return indexes.getSearchIndex().getSchema();
}
+
+ PaginationType getCurrentPaginationType() {
+ return config.getEnum("index", null, "paginationType", PaginationType.OFFSET);
+ }
}
diff --git a/javatests/com/google/gerrit/server/query/change/FakeQueryChangesLatestIndexVersionTest.java b/javatests/com/google/gerrit/server/query/change/FakeQueryChangesLatestIndexVersionTest.java
index 4dde452..df6ff5a 100644
--- a/javatests/com/google/gerrit/server/query/change/FakeQueryChangesLatestIndexVersionTest.java
+++ b/javatests/com/google/gerrit/server/query/change/FakeQueryChangesLatestIndexVersionTest.java
@@ -33,4 +33,11 @@
config.setString("index", null, "paginationType", "SEARCH_AFTER");
return config;
}
+
+ @ConfigSuite.Config
+ public static Config nonePaginationType() {
+ Config config = defaultConfig();
+ config.setString("index", null, "paginationType", "NONE");
+ return config;
+ }
}
diff --git a/javatests/com/google/gerrit/server/query/change/FakeQueryChangesPreviousIndexVersionTest.java b/javatests/com/google/gerrit/server/query/change/FakeQueryChangesPreviousIndexVersionTest.java
index 95896dc..805c99a 100644
--- a/javatests/com/google/gerrit/server/query/change/FakeQueryChangesPreviousIndexVersionTest.java
+++ b/javatests/com/google/gerrit/server/query/change/FakeQueryChangesPreviousIndexVersionTest.java
@@ -43,4 +43,11 @@
config.setString("index", null, "paginationType", "SEARCH_AFTER");
return config;
}
+
+ @ConfigSuite.Config
+ public static Config nonePaginationType() {
+ Config config = againstPreviousIndexVersion();
+ config.setString("index", null, "paginationType", "NONE");
+ return config;
+ }
}
diff --git a/javatests/com/google/gerrit/server/query/change/FakeQueryChangesTest.java b/javatests/com/google/gerrit/server/query/change/FakeQueryChangesTest.java
index 3968a33..d20a004 100644
--- a/javatests/com/google/gerrit/server/query/change/FakeQueryChangesTest.java
+++ b/javatests/com/google/gerrit/server/query/change/FakeQueryChangesTest.java
@@ -15,16 +15,24 @@
package com.google.gerrit.server.query.change;
import static com.google.common.truth.Truth.assertThat;
+import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allow;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.allowCapability;
import static com.google.gerrit.acceptance.testsuite.project.TestProjectUpdate.block;
import static com.google.gerrit.common.data.GlobalCapability.QUERY_LIMIT;
+import static com.google.gerrit.server.group.SystemGroupBackend.ANONYMOUS_USERS;
import static com.google.gerrit.server.group.SystemGroupBackend.REGISTERED_USERS;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
import com.google.gerrit.acceptance.UseClockStep;
import com.google.gerrit.entities.Permission;
import com.google.gerrit.entities.Project;
+import com.google.gerrit.extensions.common.ChangeInfo;
+import com.google.gerrit.index.PaginationType;
+import com.google.gerrit.index.query.QueryParseException;
import com.google.gerrit.index.testing.AbstractFakeIndex;
import com.google.gerrit.server.config.AllProjectsName;
+import com.google.gerrit.server.group.SystemGroupBackend;
import com.google.gerrit.server.index.change.ChangeIndexCollection;
import com.google.gerrit.testing.InMemoryModule;
import com.google.gerrit.testing.InMemoryRepositoryManager;
@@ -32,6 +40,7 @@
import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
+import java.util.List;
import org.eclipse.jgit.junit.TestRepository;
import org.eclipse.jgit.lib.Config;
import org.junit.Test;
@@ -74,44 +83,155 @@
AbstractFakeIndex idx = (AbstractFakeIndex) changeIndexCollection.getSearchIndex();
newQuery("status:new").withLimit(5).get();
- // Since the limit of the query (i.e. 5) is more than the total number of changes (i.e. 4),
- // only 1 index search is expected.
- assertThat(idx.getQueryCount()).isEqualTo(1);
+ assertThatSearchQueryWasNotPaginated(idx.getQueryCount());
}
@Test
@UseClockStep
@SuppressWarnings("unchecked")
public void noLimitQueryPaginates() throws Exception {
+ assumeFalse(PaginationType.NONE == getCurrentPaginationType());
+
TestRepository<InMemoryRepositoryManager.Repo> testRepo = createProject("repo");
// create 4 changes
insert(testRepo, newChange(testRepo));
insert(testRepo, newChange(testRepo));
insert(testRepo, newChange(testRepo));
insert(testRepo, newChange(testRepo));
-
// Set queryLimit to 2
projectOperations
.project(allProjects)
.forUpdate()
.add(allowCapability(QUERY_LIMIT).group(REGISTERED_USERS).range(0, 2))
.update();
-
AbstractFakeIndex idx = (AbstractFakeIndex) changeIndexCollection.getSearchIndex();
-
// 2 index searches are expected. The first index search will run with size 3 (i.e.
// the configured query-limit+1), and then we will paginate to get the remaining
// changes with the second index search.
newQuery("status:new").withNoLimit().get();
- assertThat(idx.getQueryCount()).isEqualTo(2);
+ assertThatSearchQueryWasPaginated(idx.getQueryCount(), 2);
+ }
+
+ @Test
+ @UseClockStep
+ public void noLimitQueryDoesNotPaginatesWithNonePaginationType() throws Exception {
+ assumeTrue(PaginationType.NONE == getCurrentPaginationType());
+ AbstractFakeIndex idx = setupRepoWithFourChanges();
+ newQuery("status:new").withNoLimit().get();
+ assertThatSearchQueryWasNotPaginated(idx.getQueryCount());
+ }
+
+ @Test
+ @UseClockStep
+ public void invisibleChangesNotPaginatedWithNonePaginationType() throws Exception {
+ assumeTrue(PaginationType.NONE == getCurrentPaginationType());
+ AbstractFakeIndex idx = setupRepoWithFourChanges();
+ final int LIMIT = 3;
+
+ projectOperations
+ .project(allProjectsName)
+ .forUpdate()
+ .removeAllAccessSections()
+ .add(allow(Permission.READ).ref("refs/*").group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
+
+ // Set queryLimit to 3
+ projectOperations
+ .project(allProjects)
+ .forUpdate()
+ .add(allowCapability(QUERY_LIMIT).group(ANONYMOUS_USERS).range(0, LIMIT))
+ .update();
+
+ requestContext.setContext(anonymousUserProvider::get);
+ List<ChangeInfo> result = newQuery("status:new").withLimit(LIMIT).get();
+ assertThat(result.size()).isEqualTo(0);
+ assertThatSearchQueryWasNotPaginated(idx.getQueryCount());
+ assertThat(idx.getResultsSizes().get(0)).isEqualTo(LIMIT + 1);
+ }
+
+ @Test
+ @UseClockStep
+ public void invisibleChangesPaginatedWithPagination() throws Exception {
+ assumeFalse(PaginationType.NONE == getCurrentPaginationType());
+
+ AbstractFakeIndex idx = setupRepoWithFourChanges();
+ final int LIMIT = 3;
+
+ projectOperations
+ .project(allProjectsName)
+ .forUpdate()
+ .removeAllAccessSections()
+ .add(allow(Permission.READ).ref("refs/*").group(SystemGroupBackend.REGISTERED_USERS))
+ .update();
+
+ projectOperations
+ .project(allProjects)
+ .forUpdate()
+ .add(allowCapability(QUERY_LIMIT).group(ANONYMOUS_USERS).range(0, LIMIT))
+ .update();
+
+ requestContext.setContext(anonymousUserProvider::get);
+ List<ChangeInfo> result = newQuery("status:new").withLimit(LIMIT).get();
+ assertThat(result.size()).isEqualTo(0);
+ assertThatSearchQueryWasPaginated(idx.getQueryCount(), 2);
+ assertThat(idx.getResultsSizes().get(0)).isEqualTo(LIMIT + 1);
+ assertThat(idx.getResultsSizes().get(1)).isEqualTo(0); // Second query size
}
@Test
@UseClockStep
@SuppressWarnings("unchecked")
public void internalQueriesPaginate() throws Exception {
+ assumeFalse(PaginationType.NONE == getCurrentPaginationType());
+ final int LIMIT = 2;
+
+ TestRepository<Repo> testRepo = createProject("repo");
// create 4 changes
- TestRepository<InMemoryRepositoryManager.Repo> testRepo = createProject("repo");
+ insert(testRepo, newChange(testRepo));
+ insert(testRepo, newChange(testRepo));
+ insert(testRepo, newChange(testRepo));
+ insert(testRepo, newChange(testRepo));
+ // Set queryLimit to 2
+ projectOperations
+ .project(allProjects)
+ .forUpdate()
+ .add(allowCapability(QUERY_LIMIT).group(REGISTERED_USERS).range(0, LIMIT))
+ .update();
+ AbstractFakeIndex idx = (AbstractFakeIndex) changeIndexCollection.getSearchIndex();
+ // 2 index searches are expected. The first index search will run with size 3 (i.e.
+ // the configured query-limit+1), and then we will paginate to get the remaining
+ // changes with the second index search.
+ queryProvider.get().query(queryBuilderProvider.get().parse("status:new"));
+ assertThat(idx.getQueryCount()).isEqualTo(LIMIT);
+ }
+
+ @Test
+ @UseClockStep
+ @SuppressWarnings("unchecked")
+ public void internalQueriesDoNotPaginateWithNonePaginationType() throws Exception {
+ assumeTrue(PaginationType.NONE == getCurrentPaginationType());
+
+ AbstractFakeIndex idx = setupRepoWithFourChanges();
+ // 1 index search is expected since we are not paginating.
+ executeQuery("status:new");
+ assertThatSearchQueryWasNotPaginated(idx.getQueryCount());
+ }
+
+ private void executeQuery(String query) throws QueryParseException {
+ queryProvider.get().query(queryBuilderProvider.get().parse(query));
+ }
+
+ private void assertThatSearchQueryWasNotPaginated(int queryCount) {
+ assertThat(queryCount).isEqualTo(1);
+ }
+
+ private void assertThatSearchQueryWasPaginated(int queryCount, int expectedPages) {
+ assertThat(queryCount).isEqualTo(expectedPages);
+ }
+
+ private AbstractFakeIndex setupRepoWithFourChanges() throws Exception {
+ TestRepository<Repo> testRepo = createProject("repo");
+ // create 4 changes
insert(testRepo, newChange(testRepo));
insert(testRepo, newChange(testRepo));
insert(testRepo, newChange(testRepo));
@@ -124,12 +244,6 @@
.add(allowCapability(QUERY_LIMIT).group(REGISTERED_USERS).range(0, 2))
.update();
- AbstractFakeIndex idx = (AbstractFakeIndex) changeIndexCollection.getSearchIndex();
-
- // 2 index searches are expected. The first index search will run with size 3 (i.e.
- // the configured query-limit+1), and then we will paginate to get the remaining
- // changes with the second index search.
- queryProvider.get().query(queryBuilderProvider.get().parse("status:new"));
- assertThat(idx.getQueryCount()).isEqualTo(2);
+ return (AbstractFakeIndex) changeIndexCollection.getSearchIndex();
}
}
diff --git a/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesLatestIndexVersionTest.java b/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesLatestIndexVersionTest.java
index 4587943..bfd1bd6 100644
--- a/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesLatestIndexVersionTest.java
+++ b/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesLatestIndexVersionTest.java
@@ -30,4 +30,11 @@
config.setString("index", null, "paginationType", "SEARCH_AFTER");
return config;
}
+
+ @ConfigSuite.Config
+ public static Config nonePaginationType() {
+ Config config = defaultConfig();
+ config.setString("index", null, "paginationType", "NONE");
+ return config;
+ }
}
diff --git a/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesPreviousIndexVersionTest.java b/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesPreviousIndexVersionTest.java
index 1782697..f93a2a7 100644
--- a/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesPreviousIndexVersionTest.java
+++ b/javatests/com/google/gerrit/server/query/change/LuceneQueryChangesPreviousIndexVersionTest.java
@@ -40,4 +40,11 @@
config.setString("index", null, "paginationType", "SEARCH_AFTER");
return config;
}
+
+ @ConfigSuite.Config
+ public static Config nonePaginationType() {
+ Config config = againstPreviousIndexVersion();
+ config.setString("index", null, "paginationType", "NONE");
+ return config;
+ }
}
diff --git a/javatests/com/google/gerrit/server/query/group/AbstractFakeQueryGroupsTest.java b/javatests/com/google/gerrit/server/query/group/AbstractFakeQueryGroupsTest.java
new file mode 100644
index 0000000..bf224f0
--- /dev/null
+++ b/javatests/com/google/gerrit/server/query/group/AbstractFakeQueryGroupsTest.java
@@ -0,0 +1,80 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed 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 com.google.gerrit.server.query.group;
+
+import static com.google.common.truth.Truth.assertThat;
+import static org.junit.Assume.assumeTrue;
+
+import com.google.gerrit.extensions.common.GroupInfo;
+import com.google.gerrit.index.PaginationType;
+import com.google.gerrit.index.testing.AbstractFakeIndex;
+import com.google.gerrit.server.index.group.GroupIndexCollection;
+import com.google.gerrit.testing.InMemoryModule;
+import com.google.inject.Guice;
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import java.util.ArrayList;
+import java.util.List;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Before;
+import org.junit.Test;
+
+public abstract class AbstractFakeQueryGroupsTest extends AbstractQueryGroupsTest {
+
+ @Inject private GroupIndexCollection groupIndexCollection;
+
+ @Override
+ protected Injector createInjector() {
+ Config fakeConfig = new Config(config);
+ InMemoryModule.setDefaults(fakeConfig);
+ fakeConfig.setString("index", null, "type", "fake");
+ return Guice.createInjector(new InMemoryModule(fakeConfig));
+ }
+
+ @Before
+ public void resetQueryCount() {
+ ((AbstractFakeIndex<?, ?, ?>) groupIndexCollection.getSearchIndex()).resetQueryCount();
+ }
+
+ @Test
+ public void internalQueriesDoNotPaginateWithNonePaginationType() throws Exception {
+ assumeTrue(PaginationType.NONE == getCurrentPaginationType());
+
+ final int GROUPS_CREATED_SIZE = 2;
+ List<GroupInfo> groupsCreated = new ArrayList<>();
+ for (int i = 0; i < GROUPS_CREATED_SIZE; i++) {
+ groupsCreated.add(createGroupThatIsVisibleToAll(name("group-" + i)));
+ }
+
+ List<GroupInfo> result = assertQuery(newQuery("is:visibletoall"), groupsCreated);
+ assertThat(result.size()).isEqualTo(GROUPS_CREATED_SIZE);
+ assertThat(result.get(result.size() - 1)._moreGroups).isNull();
+ assertThatSearchQueryWasNotPaginated();
+ }
+
+ PaginationType getCurrentPaginationType() {
+ return config.getEnum("index", null, "paginationType", PaginationType.OFFSET);
+ }
+
+ private void assertThatSearchQueryWasNotPaginated() {
+ assertThat(getQueryCount()).isEqualTo(1);
+ }
+
+ private int getQueryCount() {
+ AbstractFakeIndex<?, ?, ?> idx =
+ (AbstractFakeIndex<?, ?, ?>) groupIndexCollection.getSearchIndex();
+ return idx.getQueryCount();
+ }
+}
diff --git a/javatests/com/google/gerrit/server/query/group/BUILD b/javatests/com/google/gerrit/server/query/group/BUILD
index 0cc132d..0094bd6 100644
--- a/javatests/com/google/gerrit/server/query/group/BUILD
+++ b/javatests/com/google/gerrit/server/query/group/BUILD
@@ -1,7 +1,10 @@
load("@rules_java//java:defs.bzl", "java_library")
load("//tools/bzl:junit.bzl", "junit_tests")
-ABSTRACT_QUERY_TEST = ["AbstractQueryGroupsTest.java"]
+ABSTRACT_QUERY_TEST = [
+ "AbstractQueryGroupsTest.java",
+ "AbstractFakeQueryGroupsTest.java",
+]
java_library(
name = "abstract_query_tests",
@@ -13,6 +16,7 @@
"//java/com/google/gerrit/entities",
"//java/com/google/gerrit/extensions:api",
"//java/com/google/gerrit/index",
+ "//java/com/google/gerrit/index/testing",
"//java/com/google/gerrit/lifecycle",
"//java/com/google/gerrit/server",
"//java/com/google/gerrit/server/schema",
diff --git a/javatests/com/google/gerrit/server/query/group/FakeQueryGroupsTest.java b/javatests/com/google/gerrit/server/query/group/FakeQueryGroupsTest.java
index e4f228a..d347716 100644
--- a/javatests/com/google/gerrit/server/query/group/FakeQueryGroupsTest.java
+++ b/javatests/com/google/gerrit/server/query/group/FakeQueryGroupsTest.java
@@ -25,12 +25,26 @@
import java.util.Map;
import org.eclipse.jgit.lib.Config;
-public class FakeQueryGroupsTest extends AbstractQueryGroupsTest {
+public class FakeQueryGroupsTest extends AbstractFakeQueryGroupsTest {
@ConfigSuite.Default
public static Config defaultConfig() {
return IndexConfig.createForFake();
}
+ @ConfigSuite.Config
+ public static Config searchAfterPaginationType() {
+ Config config = defaultConfig();
+ config.setString("index", null, "paginationType", "SEARCH_AFTER");
+ return config;
+ }
+
+ @ConfigSuite.Config
+ public static Config nonePaginationType() {
+ Config config = defaultConfig();
+ config.setString("index", null, "paginationType", "NONE");
+ return config;
+ }
+
@ConfigSuite.Configs
public static Map<String, Config> againstPreviousIndexVersion() {
// the current schema version is already tested by the inherited default config suite