Move SSH command creation off NioProcessors

Creating an SSH command for an incoming user request may require
looking up group information in LDAP if the user's groups are not hot
in the cache. This can take some time and may temporarily block an
NioProcessor thread preventing network IO from occurring for other
active user sessions.

Shift command creation onto a background work queue that only does
command construction for incoming requests. This way active commands
are not blocked by LDAP group lookups.

Two threads are used to try and avoid a single LDAP lookup from
blocking all new command creation on the server.

Change-Id: I1b49a836ba3443a9a85c29b7e3156558ca34ac47
Signed-off-by: Shawn O. Pearce <sop@google.com>
This commit is contained in:
Shawn O. Pearce
2011-05-15 13:56:30 -07:00
parent 7929d874a0
commit d6296556c6
3 changed files with 47 additions and 1 deletions

View File

@@ -14,6 +14,8 @@
package com.google.gerrit.sshd;
import com.google.gerrit.server.config.GerritServerConfig;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.sshd.SshScope.Context;
import com.google.inject.Inject;
import com.google.inject.Provider;
@@ -24,26 +26,38 @@ import org.apache.sshd.server.Environment;
import org.apache.sshd.server.ExitCallback;
import org.apache.sshd.server.SessionAware;
import org.apache.sshd.server.session.ServerSession;
import org.eclipse.jgit.lib.Config;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
/**
* Creates a CommandFactory using commands registered by {@link CommandModule}.
*/
class CommandFactoryProvider implements Provider<CommandFactory> {
private static final Logger logger = LoggerFactory
.getLogger(CommandFactoryProvider.class);
private final DispatchCommandProvider dispatcher;
private final SshLog log;
private final Executor startExecutor;
@Inject
CommandFactoryProvider(
@CommandName(Commands.ROOT) final DispatchCommandProvider d,
@GerritServerConfig final Config cfg, final WorkQueue workQueue,
final SshLog l) {
dispatcher = d;
log = l;
int threads = cfg.getInt("sshd","commandStartThreads", 2);
startExecutor = workQueue.createQueue(threads, "SshCommandStart");
}
@Override
@@ -62,6 +76,7 @@ class CommandFactoryProvider implements Provider<CommandFactory> {
private OutputStream out;
private OutputStream err;
private ExitCallback exit;
private Environment env;
private Context ctx;
private DispatchCommand cmd;
private boolean logged;
@@ -93,6 +108,25 @@ class CommandFactoryProvider implements Provider<CommandFactory> {
}
public void start(final Environment env) throws IOException {
this.env = env;
startExecutor.execute(new Runnable() {
public void run() {
try {
onStart();
} catch (Exception e) {
logger.warn("Cannot start command \"" + ctx.getCommandLine()
+ "\" for user " + ctx.getSession().getUsername(), e);
}
}
@Override
public String toString() {
return "start (user " + ctx.getSession().getUsername() + ")";
}
});
}
private void onStart() throws IOException {
synchronized (this) {
final Context old = SshScope.set(ctx);
try {