SSH show-queue: option to group output by queue and print queue info

When the new option --by-queue is used the output of the show-queue
command is grouped by the work queue. Queue name is printed on top of
the tasks list and the number of worker threads assigned to that thread
pool is printed in the summary line. This option should help Gerrit admins
to identify which thread pools need to be adjusted.

For comparison, here is an example output without the new --by-queue option:

  Task     State        StartTime         Command
  ------------------------------------------------------------------------------
  9d51b721              16:29:14.750      git-upload-pack '/gerrit' (admin)
  18fr4561              16:29:14.750      git-upload-pack '/gerrit' (admin)
  ad52b823              16:29:14.750      git-upload-pack '/gerrit' (admin)
  bd51bbcc              16:29:14.750      git-upload-pack '/gerrit' (admin)
  11223344              16:29:14.750      git-upload-pack '/gerrit' (admin)
  3d120b5c 17:28:20.169 16:28:20.169      Log File Compressor
  ------------------------------------------------------------------------------
    6 tasks

With the --by-queue option the output looks like:

  $ ssh -p29418 admin@localhost gerrit show-queue -w --by-queue
  Task     State        StartTime         Command
  ------------------------------------------------------------------------------
  Queue: SSH-Interactive-Worker
  9d51b721              16:29:14.750      git-upload-pack '/gerrit' (admin)
  18fr4561              16:29:14.750      git-upload-pack '/gerrit' (admin)
  ad52b823              16:29:14.750      git-upload-pack '/gerrit' (admin)
  bd51bbcc              16:29:14.750      git-upload-pack '/gerrit' (admin)
  11223344              16:29:14.750      git-upload-pack '/gerrit' (admin)
  ------------------------------------------------------------------------------
    5 tasks, 14 worker threads

  Queue: WorkQueue
  3d120b5c 17:28:20.169 16:28:20.169      Log File Compressor
  ------------------------------------------------------------------------------
    1 tasks, 1 worker threads

Change-Id: If82bdb50702f2001f887fe5004b0d80b61131a90
This commit is contained in:
Saša Živkov 2016-07-12 17:23:19 +02:00
parent 89634f70f3
commit eaf64568a7
4 changed files with 105 additions and 44 deletions

View File

@ -39,6 +39,10 @@ Intended for interactive use only.
Do not format the output to the terminal width (default of
80 columns).
--by-queue::
-q::
Group tasks by queue and print queue info.
== DISPLAY
Task::

View File

