Fix server startup when replication.config exists
The addition of the per-request RefControl cache broke push replication as the PushOp "request" could not locate a Guice binding for the cache. Use the same trick we do with MergeOp to setup a per-operation "request" scope for Guice. Change-Id: I0cecbad4809493f43cb2fec3624b1a53e38fb48f
This commit is contained in:
@@ -22,16 +22,13 @@ import com.google.gerrit.reviewdb.Project;
|
|||||||
import com.google.gerrit.server.CurrentUser;
|
import com.google.gerrit.server.CurrentUser;
|
||||||
import com.google.gerrit.server.IdentifiedUser;
|
import com.google.gerrit.server.IdentifiedUser;
|
||||||
import com.google.gerrit.server.RemotePeer;
|
import com.google.gerrit.server.RemotePeer;
|
||||||
import com.google.gerrit.server.RequestCleanup;
|
|
||||||
import com.google.gerrit.server.config.GerritRequestModule;
|
import com.google.gerrit.server.config.GerritRequestModule;
|
||||||
import com.google.gerrit.server.ssh.SshInfo;
|
import com.google.gerrit.server.ssh.SshInfo;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
import com.google.inject.Key;
|
|
||||||
import com.google.inject.OutOfScopeException;
|
import com.google.inject.OutOfScopeException;
|
||||||
import com.google.inject.Provider;
|
import com.google.inject.Provider;
|
||||||
import com.google.inject.Scope;
|
|
||||||
import com.google.inject.servlet.RequestScoped;
|
import com.google.inject.servlet.RequestScoped;
|
||||||
|
|
||||||
import com.jcraft.jsch.HostKey;
|
import com.jcraft.jsch.HostKey;
|
||||||
@@ -65,7 +62,7 @@ public class ChangeMergeQueue implements MergeQueue {
|
|||||||
Injector child = parent.createChildInjector(new AbstractModule() {
|
Injector child = parent.createChildInjector(new AbstractModule() {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
bindScope(RequestScoped.class, MyScope.REQUEST);
|
bindScope(RequestScoped.class, PerThreadRequestScope.REQUEST);
|
||||||
install(new GerritRequestModule());
|
install(new GerritRequestModule());
|
||||||
|
|
||||||
bind(CurrentUser.class).to(IdentifiedUser.class);
|
bind(CurrentUser.class).to(IdentifiedUser.class);
|
||||||
@@ -186,8 +183,8 @@ public class ChangeMergeQueue implements MergeQueue {
|
|||||||
|
|
||||||
private void mergeImpl(Branch.NameKey branch) {
|
private void mergeImpl(Branch.NameKey branch) {
|
||||||
try {
|
try {
|
||||||
MyScope ctx = new MyScope();
|
PerThreadRequestScope ctx = new PerThreadRequestScope();
|
||||||
MyScope old = MyScope.set(ctx);
|
PerThreadRequestScope old = PerThreadRequestScope.set(ctx);
|
||||||
try {
|
try {
|
||||||
try {
|
try {
|
||||||
bgFactory.get().create(branch).merge();
|
bgFactory.get().create(branch).merge();
|
||||||
@@ -195,7 +192,7 @@ public class ChangeMergeQueue implements MergeQueue {
|
|||||||
ctx.cleanup.run();
|
ctx.cleanup.run();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
MyScope.set(old);
|
PerThreadRequestScope.set(old);
|
||||||
}
|
}
|
||||||
} catch (Throwable e) {
|
} catch (Throwable e) {
|
||||||
log.error("Merge attempt for " + branch + " failed", e);
|
log.error("Merge attempt for " + branch + " failed", e);
|
||||||
@@ -261,65 +258,4 @@ public class ChangeMergeQueue implements MergeQueue {
|
|||||||
return "recheck " + project.get() + " " + dest.getShortName();
|
return "recheck " + project.get() + " " + dest.getShortName();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static class MyScope {
|
|
||||||
private static final ThreadLocal<MyScope> current =
|
|
||||||
new ThreadLocal<MyScope>();
|
|
||||||
|
|
||||||
private static MyScope getContext() {
|
|
||||||
final MyScope ctx = current.get();
|
|
||||||
if (ctx == null) {
|
|
||||||
throw new OutOfScopeException("Not in command/request");
|
|
||||||
}
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
static MyScope set(MyScope ctx) {
|
|
||||||
MyScope old = current.get();
|
|
||||||
current.set(ctx);
|
|
||||||
return old;
|
|
||||||
}
|
|
||||||
|
|
||||||
static final Scope REQUEST = new Scope() {
|
|
||||||
public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
|
|
||||||
return new Provider<T>() {
|
|
||||||
public T get() {
|
|
||||||
return getContext().get(key, creator);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return String.format("%s[%s]", creator, REQUEST);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "MergeQueue.REQUEST";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private static final Key<RequestCleanup> RC_KEY =
|
|
||||||
Key.get(RequestCleanup.class);
|
|
||||||
|
|
||||||
private final RequestCleanup cleanup;
|
|
||||||
private final Map<Key<?>, Object> map;
|
|
||||||
|
|
||||||
MyScope() {
|
|
||||||
cleanup = new RequestCleanup();
|
|
||||||
map = new HashMap<Key<?>, Object>();
|
|
||||||
map.put(RC_KEY, cleanup);
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized <T> T get(Key<T> key, Provider<T> creator) {
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
T t = (T) map.get(key);
|
|
||||||
if (t == null) {
|
|
||||||
t = creator.get();
|
|
||||||
map.put(key, t);
|
|
||||||
}
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
// Copyright (C) 2009 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.git;
|
||||||
|
|
||||||
|
import com.google.gerrit.server.RequestCleanup;
|
||||||
|
import com.google.inject.Key;
|
||||||
|
import com.google.inject.OutOfScopeException;
|
||||||
|
import com.google.inject.Provider;
|
||||||
|
import com.google.inject.Scope;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
class PerThreadRequestScope {
|
||||||
|
private static final ThreadLocal<PerThreadRequestScope> current =
|
||||||
|
new ThreadLocal<PerThreadRequestScope>();
|
||||||
|
|
||||||
|
private static PerThreadRequestScope getContext() {
|
||||||
|
final PerThreadRequestScope ctx = current.get();
|
||||||
|
if (ctx == null) {
|
||||||
|
throw new OutOfScopeException("Not in command/request");
|
||||||
|
}
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PerThreadRequestScope set(PerThreadRequestScope ctx) {
|
||||||
|
PerThreadRequestScope old = current.get();
|
||||||
|
current.set(ctx);
|
||||||
|
return old;
|
||||||
|
}
|
||||||
|
|
||||||
|
static final Scope REQUEST = new Scope() {
|
||||||
|
public <T> Provider<T> scope(final Key<T> key, final Provider<T> creator) {
|
||||||
|
return new Provider<T>() {
|
||||||
|
public T get() {
|
||||||
|
return getContext().get(key, creator);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return String.format("%s[%s]", creator, REQUEST);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "PerThreadRequestScope.REQUEST";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final Key<RequestCleanup> RC_KEY =
|
||||||
|
Key.get(RequestCleanup.class);
|
||||||
|
|
||||||
|
final RequestCleanup cleanup;
|
||||||
|
private final Map<Key<?>, Object> map;
|
||||||
|
|
||||||
|
PerThreadRequestScope() {
|
||||||
|
cleanup = new RequestCleanup();
|
||||||
|
map = new HashMap<Key<?>, Object>();
|
||||||
|
map.put(RC_KEY, cleanup);
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized <T> T get(Key<T> key, Provider<T> creator) {
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
T t = (T) map.get(key);
|
||||||
|
if (t == null) {
|
||||||
|
t = creator.get();
|
||||||
|
map.put(key, t);
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -155,6 +155,16 @@ class PushOp implements ProjectRunnable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
|
PerThreadRequestScope ctx = new PerThreadRequestScope();
|
||||||
|
PerThreadRequestScope old = PerThreadRequestScope.set(ctx);
|
||||||
|
try {
|
||||||
|
runPushOperation();
|
||||||
|
} finally {
|
||||||
|
PerThreadRequestScope.set(old);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runPushOperation() {
|
||||||
// Lock the queue, and remove ourselves, so we can't be modified once
|
// Lock the queue, and remove ourselves, so we can't be modified once
|
||||||
// we start replication (instead a new instance, with the same URI, is
|
// we start replication (instead a new instance, with the same URI, is
|
||||||
// created and scheduled for a future point in time.)
|
// created and scheduled for a future point in time.)
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import com.google.gerrit.server.ReplicationUser;
|
|||||||
import com.google.gerrit.server.config.ConfigUtil;
|
import com.google.gerrit.server.config.ConfigUtil;
|
||||||
import com.google.gerrit.server.config.SitePaths;
|
import com.google.gerrit.server.config.SitePaths;
|
||||||
import com.google.gerrit.server.project.NoSuchProjectException;
|
import com.google.gerrit.server.project.NoSuchProjectException;
|
||||||
|
import com.google.gerrit.server.project.PerRequestProjectControlCache;
|
||||||
import com.google.gerrit.server.project.ProjectControl;
|
import com.google.gerrit.server.project.ProjectControl;
|
||||||
import com.google.gwtorm.client.SchemaFactory;
|
import com.google.gwtorm.client.SchemaFactory;
|
||||||
import com.google.inject.AbstractModule;
|
import com.google.inject.AbstractModule;
|
||||||
@@ -29,6 +30,7 @@ import com.google.inject.Inject;
|
|||||||
import com.google.inject.Injector;
|
import com.google.inject.Injector;
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
import com.google.inject.assistedinject.FactoryProvider;
|
import com.google.inject.assistedinject.FactoryProvider;
|
||||||
|
import com.google.inject.servlet.RequestScoped;
|
||||||
|
|
||||||
import com.jcraft.jsch.Session;
|
import com.jcraft.jsch.Session;
|
||||||
|
|
||||||
@@ -373,6 +375,8 @@ public class PushReplication implements ReplicationQueue {
|
|||||||
injector.createChildInjector(new AbstractModule() {
|
injector.createChildInjector(new AbstractModule() {
|
||||||
@Override
|
@Override
|
||||||
protected void configure() {
|
protected void configure() {
|
||||||
|
bindScope(RequestScoped.class, PerThreadRequestScope.REQUEST);
|
||||||
|
bind(PerRequestProjectControlCache.class).in(RequestScoped.class);
|
||||||
bind(CurrentUser.class).toInstance(remoteUser);
|
bind(CurrentUser.class).toInstance(remoteUser);
|
||||||
}
|
}
|
||||||
}).getInstance(ProjectControl.Factory.class);
|
}).getInstance(ProjectControl.Factory.class);
|
||||||
@@ -396,15 +400,22 @@ public class PushReplication implements ReplicationQueue {
|
|||||||
|
|
||||||
void schedule(final Project.NameKey project, final String ref,
|
void schedule(final Project.NameKey project, final String ref,
|
||||||
final URIish uri) {
|
final URIish uri) {
|
||||||
|
PerThreadRequestScope ctx = new PerThreadRequestScope();
|
||||||
|
PerThreadRequestScope old = PerThreadRequestScope.set(ctx);
|
||||||
try {
|
try {
|
||||||
if (!controlFor(project).isVisible()) {
|
try {
|
||||||
|
if (!controlFor(project).isVisible()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (NoSuchProjectException e1) {
|
||||||
|
log.error("Internal error: project " + project
|
||||||
|
+ " not found during replication");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} catch (NoSuchProjectException e1) {
|
} finally {
|
||||||
log.error("Internal error: project " + project
|
PerThreadRequestScope.set(old);
|
||||||
+ " not found during replication");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
synchronized (pending) {
|
synchronized (pending) {
|
||||||
PushOp e = pending.get(uri);
|
PushOp e = pending.get(uri);
|
||||||
if (e == null) {
|
if (e == null) {
|
||||||
@@ -443,16 +454,6 @@ public class PushReplication implements ReplicationQueue {
|
|||||||
* @param pushOp The PushOp instance to be scheduled.
|
* @param pushOp The PushOp instance to be scheduled.
|
||||||
*/
|
*/
|
||||||
void reschedule(final PushOp pushOp) {
|
void reschedule(final PushOp pushOp) {
|
||||||
try {
|
|
||||||
if (!controlFor(pushOp.getProjectNameKey()).isVisible()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (NoSuchProjectException e1) {
|
|
||||||
log.error("Internal error: project " + pushOp.getProjectNameKey()
|
|
||||||
+ " not found during replication");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// It locks access to pending variable.
|
// It locks access to pending variable.
|
||||||
synchronized (pending) {
|
synchronized (pending) {
|
||||||
URIish uri = pushOp.getURI();
|
URIish uri = pushOp.getURI();
|
||||||
|
|||||||
Reference in New Issue
Block a user