Bind the project cache clock thread in its own object
This allows me to better control when this thread gets created in a server. Right now its created 1:1 with the ProjectCacheImpl, which works fine for servers that have only one site loaded in the JVM. Longer term we should be supporting virtual hosting, where different Gerrit Code Review sites are able to be managed from a single JVM, and a single unified cache footprint (that still shards data uniquely for each site). Change-Id: I3e9b269aac7071a2f26a4289b4b524cb468592c7
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
// Copyright (C) 2012 The Android Open Source Project
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package com.google.gerrit.server.project;
|
||||
|
||||
import com.google.gerrit.server.config.ConfigUtil;
|
||||
import com.google.gerrit.server.config.GerritServerConfig;
|
||||
import com.google.inject.Inject;
|
||||
import com.google.inject.Singleton;
|
||||
|
||||
import org.eclipse.jgit.lib.Config;
|
||||
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/** Ticks periodically to force refresh events for {@link ProjectCacheImpl}. */
|
||||
@Singleton
|
||||
public class ProjectCacheClock {
|
||||
private volatile long generation;
|
||||
|
||||
@Inject
|
||||
public ProjectCacheClock(@GerritServerConfig Config serverConfig) {
|
||||
this(TimeUnit.MILLISECONDS.convert(
|
||||
ConfigUtil.getTimeUnit(serverConfig,
|
||||
"cache", "projects", "checkFrequency",
|
||||
5, TimeUnit.MINUTES), TimeUnit.MINUTES));
|
||||
}
|
||||
|
||||
public ProjectCacheClock(long checkFrequencyMillis) {
|
||||
if (10 < checkFrequencyMillis) {
|
||||
// Start with generation 1 (to avoid magic 0 below).
|
||||
generation = 1;
|
||||
ThreadFactory factory = new ThreadFactory() {
|
||||
private final AtomicInteger id = new AtomicInteger();
|
||||
|
||||
@Override
|
||||
public Thread newThread(Runnable runnable) {
|
||||
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
|
||||
thread.setName(String.format("ProjectCacheClock-%d", id.incrementAndGet()));
|
||||
thread.setDaemon(true);
|
||||
thread.setPriority(Thread.MIN_PRIORITY);
|
||||
return thread;
|
||||
}
|
||||
};
|
||||
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, factory);
|
||||
executor.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;
|
||||
}
|
||||
}
|
||||
|
||||
long read() {
|
||||
return generation;
|
||||
}
|
||||
}
|
||||
@@ -19,8 +19,6 @@ 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.AllProjectsName;
|
||||
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.inject.Inject;
|
||||
@@ -30,7 +28,6 @@ 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;
|
||||
@@ -38,8 +35,6 @@ 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;
|
||||
|
||||
@@ -71,39 +66,19 @@ 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;
|
||||
private final ProjectCacheClock clock;
|
||||
|
||||
@Inject
|
||||
ProjectCacheImpl(
|
||||
final AllProjectsName allProjectsName,
|
||||
@Named(CACHE_NAME) final Cache<Project.NameKey, ProjectState> byName,
|
||||
@Named(CACHE_LIST) final Cache<ListKey, SortedSet<Project.NameKey>> list,
|
||||
@GerritServerConfig final Config serverConfig) {
|
||||
ProjectCacheClock clock) {
|
||||
this.allProjectsName = allProjectsName;
|
||||
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;
|
||||
}
|
||||
this.clock = clock;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -125,7 +100,7 @@ public class ProjectCacheImpl implements ProjectCache {
|
||||
*/
|
||||
public ProjectState get(final Project.NameKey projectName) {
|
||||
ProjectState state = byName.get(projectName);
|
||||
if (state != null && state.needsRefresh(generation)) {
|
||||
if (state != null && state.needsRefresh(clock.read())) {
|
||||
byName.remove(projectName);
|
||||
state = byName.get(projectName);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user