@ -113,6 +113,7 @@ public class ListTasks implements RestReadView<ConfigResource> {
public String command;
public String remoteName;
public String projectName;
public String queueName;
public TaskInfo(Task<?> task) {
this.id = IdGenerator.format(task.getTaskId());
@ -120,6 +121,7 @@ public class ListTasks implements RestReadView<ConfigResource> {
this.startTime = new Timestamp(task.getStartTime().getTime());
this.delay = task.getDelay(TimeUnit.MILLISECONDS);
this.command = task.toString();
this.queueName = task.getQueueName();
if (task instanceof ProjectTask) {
ProjectTask<?> projectTask = ((ProjectTask<?>) task);

View File

@ -152,6 +152,15 @@ public class WorkQueue {
return result;
}
public Executor getExecutor(String queueName) {
for (Executor e : queues) {
if (e.queueName.equals(queueName)) {
return e;
}
}
return null;
}
private void stop() {
for (final Executor p : queues) {
p.shutdown();
@ -170,8 +179,9 @@ public class WorkQueue {
/** An isolated queue. */
public class Executor extends ScheduledThreadPoolExecutor {
private final ConcurrentHashMap<Integer, Task<?>> all;
private final String queueName;
Executor(final int corePoolSize, final String prefix) {
Executor(int corePoolSize, final String prefix) {
super(corePoolSize, new ThreadFactory() {
private final ThreadFactory parent = Executors.defaultThreadFactory();
private final AtomicInteger tid = new AtomicInteger(1);
@ -190,6 +200,7 @@ public class WorkQueue {
0.75f, // load factor
corePoolSize + 4 // concurrency level
);
queueName = prefix;
}
public void unregisterWorkQueue() {
@ -325,6 +336,10 @@ public class WorkQueue {
return startTime;
}
public String getQueueName() {
return executor.queueName;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
if (task.cancel(mayInterruptIfRunning)) {

View File

@ -17,12 +17,15 @@ package com.google.gerrit.sshd.commands;
import static com.google.gerrit.sshd.CommandMetaData.Mode.MASTER_OR_SLAVE;
import com.google.common.base.MoreObjects;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.gerrit.common.TimeUtil;
import com.google.gerrit.extensions.restapi.AuthException;
import com.google.gerrit.server.IdentifiedUser;
import com.google.gerrit.server.config.ConfigResource;
import com.google.gerrit.server.config.ListTasks;
import com.google.gerrit.server.config.ListTasks.TaskInfo;
import com.google.gerrit.server.git.WorkQueue;
import com.google.gerrit.server.git.WorkQueue.Task;
import com.google.gerrit.sshd.AdminHighPriorityCommand;
import com.google.gerrit.sshd.CommandMetaData;
@ -39,18 +42,27 @@ import java.util.List;
/** Display the current work queue. */
@AdminHighPriorityCommand
@CommandMetaData(name = "show-queue", description = "Display the background work queues",
runsAt = MASTER_OR_SLAVE)
@CommandMetaData(name = "show-queue",
description = "Display the background work queues",
runsAt = MASTER_OR_SLAVE)
final class ShowQueue extends SshCommand {
@Option(name = "--wide", aliases = {"-w"}, usage = "display without line width truncation")
@Option(name = "--wide", aliases = {"-w"},
usage = "display without line width truncation")
private boolean wide;
@Option(name = "--by-queue", aliases = {"-q"},
usage = "group tasks by queue and print queue info")
private boolean groupByQueue;
@Inject
private ListTasks listTasks;
@Inject
private IdentifiedUser currentUser;
@Inject
private WorkQueue workQueue;
private int columns = 80;
private int maxCommandWidth;
@ -75,50 +87,78 @@ final class ShowQueue extends SshCommand {
stdout.print("----------------------------------------------"
+ "--------------------------------\n");
List<TaskInfo> tasks;
try {
List<TaskInfo> tasks = listTasks.apply(new ConfigResource());
long now = TimeUtil.nowMs();
boolean viewAll = currentUser.getCapabilities().canViewQueue();
for (TaskInfo task : tasks) {
String start;
switch (task.state) {
case DONE:
case CANCELLED:
case RUNNING:
case READY:
start = format(task.state);
break;
case OTHER:
case SLEEPING:
default:
start = time(now, task.delay);
break;
}
// Shows information about tasks depending on the user rights
if (viewAll || task.projectName == null) {
String command = task.command.length() < maxCommandWidth
? task.command
: task.command.substring(0, maxCommandWidth);
stdout.print(String.format("%8s %-12s %-12s %-4s %s\n",
task.id, start, startTime(task.startTime), "", command));
} else {
String remoteName = task.remoteName != null
? task.remoteName + "/" + task.projectName
: task.projectName;
stdout.print(String.format("%8s %-12s %-4s %s\n",
task.id, start, startTime(task.startTime),
MoreObjects.firstNonNull(remoteName, "n/a")));
}
}
stdout.print("----------------------------------------------"
+ "--------------------------------\n");
stdout.print(" " + tasks.size() + " tasks\n");
tasks = listTasks.apply(new ConfigResource());
} catch (AuthException e) {
throw die(e);
}
boolean viewAll = currentUser.getCapabilities().canViewQueue();
long now = TimeUtil.nowMs();
if (groupByQueue) {
ListMultimap<String, TaskInfo> byQueue = byQueue(tasks);
for (String queueName : byQueue.keySet()) {
WorkQueue.Executor e = workQueue.getExecutor(queueName);
stdout.print(String.format("Queue: %s\n", queueName));
print(byQueue.get(queueName), now, viewAll, e.getCorePoolSize());
}
} else {
print(tasks, now, viewAll, 0);
}
}
private ListMultimap<String, TaskInfo> byQueue(List<TaskInfo> tasks) {
ListMultimap<String, TaskInfo> byQueue = LinkedListMultimap.create();
for (TaskInfo task : tasks) {
byQueue.put(task.queueName, task);
}
return byQueue;
}
private void print(List<TaskInfo> tasks, long now, boolean viewAll,
int threadPoolSize) {
for (TaskInfo task : tasks) {
String start;
switch (task.state) {
case DONE:
case CANCELLED:
case RUNNING:
case READY:
start = format(task.state);
break;
case OTHER:
case SLEEPING:
default:
start = time(now, task.delay);
break;
}
// Shows information about tasks depending on the user rights
if (viewAll || task.projectName == null) {
String command = task.command.length() < maxCommandWidth
? task.command
: task.command.substring(0, maxCommandWidth);
stdout.print(String.format("%8s %-12s %-12s %-4s %s\n",
task.id, start, startTime(task.startTime), "", command));
} else {
String remoteName = task.remoteName != null
? task.remoteName + "/" + task.projectName
: task.projectName;
stdout.print(String.format("%8s %-12s %-4s %s\n",
task.id, start, startTime(task.startTime),
MoreObjects.firstNonNull(remoteName, "n/a")));
}
}
stdout.print("----------------------------------------------"
+ "--------------------------------\n");
stdout.print(" " + tasks.size() + " tasks");
if (threadPoolSize > 0) {
stdout.print(", " + threadPoolSize + " worker threads");
}
stdout.print("\n\n");
}
private static String time(long now, long delay) {