summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShawn O. Pearce <sop@google.com>2011-05-20 17:50:49 -0700
committerAndroid Code Review <code-review@android.com>2011-05-20 17:50:49 -0700
commitb7ebfe1d9032294cce8a2eecb21c6f54b23abab6 (patch)
treebcce452051c7c1aeadd2ea2c8b596861c9f139d4
parent3d1f73fff7242b850466cdfe64d143c1cadc688e (diff)
parentb8e4e3594942b55409807e1bff1251e4b916f422 (diff)
Merge "Automatically check for refs/meta/config changes"
-rw-r--r--Documentation/config-gerrit.txt15
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java37
-rw-r--r--gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java40
-rw-r--r--gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java6
4 files changed, 94 insertions, 4 deletions
diff --git a/Documentation/config-gerrit.txt b/Documentation/config-gerrit.txt
index b288c89e2b..39eb057a56 100644
--- a/Documentation/config-gerrit.txt
+++ b/Documentation/config-gerrit.txt
@@ -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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
index 6459e3c2ff..3990d06d07 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectCacheImpl.java
@@ -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. */
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
index 73765e8756..0d297cd71d 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/project/ProjectState.java
@@ -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();
}
diff --git a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
index a037e30f7b..ab5d75f087 100644
--- a/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
+++ b/gerrit-server/src/test/java/com/google/gerrit/server/project/RefControlTest.java
@@ -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());
}