Merge "Automatically check for refs/meta/config changes"
This commit is contained in:
commit
b7ebfe1d90
|
@ -485,6 +485,21 @@ configuration.
|
|||
+
|
||||
Default is true, enabled.
|
||||
|
||||
cache.projects.checkFrequency::
|
||||
+
|
||||
How often project configuration should be checked for update from Git.
|
||||
Gerrit Code Review caches project access rules and configuration in
|
||||
memory, checking the refs/meta/config branch every checkFrequency
|
||||
minutes to see if a new revision should be loaded and used for future
|
||||
access. Values can be specified using standard time unit abbreviations
|
||||
('ms', 'sec', 'min', etc.).
|
||||
+
|
||||
If set to 0, checks occur every time, which may slow down operations.
|
||||
Administrators may force the cache to flush with
|
||||
link:cmd-flush-caches.html[gerrit flush-caches].
|
||||
+
|
||||
Default is 5 minutes.
|
||||
|
||||
|
||||
[[commentlink]]Section commentlink
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
|
|
@ -19,6 +19,8 @@ import com.google.gerrit.reviewdb.ReviewDb;
|
|||
import com.google.gerrit.server.cache.Cache;
|
||||
import com.google.gerrit.server.cache.CacheModule;
|
||||
import com.google.gerrit.server.cache.EntryCreator;
|
||||
import com.google.gerrit.server.config.ConfigUtil;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.gwtorm.client.SchemaFactory;
|
||||
|
@ -29,6 +31,7 @@ import com.google.inject.TypeLiteral;
|
|||
import com.google.inject.name.Named;
|
||||
|
||||
import org.eclipse.jgit.errors.RepositoryNotFoundException;
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
|
||||
import java.util.Collections;
|
||||
|
@ -36,6 +39,8 @@ import java.util.Iterator;
|
|||
import java.util.NoSuchElementException;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
|
@ -66,14 +71,37 @@ public class ProjectCacheImpl implements ProjectCache {
|
|||
private final Cache<Project.NameKey, ProjectState> byName;
|
||||
private final Cache<ListKey,SortedSet<Project.NameKey>> list;
|
||||
private final Lock listLock;
|
||||
private volatile long generation;
|
||||
|
||||
@Inject
|
||||
ProjectCacheImpl(
|
||||
@Named(CACHE_NAME) final Cache<Project.NameKey, ProjectState> byName,
|
||||
@Named(CACHE_LIST) final Cache<ListKey, SortedSet<Project.NameKey>> list) {
|
||||
@Named(CACHE_LIST) final Cache<ListKey, SortedSet<Project.NameKey>> list,
|
||||
@GerritServerConfig final Config serverConfig) {
|
||||
this.byName = byName;
|
||||
this.list = list;
|
||||
this.listLock = new ReentrantLock(true /* fair */);
|
||||
|
||||
long checkFrequencyMillis = TimeUnit.MILLISECONDS.convert(
|
||||
ConfigUtil.getTimeUnit(serverConfig,
|
||||
"cache", "projects", "checkFrequency",
|
||||
5, TimeUnit.MINUTES), TimeUnit.MINUTES);
|
||||
if (10 < checkFrequencyMillis) {
|
||||
// Start with generation 1 (to avoid magic 0 below).
|
||||
generation = 1;
|
||||
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// This is not exactly thread-safe, but is OK for our use.
|
||||
// The only thread that writes the volatile is this task.
|
||||
generation = generation + 1;
|
||||
}
|
||||
}, checkFrequencyMillis, checkFrequencyMillis, TimeUnit.MILLISECONDS);
|
||||
} else {
|
||||
// Magic generation 0 triggers ProjectState to always
|
||||
// check on each needsRefresh() request we make to it.
|
||||
generation = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -83,7 +111,12 @@ public class ProjectCacheImpl implements ProjectCache {
|
|||
* @return the cached data; null if no such project exists.
|
||||
*/
|
||||
public ProjectState get(final Project.NameKey projectName) {
|
||||
return byName.get(projectName);
|
||||
ProjectState state = byName.get(projectName);
|
||||
if (state != null && state.needsRefresh(generation)) {
|
||||
byName.remove(projectName);
|
||||
state = byName.get(projectName);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
/** Invalidate the cached information about the given project. */
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ import com.google.gerrit.server.AnonymousUser;
|
|||
import com.google.gerrit.server.CurrentUser;
|
||||
import com.google.gerrit.server.config.AuthConfig;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.gerrit.server.git.GitRepositoryManager;
|
||||
import com.google.gerrit.server.git.ProjectConfig;
|
||||
import com.google.inject.AbstractModule;
|
||||
import com.google.inject.Guice;
|
||||
|
@ -316,12 +317,13 @@ public class RefControlTest extends TestCase {
|
|||
}
|
||||
};
|
||||
|
||||
GitRepositoryManager mgr = null;
|
||||
Project.NameKey wildProject = new Project.NameKey("-- All Projects --");
|
||||
ProjectControl.AssistedFactory projectControlFactory = null;
|
||||
all.put(local.getProject().getNameKey(), new ProjectState(anonymousUser,
|
||||
projectCache, wildProject, projectControlFactory, local));
|
||||
projectCache, wildProject, projectControlFactory, mgr, local));
|
||||
all.put(parent.getProject().getNameKey(), new ProjectState(anonymousUser,
|
||||
projectCache, wildProject, projectControlFactory, parent));
|
||||
projectCache, wildProject, projectControlFactory, mgr, parent));
|
||||
return all.get(local.getProject().getNameKey());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue