Add sql/connection_pool/connections metric

Export the number of active (true) and idle (false) connections
inside of the connection pool, if the connection pool was enabled.

Change-Id: I42f4f24a27883a63730f6fe9e3649b93f59ebfc8
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
index ebbb765..c9da6dd 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/Daemon.java
@@ -276,7 +276,7 @@
   @VisibleForTesting
   public void start() throws IOException {
     if (dbInjector == null) {
-      dbInjector = createDbInjector(MULTI_USER);
+      dbInjector = createDbInjector(true /* enableMetrics */, MULTI_USER);
     }
     cfgInjector = createCfgInjector();
     config = cfgInjector.getInstance(
@@ -325,7 +325,6 @@
   private Injector createSysInjector() {
     final List<Module> modules = new ArrayList<>();
     modules.add(SchemaVersionCheck.module());
-    modules.add(new DropWizardMetricMaker.ApiModule());
     modules.add(new DropWizardMetricMaker.RestModule());
     modules.add(new LogFileCompressor.Module());
     modules.add(new WorkQueue.Module());
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
index ba8c28f..b6539f1 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/BatchProgramModule.java
@@ -20,8 +20,6 @@
 import com.google.gerrit.extensions.config.FactoryModule;
 import com.google.gerrit.extensions.registration.DynamicMap;
 import com.google.gerrit.extensions.registration.DynamicSet;
-import com.google.gerrit.metrics.DisabledMetricMaker;
-import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.reviewdb.client.AccountGroup;
 import com.google.gerrit.rules.PrologModule;
 import com.google.gerrit.server.CurrentUser;
@@ -91,7 +89,6 @@
     install(reviewDbModule);
     install(new DiffExecutorModule());
     install(PatchListCacheImpl.module());
-    bind(MetricMaker.class).to(DisabledMetricMaker.class);
 
     // Plugins are not loaded and we're just running through each change
     // once, so don't worry about cache removal.
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java
index 048c2ee..7f6313d 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteLibraryBasedDataSourceProvider.java
@@ -15,6 +15,7 @@
 package com.google.gerrit.pgm.util;
 
 import com.google.gerrit.common.SiteLibraryLoaderUtil;
+import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.SitePaths;
 import com.google.gerrit.server.schema.DataSourceProvider;
@@ -37,9 +38,10 @@
   @Inject
   SiteLibraryBasedDataSourceProvider(SitePaths site,
       @GerritServerConfig Config cfg,
+      MetricMaker metrics,
       DataSourceProvider.Context ctx,
       DataSourceType dst) {
-    super(cfg, ctx, dst);
+    super(cfg, metrics, ctx, dst);
     libdir = site.lib_dir;
   }
 
diff --git a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java
index a6f1f93..bf69d9b 100644
--- a/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java
+++ b/gerrit-pgm/src/main/java/com/google/gerrit/pgm/util/SiteProgram.java
@@ -22,6 +22,9 @@
 import com.google.gerrit.common.Die;
 import com.google.gerrit.extensions.events.LifecycleListener;
 import com.google.gerrit.lifecycle.LifecycleModule;
+import com.google.gerrit.metrics.DisabledMetricMaker;
+import com.google.gerrit.metrics.MetricMaker;
+import com.google.gerrit.metrics.dropwizard.DropWizardMetricMaker;
 import com.google.gerrit.server.config.GerritServerConfig;
 import com.google.gerrit.server.config.GerritServerConfigModule;
 import com.google.gerrit.server.config.SitePath;
@@ -93,7 +96,13 @@
   }
 
   /** @return provides database connectivity and site path. */
-  protected Injector createDbInjector(final DataSourceProvider.Context context) {
+  protected Injector createDbInjector(DataSourceProvider.Context context) {
+    return createDbInjector(false, context);
+  }
+
+  /** @return provides database connectivity and site path. */
+  protected Injector createDbInjector(final boolean enableMetrics,
+      final DataSourceProvider.Context context) {
     final Path sitePath = getSitePath();
     final List<Module> modules = new ArrayList<>();
 
@@ -107,6 +116,17 @@
     };
     modules.add(sitePathModule);
 
+    if (enableMetrics) {
+      modules.add(new DropWizardMetricMaker.ApiModule());
+    } else {
+      modules.add(new AbstractModule() {
+        @Override
+        protected void configure() {
+          bind(MetricMaker.class).to(DisabledMetricMaker.class);
+        }
+      });
+    }
+
     modules.add(new LifecycleModule() {
       @Override
       protected void configure() {
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceProvider.java b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceProvider.java
index 30ebaa7..e0482f9 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceProvider.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/schema/DataSourceProvider.java
@@ -20,6 +20,10 @@
 import com.google.common.base.Strings;
 import com.google.gerrit.extensions.events.LifecycleListener;
 import com.google.gerrit.extensions.persistence.DataSourceInterceptor;
+import com.google.gerrit.metrics.CallbackMetric1;
+import com.google.gerrit.metrics.Description;
+import com.google.gerrit.metrics.Field;
+import com.google.gerrit.metrics.MetricMaker;
 import com.google.gerrit.server.config.ConfigSection;
 import com.google.gerrit.server.config.ConfigUtil;
 import com.google.gerrit.server.config.GerritServerConfig;
@@ -46,15 +50,18 @@
   public static final int DEFAULT_POOL_LIMIT = 8;
 
   private final Config cfg;
+  private final MetricMaker metrics;
   private final Context ctx;
   private final DataSourceType dst;
   private DataSource ds;
 
   @Inject
   protected DataSourceProvider(@GerritServerConfig Config cfg,
+      MetricMaker metrics,
       Context ctx,
       DataSourceType dst) {
     this.cfg = cfg;
+    this.metrics = metrics;
     this.ctx = ctx;
     this.dst = dst;
   }
@@ -126,6 +133,7 @@
       ds.setMaxWait(ConfigUtil.getTimeUnit(cfg, "database", null,
           "poolmaxwait", MILLISECONDS.convert(30, SECONDS), MILLISECONDS));
       ds.setInitialSize(ds.getMinIdle());
+      exportPoolMetrics(ds);
       return intercept(interceptor, ds);
 
     } else {
@@ -148,6 +156,25 @@
     }
   }
 
+  private void exportPoolMetrics(final BasicDataSource pool) {
+    final CallbackMetric1<Boolean, Integer> cnt = metrics.newCallbackMetric(
+        "sql/connection_pool/connections",
+        Integer.class,
+        new Description("SQL database connections")
+          .setGauge()
+          .setUnit("connections"),
+        Field.ofBoolean("active"));
+    metrics.newTrigger(cnt, new Runnable() {
+      @Override
+      public void run() {
+        synchronized (pool) {
+          cnt.set(true, pool.getNumActive());
+          cnt.set(false, pool.getNumIdle());
+        }
+      }
+    });
+  }
+
   private DataSource intercept(String interceptor, DataSource ds) {
     if (interceptor == null) {
       return ds;
diff --git a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
index 10e3f9c..2ce4218 100644
--- a/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
+++ b/gerrit-war/src/main/java/com/google/gerrit/httpd/WebAppInitializer.java
@@ -261,6 +261,7 @@
       });
     }
     modules.add(new DatabaseModule());
+    modules.add(new DropWizardMetricMaker.ApiModule());
     return Guice.createInjector(PRODUCTION, modules);
   }
 
@@ -289,7 +290,6 @@
 
   private Injector createSysInjector() {
     final List<Module> modules = new ArrayList<>();
-    modules.add(new DropWizardMetricMaker.ApiModule());
     modules.add(new DropWizardMetricMaker.RestModule());
     modules.add(new WorkQueue.Module());
     modules.add(new ChangeHookRunner.Module());