Merge branch 'stable-3.6' into stable-3.7 * stable-3.6: Add missing Singleton to ActiveWokersCheck and DeadlockCheck Add active http workers check Change-Id: I211ca8452b5bb1005b71c43b897ad51d4c51563b
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java index 3cda7b6..bb70839 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java +++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/HealthCheckSubsystemsModule.java
@@ -21,6 +21,7 @@ import com.googlesource.gerrit.plugins.healthcheck.check.BlockedThreadsCheck; import com.googlesource.gerrit.plugins.healthcheck.check.DeadlockCheck; import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck; +import com.googlesource.gerrit.plugins.healthcheck.check.HttpActiveWorkersCheck; import com.googlesource.gerrit.plugins.healthcheck.check.JGitHealthCheck; import com.googlesource.gerrit.plugins.healthcheck.check.ProjectsListHealthCheck; import com.googlesource.gerrit.plugins.healthcheck.check.QueryChangesHealthCheck; @@ -34,6 +35,7 @@ bindChecker(QueryChangesHealthCheck.class); bindChecker(AuthHealthCheck.class); bindChecker(ActiveWorkersCheck.class); + bindChecker(HttpActiveWorkersCheck.class); bindChecker(DeadlockCheck.class); bindChecker(BlockedThreadsCheck.class);
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AbstractWorkersHealthCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AbstractWorkersHealthCheck.java new file mode 100644 index 0000000..a01eba1 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/AbstractWorkersHealthCheck.java
@@ -0,0 +1,72 @@ +// Copyright (C) 2020 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.googlesource.gerrit.plugins.healthcheck.check; + +import com.codahale.metrics.Gauge; +import com.codahale.metrics.MetricRegistry; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig; +import com.googlesource.gerrit.plugins.healthcheck.HealthCheckMetrics; +import java.util.Optional; + +public abstract class AbstractWorkersHealthCheck extends AbstractHealthCheck { + + private final String metricName; + private final Integer maxPoolSize; + private final Integer threshold; + private final MetricRegistry metricRegistry; + + protected AbstractWorkersHealthCheck( + ListeningExecutorService executor, + HealthCheckConfig config, + MetricRegistry metricRegistry, + String name, + String metricName, + Integer maxPoolSize, + HealthCheckMetrics.Factory healthCheckMetricsFactory) { + super(executor, config, name, healthCheckMetricsFactory); + this.metricRegistry = metricRegistry; + this.metricName = metricName; + this.maxPoolSize = maxPoolSize; + this.threshold = config.getActiveWorkersThreshold(name); + } + + @Override + protected Result doCheck() throws Exception { + return Optional.ofNullable(metricRegistry.getGauges().get(metricName)) + .map( + metric -> { + float currentThreadsPercentage = (getMetricValue(metric) * 100) / maxPoolSize; + return (currentThreadsPercentage <= threshold) ? Result.PASSED : Result.FAILED; + }) + .orElse(Result.PASSED); + } + + private Long getMetricValue(Gauge<?> metric) { + Object value = metric.getValue(); + if (value instanceof Long) { + return (Long) value; + } + + if (value instanceof Integer) { + return ((Integer) value).longValue(); + } + + throw new IllegalArgumentException( + String.format( + "Workers metric value must be of type java.lang.Long or java.lang.Integer but was %s ", + value.getClass().getName())); + } +}
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ActiveWorkersCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ActiveWorkersCheck.java index 8e64593..a23567c 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ActiveWorkersCheck.java +++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/ActiveWorkersCheck.java
@@ -21,20 +21,17 @@ import com.google.gerrit.server.config.GerritServerConfig; import com.google.gerrit.server.config.ThreadSettingsConfig; import com.google.inject.Inject; +import com.google.inject.Singleton; import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig; import com.googlesource.gerrit.plugins.healthcheck.HealthCheckMetrics; -import java.util.Optional; import org.eclipse.jgit.lib.Config; -public class ActiveWorkersCheck extends AbstractHealthCheck { +@Singleton +public class ActiveWorkersCheck extends AbstractWorkersHealthCheck { public static final String ACTIVE_WORKERS_METRIC_NAME = "queue/ssh_interactive_worker/active_threads"; - private Integer threshold; - private Integer interactiveThreadsMaxPoolSize; - private MetricRegistry metricRegistry; - @Inject public ActiveWorkersCheck( @GerritServerConfig Config gerritConfig, @@ -43,25 +40,15 @@ ThreadSettingsConfig threadSettingsConfig, MetricRegistry metricRegistry, HealthCheckMetrics.Factory healthCheckMetricsFactory) { - super(executor, healthCheckConfig, ACTIVEWORKERS, healthCheckMetricsFactory); - this.threshold = healthCheckConfig.getActiveWorkersThreshold(ACTIVEWORKERS); - this.metricRegistry = metricRegistry; - this.interactiveThreadsMaxPoolSize = - getInteractiveThreadsMaxPoolSize(threadSettingsConfig, gerritConfig); - } - @Override - protected Result doCheck() throws Exception { - return Optional.ofNullable(metricRegistry.getGauges().get(ACTIVE_WORKERS_METRIC_NAME)) - .map( - metric -> { - float currentInteractiveThreadsPercentage = - ((long) metric.getValue() * 100) / interactiveThreadsMaxPoolSize; - return (currentInteractiveThreadsPercentage <= threshold) - ? Result.PASSED - : Result.FAILED; - }) - .orElse(Result.PASSED); + super( + executor, + healthCheckConfig, + metricRegistry, + ACTIVEWORKERS, + ACTIVE_WORKERS_METRIC_NAME, + getInteractiveThreadsMaxPoolSize(threadSettingsConfig, gerritConfig), + healthCheckMetricsFactory); } /** @@ -72,7 +59,7 @@ * * @return max number of allowed threads in interactive work queue */ - private Integer getInteractiveThreadsMaxPoolSize( + private static Integer getInteractiveThreadsMaxPoolSize( ThreadSettingsConfig threadSettingsConfig, Config gerritConfig) { int poolSize = threadSettingsConfig.getSshdThreads(); int batchThreads =
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/DeadlockCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/DeadlockCheck.java index 6a6ade6..85e721d 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/DeadlockCheck.java +++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/DeadlockCheck.java
@@ -19,10 +19,12 @@ import com.codahale.metrics.MetricRegistry; import com.google.common.util.concurrent.ListeningExecutorService; import com.google.inject.Inject; +import com.google.inject.Singleton; import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig; import com.googlesource.gerrit.plugins.healthcheck.HealthCheckMetrics; import java.util.Optional; +@Singleton public class DeadlockCheck extends AbstractHealthCheck { public static final String DEADLOCKED_THREADS_METRIC_NAME =
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheckNames.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheckNames.java index 4771942..4604af0 100644 --- a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheckNames.java +++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HealthCheckNames.java
@@ -20,6 +20,7 @@ String QUERYCHANGES = "querychanges"; String AUTH = "auth"; String ACTIVEWORKERS = "activeworkers"; + String HTTPACTIVEWORKERS = "httpactiveworkers"; String DEADLOCK = "deadlock"; String BLOCKEDTHREADS = "blockedthreads"; String GLOBAL = "global";
diff --git a/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HttpActiveWorkersCheck.java b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HttpActiveWorkersCheck.java new file mode 100644 index 0000000..19c2815 --- /dev/null +++ b/src/main/java/com/googlesource/gerrit/plugins/healthcheck/check/HttpActiveWorkersCheck.java
@@ -0,0 +1,46 @@ +// Copyright (C) 2020 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.googlesource.gerrit.plugins.healthcheck.check; + +import static com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames.HTTPACTIVEWORKERS; + +import com.codahale.metrics.MetricRegistry; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.gerrit.server.config.ThreadSettingsConfig; +import com.google.inject.Inject; +import com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig; +import com.googlesource.gerrit.plugins.healthcheck.HealthCheckMetrics; + +public class HttpActiveWorkersCheck extends AbstractWorkersHealthCheck { + public static final String HTTP_WORKERS_METRIC_NAME = + "http/server/jetty/threadpool/active_threads"; + + @Inject + public HttpActiveWorkersCheck( + ListeningExecutorService executor, + HealthCheckConfig healthCheckConfig, + ThreadSettingsConfig threadSettingsConfig, + MetricRegistry metricRegistry, + HealthCheckMetrics.Factory healthCheckMetricsFactory) { + super( + executor, + healthCheckConfig, + metricRegistry, + HTTPACTIVEWORKERS, + HTTP_WORKERS_METRIC_NAME, + threadSettingsConfig.getHttpdMaxThreads(), + healthCheckMetricsFactory); + } +}
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md index 0b4f05f..be418b8 100644 --- a/src/main/resources/Documentation/config.md +++ b/src/main/resources/Documentation/config.md
@@ -54,6 +54,8 @@ - `projectslist` : check the ability to list projects with their descriptions - `auth`: check the ability to authenticate with username and password - `activeworkers`: check the number of active worker threads and the ability to create a new one +- `httpactiveworkers`: check the number of active HTTP worker threads and the ability + to create a new one - `deadlock` : check if Java deadlocks are reported by the JVM - `blockedthreads` : check the number of blocked threads @@ -91,8 +93,8 @@ Default: no password - - `healthcheck.activeworkers.threshold` : Percent of queue occupancy above which queue is consider - as full. + - `healthcheck.activeworkers.threshold` : Percent of queue occupancy above which queue is + considered as full. Default: 80
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ActiveWorkersCheckTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ActiveWorkersCheckTest.java index df88c48..2d2ebd4 100644 --- a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ActiveWorkersCheckTest.java +++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/ActiveWorkersCheckTest.java
@@ -104,6 +104,27 @@ assertThat(check.run().result).isEqualTo(Result.PASSED); } + @Test + public void shouldFailCheckWhenMetricValueIsNotLongOrInteger() { + + MetricRegistry metricRegistry = new MetricRegistry(); + metricRegistry.register( + ActiveWorkersCheck.ACTIVE_WORKERS_METRIC_NAME, + new Gauge<String>() { + @Override + public String getValue() { + return "55"; + } + }); + Config gerritConfig = new Config(); + gerritConfig.setInt("sshd", null, "threads", 12); + + Injector injector = testInjector(new TestModule(gerritConfig, metricRegistry)); + + ActiveWorkersCheck check = createCheck(injector); + assertThat(check.run().result).isEqualTo(Result.FAILED); + } + private Injector testInjector(AbstractModule testModule) { return Guice.createInjector(new HealthCheckModule(), testModule); }
diff --git a/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HttpActiveWorkersCheckTest.java b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HttpActiveWorkersCheckTest.java new file mode 100644 index 0000000..515a8e6 --- /dev/null +++ b/src/test/java/com/googlesource/gerrit/plugins/healthcheck/HttpActiveWorkersCheckTest.java
@@ -0,0 +1,143 @@ +// Copyright (C) 2020 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.googlesource.gerrit.plugins.healthcheck; + +import static com.google.common.truth.Truth.assertThat; +import static com.googlesource.gerrit.plugins.healthcheck.HealthCheckConfig.DEFAULT_CONFIG; +import static com.googlesource.gerrit.plugins.healthcheck.check.HealthCheckNames.HTTPACTIVEWORKERS; + +import com.codahale.metrics.Gauge; +import com.codahale.metrics.MetricRegistry; +import com.google.common.util.concurrent.ListeningExecutorService; +import com.google.gerrit.server.config.GerritServerConfig; +import com.google.gerrit.server.config.ThreadSettingsConfig; +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.googlesource.gerrit.plugins.healthcheck.check.HealthCheck.Result; +import com.googlesource.gerrit.plugins.healthcheck.check.HttpActiveWorkersCheck; +import org.eclipse.jgit.lib.Config; +import org.junit.Test; + +public class HttpActiveWorkersCheckTest { + + @Test + public void shouldPassCheckWhenActiveHttpWorkersLessThanThreshold() { + + int thresholdPerc = 50; + int maxThreads = 12; + int usedThreads = maxThreads * thresholdPerc / 100 - 1; + + MetricRegistry metricRegistry = createHttpMetricRegistry(usedThreads); + + Config gerritConfig = new Config(); + gerritConfig.setInt("httpd", null, "maxThreads", maxThreads); + + Injector injector = testInjector(new TestModule(gerritConfig, metricRegistry)); + + HealthCheckConfig healthCheckConfig = + new HealthCheckConfig( + "[healthcheck \"" + HTTPACTIVEWORKERS + "\"]\n" + " threshold = " + thresholdPerc); + HttpActiveWorkersCheck check = createCheck(injector, healthCheckConfig); + assertThat(check.run().result).isEqualTo(Result.PASSED); + } + + @Test + public void shouldFailCheckWhenActiveHttpWorkersMoreThanThreshold() { + int thresholdPerc = 50; + int maxThreads = 12; + int usedThreads = maxThreads * thresholdPerc / 100 + 1; + + MetricRegistry metricRegistry = createHttpMetricRegistry(usedThreads); + + Config gerritConfig = new Config(); + gerritConfig.setInt("httpd", null, "maxThreads", maxThreads); + + Injector injector = testInjector(new TestModule(gerritConfig, metricRegistry)); + + HealthCheckConfig healthCheckConfig = + new HealthCheckConfig( + "[healthcheck \"" + HTTPACTIVEWORKERS + "\"]\n" + " threshold = " + thresholdPerc); + HttpActiveWorkersCheck check = createCheck(injector, healthCheckConfig); + assertThat(check.run().result).isEqualTo(Result.FAILED); + } + + @Test + public void shouldUseHttpWorkersDefaultThreshold() { + HealthCheckConfig healthCheckConfig = + new HealthCheckConfig("[healthcheck \"" + HTTPACTIVEWORKERS + "\"]\n"); + assertThat(healthCheckConfig.getActiveWorkersThreshold(HTTPACTIVEWORKERS)).isEqualTo(80); + } + + @Test + public void shouldPassCheckWhenNoActiveHttpWorkers() { + + MetricRegistry metricRegistry = createHttpMetricRegistry(0); + + Injector injector = testInjector(new TestModule(new Config(), metricRegistry)); + + HttpActiveWorkersCheck check = createCheck(injector); + assertThat(check.run().result).isEqualTo(Result.PASSED); + } + + private MetricRegistry createHttpMetricRegistry(Integer value) { + MetricRegistry metricRegistry = new MetricRegistry(); + + metricRegistry.register( + HttpActiveWorkersCheck.HTTP_WORKERS_METRIC_NAME, + new Gauge<Integer>() { + @Override + public Integer getValue() { + return value; + } + }); + return metricRegistry; + } + + private HttpActiveWorkersCheck createCheck(Injector injector) { + return createCheck(injector, DEFAULT_CONFIG); + } + + private HttpActiveWorkersCheck createCheck( + Injector injector, HealthCheckConfig healtchCheckConfig) { + return new HttpActiveWorkersCheck( + injector.getInstance(ListeningExecutorService.class), + healtchCheckConfig, + injector.getInstance(ThreadSettingsConfig.class), + injector.getInstance(MetricRegistry.class), + new DummyHealthCheckMetricsFactory()); + } + + private Injector testInjector(AbstractModule testModule) { + return Guice.createInjector(new HealthCheckModule(), testModule); + } + + private class TestModule extends AbstractModule { + Config gerritConfig; + MetricRegistry metricRegistry; + + public TestModule(Config gerritConfig, MetricRegistry metricRegistry) { + this.gerritConfig = gerritConfig; + this.metricRegistry = metricRegistry; + } + + @Override + protected void configure() { + bind(Config.class).annotatedWith(GerritServerConfig.class).toInstance(gerritConfig); + bind(ThreadSettingsConfig.class); + bind(MetricRegistry.class).toInstance(metricRegistry); + } + } +}