Improve performance of parentof operator
Make parentof operator use IndexPredicates to improve performance.
On a site with ~20k open changes, this change significantly improves
performance of queries like [1]. They take ~25ms with this change and
~4s without it.
[1] gerrit query parentof:<change> status:new
Release-Notes: skip
Change-Id: I68fc26f8a6b64a3615732d885e0d118fd2752f3d
diff --git a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
index da36633..e36dbfc 100644
--- a/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
+++ b/java/com/google/gerrit/server/query/change/ChangeQueryBuilder.java
@@ -26,6 +26,7 @@
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
+import com.google.common.collect.Sets;
import com.google.common.flogger.FluentLogger;
import com.google.common.primitives.Ints;
import com.google.gerrit.entities.Account;
@@ -104,6 +105,8 @@
import org.eclipse.jgit.errors.RepositoryNotFoundException;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
/** Parses a query string meant to be applied to change objects. */
public class ChangeQueryBuilder extends QueryBuilder<ChangeData, ChangeQueryBuilder> {
@@ -813,11 +816,28 @@
List<ChangeData> changes = parseChangeData(value);
List<Predicate<ChangeData>> or = new ArrayList<>(changes.size());
for (ChangeData c : changes) {
- or.add(new ParentOfPredicate(value, c, args.repoManager));
+ for (RevCommit revCommit : getParents(c)) {
+ or.add(ChangePredicates.commitPrefix(revCommit.getId().getName()));
+ }
}
return Predicate.or(or);
}
+ private Set<RevCommit> getParents(ChangeData change) {
+ PatchSet ps = change.currentPatchSet();
+ try (Repository repo = args.repoManager.openRepository(change.project());
+ RevWalk walk = new RevWalk(repo)) {
+ RevCommit c = walk.parseCommit(ps.commitId());
+ return Sets.newHashSet(c.getParents());
+ } catch (IOException e) {
+ throw new StorageException(
+ String.format(
+ "Loading commit %s for ps %d of change %d failed.",
+ ps.commitId(), ps.id().get(), ps.id().changeId().get()),
+ e);
+ }
+ }
+
@Operator
public Predicate<ChangeData> parentproject(String name) {
return new ParentProjectPredicate(args.projectCache, args.childProjects, name);
diff --git a/java/com/google/gerrit/server/query/change/ParentOfPredicate.java b/java/com/google/gerrit/server/query/change/ParentOfPredicate.java
deleted file mode 100644
index e48d586..0000000
--- a/java/com/google/gerrit/server/query/change/ParentOfPredicate.java
+++ /dev/null
@@ -1,62 +0,0 @@
-// Copyright (C) 2021 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.change;
-
-import com.google.common.collect.Sets;
-import com.google.gerrit.entities.PatchSet;
-import com.google.gerrit.exceptions.StorageException;
-import com.google.gerrit.index.query.Matchable;
-import com.google.gerrit.index.query.OperatorPredicate;
-import com.google.gerrit.server.git.GitRepositoryManager;
-import java.io.IOException;
-import java.util.Set;
-import org.eclipse.jgit.lib.Repository;
-import org.eclipse.jgit.revwalk.RevCommit;
-import org.eclipse.jgit.revwalk.RevWalk;
-
-public class ParentOfPredicate extends OperatorPredicate<ChangeData>
- implements Matchable<ChangeData> {
- protected final Set<RevCommit> parents;
-
- public ParentOfPredicate(String value, ChangeData change, GitRepositoryManager repoManager) {
- super(ChangeQueryBuilder.FIELD_PARENTOF, value);
- this.parents = getParents(change, repoManager);
- }
-
- @Override
- public boolean match(ChangeData changeData) {
- return changeData.patchSets().stream().anyMatch(ps -> parents.contains(ps.commitId()));
- }
-
- @Override
- public int getCost() {
- return 1;
- }
-
- protected Set<RevCommit> getParents(ChangeData change, GitRepositoryManager repoManager) {
- PatchSet ps = change.currentPatchSet();
- try (Repository repo = repoManager.openRepository(change.project());
- RevWalk walk = new RevWalk(repo)) {
- RevCommit c = walk.parseCommit(ps.commitId());
- return Sets.newHashSet(c.getParents());
- } catch (IOException e) {
- throw new StorageException(
- String.format(
- "Loading commit %s for ps %d of change %d failed.",
- ps.commitId(), ps.id().get(), ps.id().changeId().get()),
- e);
- }
- }
-}