Optimize PermissionCollection to delay listing emails

The email addresses are only necessary if a ref pattern
containing ${username} pertains to an access request made
by the caller.

If patterns are not user-specific (e.g. refs/heads/*),
it is safe to skip loading emails from the AccountState
and Realm objects.

Change-Id: Id801dd637f69f1983bf28268654923099933817a
This commit is contained in:
Shawn Pearce
2014-12-19 16:01:03 -08:00
parent 773f8f1469
commit 9133543f80
2 changed files with 33 additions and 19 deletions

View File

@@ -24,6 +24,7 @@ import com.google.gerrit.common.data.PermissionRule;
import com.google.gerrit.reviewdb.client.AccountGroup;
import com.google.gerrit.reviewdb.client.Project;
import com.google.inject.Inject;
import com.google.inject.Provider;
import com.google.inject.Singleton;
import java.util.ArrayList;
@@ -60,23 +61,22 @@ public class PermissionCollection {
* priority order (project specific definitions must appear before
* inherited ones).
* @param ref reference being accessed.
* @param usernames if the reference is a per-user reference, access sections
* using the parameter variable "${username}" will first have each of
* {@code usernames} inserted into them before seeing if they apply to
* the reference named by {@code ref}. If null or empty, per-user
* references are ignored.
* @param usernameProvider if the reference is a per-user reference, access
* sections using the parameter variable "${username}" will first
* have each of {@code usernames} inserted into them before seeing if
* they apply to the reference named by {@code ref}.
* @return map of permissions that apply to this reference, keyed by
* permission name.
*/
PermissionCollection filter(Iterable<SectionMatcher> matcherList,
String ref, Collection<String> usernames) {
String ref, Provider<? extends Collection<String>> usernameProvider) {
if (isRE(ref)) {
ref = RefControl.shortestExample(ref);
} else if (ref.endsWith("/*")) {
ref = ref.substring(0, ref.length() - 1);
}
boolean hasUsernames = usernames != null && !usernames.isEmpty();
Collection<String> usernames = null;
boolean perUser = false;
Map<AccessSection, Project.NameKey> sectionToProject = Maps.newLinkedHashMap();
for (SectionMatcher sm : matcherList) {
@@ -92,9 +92,13 @@ public class PermissionCollection {
// that will never be shared with non-user references, and the per-user
// references are usually less frequent than the non-user references.
//
if (hasUsernames) {
if (!perUser && sm.matcher instanceof RefPatternMatcher.ExpandParameters) {
perUser = ((RefPatternMatcher.ExpandParameters) sm.matcher).matchPrefix(ref);
if (sm.matcher instanceof RefPatternMatcher.ExpandParameters) {
if (!((RefPatternMatcher.ExpandParameters) sm.matcher).matchPrefix(ref)) {
continue;
}
perUser = true;
if (usernames == null) {
usernames = usernameProvider.get();
}
for (String username : usernames) {
if (sm.match(ref, username)) {

View File

@@ -14,7 +14,6 @@
package com.google.gerrit.server.project;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.gerrit.common.Nullable;
@@ -57,6 +56,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
@@ -207,15 +207,25 @@ public class ProjectControl {
}
RefControl ctl = refControls.get(refName);
if (ctl == null) {
ImmutableList.Builder<String> usernames = ImmutableList.<String> builder();
if (user.getUserName() != null) {
usernames.add(user.getUserName());
}
if (user instanceof IdentifiedUser) {
usernames.addAll(((IdentifiedUser) user).getEmailAddresses());
}
Provider<List<String>> usernames = new Provider<List<String>>() {
@Override
public List<String> get() {
List<String> r;
if (user.isIdentifiedUser()) {
Set<String> emails = ((IdentifiedUser) user).getEmailAddresses();
r = new ArrayList<>(emails.size() + 1);
r.addAll(emails);
} else {
r = new ArrayList<>(1);
}
if (user.getUserName() != null) {
r.add(user.getUserName());
}
return r;
}
};
PermissionCollection relevant =
permissionFilter.filter(access(), refName, usernames.build());
permissionFilter.filter(access(), refName, usernames);
ctl = new RefControl(this, refName, relevant);
refControls.put(refName, ctl);
}