Rely on Gerrit change_notes cache for filtering
The git-refs-filter makes sense only when it provides
a significant performance improvement from a network
and CPU perspective.
Do not scan for all refs on the repository because it
would consume too much CPU and I/O. Rely on Gerrit's
pre-defined caching abilities on change notes.
Change-Id: I2ad7496e2f8f5a1860245de6460fc64bfe8c0905
diff --git a/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/ForProjectWrapper.java b/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/ForProjectWrapper.java
index 0544665..426519e 100644
--- a/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/ForProjectWrapper.java
+++ b/src/main/java/com/googlesource/gerrit/modules/gitrefsfilter/ForProjectWrapper.java
@@ -14,36 +14,25 @@
package com.googlesource.gerrit.modules.gitrefsfilter;
-import static com.google.common.collect.ImmutableList.toImmutableList;
-
-import com.google.common.flogger.FluentLogger;
-import com.google.gerrit.common.Nullable;
import com.google.gerrit.entities.Change;
import com.google.gerrit.entities.Project;
import com.google.gerrit.extensions.api.access.CoreOrPluginProjectPermission;
import com.google.gerrit.extensions.conditions.BooleanCondition;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.server.notedb.ChangeNotes;
-import com.google.gerrit.server.notedb.ChangeNotes.Factory.ChangeNotesResult;
import com.google.gerrit.server.permissions.PermissionBackend.ForProject;
import com.google.gerrit.server.permissions.PermissionBackend.ForRef;
import com.google.gerrit.server.permissions.PermissionBackend.RefFilterOptions;
import com.google.gerrit.server.permissions.PermissionBackendException;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
-import java.io.IOException;
-import java.util.ArrayList;
import java.util.Collection;
-import java.util.Collections;
-import java.util.HashSet;
import java.util.Set;
-import java.util.stream.Stream;
+import java.util.stream.Collectors;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
public class ForProjectWrapper extends ForProject {
- private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
private final ForProject defaultForProject;
private final Project.NameKey project;
private final ChangeNotes.Factory changeNotesFactory;
@@ -82,20 +71,13 @@
@Override
public Collection<Ref> filter(Collection<Ref> refs, Repository repo, RefFilterOptions opts)
throws PermissionBackendException {
- Collection<Ref> filteredRefs = new ArrayList<>();
- Collection<Ref> defaultFilteredRefs =
- defaultForProject.filter(refs, repo, opts); // FIXME: can we filter the closed refs here?
- Set<String> openChangesRefs = openChangesByScan(repo);
-
- for (Ref ref : defaultFilteredRefs) {
- String refName = ref.getName();
- if (!isChangeRef(refName)
- || (isOpen(openChangesRefs, refName) && !isChangeMetaRef(refName))) {
- filteredRefs.add(ref);
- }
- }
-
- return filteredRefs;
+ return defaultForProject.filter(refs, repo, opts).parallelStream()
+ .filter(
+ (ref) -> {
+ String refName = ref.getName();
+ return (!isChangeRef(refName) || (!isChangeMetaRef(refName) && isOpen(refName)));
+ })
+ .collect(Collectors.toList());
}
private boolean isChangeRef(String changeKey) {
@@ -106,10 +88,10 @@
return isChangeRef(changeKey) && changeKey.endsWith("/meta");
}
- private boolean isOpen(Set<String> openChangesRefs, String changeKey) {
- // Parse changeKey as refs/changes/NN/<change num>/PP
- String changeRefWithoutPatchset = changeKey.substring(0, changeKey.lastIndexOf('/') + 1);
- return openChangesRefs.contains(changeRefWithoutPatchset);
+ private boolean isOpen(String refName) {
+ Change.Id changeId = Change.Id.fromRef(refName);
+ ChangeNotes changeNotes = changeNotesFactory.create(project, changeId);
+ return changeNotes.getChange().getStatus().isOpen();
}
@Override
@@ -121,34 +103,4 @@
public String resourcePath() {
return defaultForProject.resourcePath();
}
-
- private Set<String> openChangesByScan(Repository repo) {
- Set<String> result = new HashSet<>();
- Stream<ChangeNotesResult> s;
- try {
- s = changeNotesFactory.scan(repo, project);
- } catch (IOException e) {
- logger.atSevere().withCause(e).log(
- "Cannot load changes for project %s, assuming no changes are visible", project);
- return Collections.emptySet();
- }
-
- for (ChangeNotesResult notesResult : s.collect(toImmutableList())) {
- Change change = toNotes(notesResult).getChange();
- if (change.getStatus().isOpen()) {
- result.add(change.getId().toRefPrefix());
- }
- }
- return result;
- }
-
- @Nullable
- private ChangeNotes toNotes(ChangeNotesResult r) {
- if (r.error().isPresent()) {
- logger.atWarning().withCause(r.error().get()).log(
- "Failed to load change %s in %s", r.id(), project);
- return null;
- }
- return r.notes();
- }
}