Automatically check for refs/meta/config changes

Periodically check a project's refs/meta/config for modifications
made outside of Gerrit Code Review.  This ensures slave servers will
eventually pick up new access controls or project settings without
requiring administrators to flush the "projects" cache over SSH.

Checks are done only every cache.projects.checkFrequency period, as a
local disk check requires at least one stat() call to examine the
loose reference's last modified time. This is relatively inexpensive
for a single project request like git clone, but not feasible for
multiple project lookups like a query results page or user dashboard.

To prevent many calls to System.currentTimeMillis() a background
thread (managed by Executors.newScheduledThreadPool) is used to update
a generation flag every checkFrequency period. During a cache get the
ProjectState rechecks its refs/meta/config if the generation does not
match, and gets replaced if there were changes.

Bug: issue 962
Change-Id: I9ad4db27329968e2993b4dd142d1325446190065
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2011-05-19 18:09:01 -07:00
parent cb115b6d24
commit b8e4e35949
4 changed files with 94 additions and 4 deletions

View File

@@ -23,10 +23,15 @@ import com.google.gerrit.reviewdb.Project;
import com.google.gerrit.server.AnonymousUser;
import com.google.gerrit.server.CurrentUser;
import com.google.gerrit.server.config.WildProjectName;
import com.google.gerrit.server.git.GitRepositoryManager;
import com.google.gerrit.server.git.ProjectConfig;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -44,21 +49,28 @@ public class ProjectState {
private final Project.NameKey wildProject;
private final ProjectCache projectCache;
private final ProjectControl.AssistedFactory projectControlFactory;
private final GitRepositoryManager gitMgr;
private final ProjectConfig config;
private final Set<AccountGroup.UUID> localOwners;
/** Last system time the configuration's revision was examined. */
private transient long lastCheckTime;
@Inject
protected ProjectState(final AnonymousUser anonymousUser,
final ProjectCache projectCache,
@WildProjectName final Project.NameKey wildProject,
final ProjectControl.AssistedFactory projectControlFactory,
final GitRepositoryManager gitMgr,
@Assisted final ProjectConfig config) {
this.anonymousUser = anonymousUser;
this.projectCache = projectCache;
this.wildProject = wildProject;
this.projectControlFactory = projectControlFactory;
this.gitMgr = gitMgr;
this.config = config;
this.lastCheckTime = System.currentTimeMillis();
HashSet<AccountGroup.UUID> groups = new HashSet<AccountGroup.UUID>();
AccessSection all = config.getAccessSection(AccessSection.ALL);
@@ -76,6 +88,34 @@ public class ProjectState {
localOwners = Collections.unmodifiableSet(groups);
}
boolean needsRefresh(long generation) {
if (generation <= 0) {
return isRevisionOutOfDate();
}
if (lastCheckTime != generation) {
lastCheckTime = generation;
return isRevisionOutOfDate();
}
return false;
}
private boolean isRevisionOutOfDate() {
try {
Repository git = gitMgr.openRepository(getProject().getNameKey());
try {
Ref ref = git.getRef(GitRepositoryManager.REF_CONFIG);
if (ref == null || ref.getObjectId() == null) {
return true;
}
return !ref.getObjectId().equals(config.getRevision());
} finally {
git.close();
}
} catch (IOException gone) {
return true;
}
}
public Project getProject() {
return getConfig().getProject();
}