Deregister queue metrics upon shutdown Historically, the registration of metrics in Gerrit has been a fire-and-forget operation, without the possibility of having access to the registration handle and remove them as needed. The initial design of metrics made it almost impossible for plugins to use them effectively, until I06d4853 introduced the possibility of having access to the registration handles and control them as part of the plugins lifecycle. The I06d4853 missed however, the integration with the WorkQueue, which was left in its initial design issue, without any ability to have queues with metrics in plugins. Add the missing deregistration path as a follow up of I06d4853 to the WorkQueue so that plugins can easily create queues with associated metrics, without the issue of ending up with reloading/restarting failures because of duplicated metric names. Release-Notes: Deregister queue metrics upon executor shutdown Bug: Issue 461836624 Change-Id: I9ed45cedd00d39beb3f514d23ed2e08c853aae72
diff --git a/java/com/google/gerrit/server/git/WorkQueue.java b/java/com/google/gerrit/server/git/WorkQueue.java index 3032bfe..54c9eb6 100644 --- a/java/com/google/gerrit/server/git/WorkQueue.java +++ b/java/com/google/gerrit/server/git/WorkQueue.java
@@ -20,6 +20,7 @@ import com.google.common.flogger.FluentLogger; import com.google.gerrit.entities.Project; import com.google.gerrit.extensions.events.LifecycleListener; +import com.google.gerrit.extensions.registration.RegistrationHandle; import com.google.gerrit.lifecycle.LifecycleModule; import com.google.gerrit.metrics.Description; import com.google.gerrit.metrics.MetricMaker; @@ -243,6 +244,7 @@ private class Executor extends ScheduledThreadPoolExecutor { private final ConcurrentHashMap<Integer, Task<?>> all; private final String queueName; + private final List<RegistrationHandle> metricsRegistrationHandles; Executor(int corePoolSize, final String queueName) { super( @@ -267,6 +269,7 @@ corePoolSize + 4 // concurrency level ); this.queueName = queueName; + this.metricsRegistrationHandles = new ArrayList<>(); } @Override @@ -345,44 +348,69 @@ } private void buildMetrics(String queueName) { - metrics.newCallbackMetric( - getMetricName(queueName, "max_pool_size"), - Long.class, - new Description("Maximum allowed number of threads in the pool") - .setGauge() - .setUnit("threads"), - () -> (long) getMaximumPoolSize()); - metrics.newCallbackMetric( - getMetricName(queueName, "pool_size"), - Long.class, - new Description("Current number of threads in the pool").setGauge().setUnit("threads"), - () -> (long) getPoolSize()); - metrics.newCallbackMetric( - getMetricName(queueName, "active_threads"), - Long.class, - new Description("Number number of threads that are actively executing tasks") - .setGauge() - .setUnit("threads"), - () -> (long) getActiveCount()); - metrics.newCallbackMetric( - getMetricName(queueName, "scheduled_tasks"), - Integer.class, - new Description("Number of scheduled tasks in the queue").setGauge().setUnit("tasks"), - () -> getQueue().size()); - metrics.newCallbackMetric( - getMetricName(queueName, "total_scheduled_tasks_count"), - Long.class, - new Description("Total number of tasks that have been scheduled for execution") - .setCumulative() - .setUnit("tasks"), - this::getTaskCount); - metrics.newCallbackMetric( - getMetricName(queueName, "total_completed_tasks_count"), - Long.class, - new Description("Total number of tasks that have completed execution") - .setCumulative() - .setUnit("tasks"), - this::getCompletedTaskCount); + metricsRegistrationHandles.add( + metrics.newCallbackMetric( + getMetricName(queueName, "max_pool_size"), + Long.class, + new Description("Maximum allowed number of threads in the pool") + .setGauge() + .setUnit("threads"), + () -> (long) getMaximumPoolSize())); + metricsRegistrationHandles.add( + metrics.newCallbackMetric( + getMetricName(queueName, "pool_size"), + Long.class, + new Description("Current number of threads in the pool") + .setGauge() + .setUnit("threads"), + () -> (long) getPoolSize())); + metricsRegistrationHandles.add( + metrics.newCallbackMetric( + getMetricName(queueName, "active_threads"), + Long.class, + new Description("Number number of threads that are actively executing tasks") + .setGauge() + .setUnit("threads"), + () -> (long) getActiveCount())); + metricsRegistrationHandles.add( + metrics.newCallbackMetric( + getMetricName(queueName, "scheduled_tasks"), + Integer.class, + new Description("Number of scheduled tasks in the queue").setGauge().setUnit("tasks"), + () -> getQueue().size())); + metricsRegistrationHandles.add( + metrics.newCallbackMetric( + getMetricName(queueName, "total_scheduled_tasks_count"), + Long.class, + new Description("Total number of tasks that have been scheduled for execution") + .setCumulative() + .setUnit("tasks"), + this::getTaskCount)); + metricsRegistrationHandles.add( + metrics.newCallbackMetric( + getMetricName(queueName, "total_completed_tasks_count"), + Long.class, + new Description("Total number of tasks that have completed execution") + .setCumulative() + .setUnit("tasks"), + this::getCompletedTaskCount)); + } + + @Override + public void shutdown() { + super.shutdown(); + deregisterMetrics(); + } + + @Override + public List<Runnable> shutdownNow() { + List<Runnable> runnables = super.shutdownNow(); + deregisterMetrics(); + return runnables; + } + + private void deregisterMetrics() { + metricsRegistrationHandles.forEach(RegistrationHandle::remove); } private String getMetricName(String queueName, String metricName) {