Configure DB connection pool

So far, the connection pool to the database was using the default values
for the maximum number of connections (8). In high traffic situations,
this can negatively affect the plugin performance.

Add a configuration option, maxConnections, to specify the max number of
instances in the connection pool. Other important settings are relative
to this option: the minimum and the maximum number of idle connections
are set to 25% and 50% of maxConnections respectively while the initial
size of the pool is set to the minimal number of idle connections. These
values follow the recommendations stated by one of the contributors [1].

Also, validate the connection instances retrieved from the pool in order
to avoid using closed connections and set the maximum time to wait for a
connection to be retrieved from the pool.

[1] https://www.slideshare.net/psteitz/apachecon2010-pooldbcp

Change-Id: I7ac84b4b5c6f72c3c8726a0d16033b356de0ae75
diff --git a/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventsLogConfig.java b/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventsLogConfig.java
index 55ca83d..aae0df3 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventsLogConfig.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/eventslog/EventsLogConfig.java
@@ -40,6 +40,7 @@
   static final String CONFIG_WAIT_TIME = "retryTimeout";
   static final String CONFIG_CONN_TIME = "connectTimeout";
   static final String CONFIG_EVICT_IDLE_TIME = "evictIdleTime";
+  static final String CONFIG_MAX_CONNECTIONS = "maxConnections";
 
   static final boolean DEFAULT_COPY_LOCAL = false;
   static final int DEFAULT_MAX_AGE = 30;
@@ -49,6 +50,7 @@
   static final int DEFAULT_CONN_TIME = 1000;
   static final String DEFAULT_DRIVER = "org.h2.Driver";
   static final int DEFAULT_EVICT_IDLE_TIME = 1000 * 60;
+  static final int DEFAULT_MAX_CONNECTIONS = 8;
 
   private boolean copyLocal;
   private int maxAge;
@@ -63,6 +65,7 @@
   private String storeUsername;
   private String storePassword;
   private int evictIdleTime;
+  private int maxConnections;
   private String defaultUrl;
 
   @Inject
@@ -83,6 +86,7 @@
     storeUsername = cfg.getString(CONFIG_USERNAME);
     storePassword = cfg.getString(CONFIG_PASSWORD);
     evictIdleTime = cfg.getInt(CONFIG_EVICT_IDLE_TIME, DEFAULT_EVICT_IDLE_TIME);
+    maxConnections = Math.max(cfg.getInt(CONFIG_MAX_CONNECTIONS, DEFAULT_MAX_CONNECTIONS), 1);
   }
 
   public int getMaxAge() {
@@ -141,4 +145,8 @@
   public int getEvictIdleTime() {
     return evictIdleTime;
   }
+
+  public int getMaxConnections() {
+    return maxConnections;
+  }
 }
diff --git a/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLClient.java b/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLClient.java
index a0186e7..760bb65 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLClient.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLClient.java
@@ -56,6 +56,11 @@
     ds.setDriverClassName(storeDriver);
     ds.setUrl(storeUrl);
     ds.setConnectionProperties(urlOptions);
+    ds.setMaxWait(MILLISECONDS.convert(30, SECONDS));
+    ds.setTestOnBorrow(true);
+    ds.setValidationQuery("SELECT 1");
+    ds.setValidationQueryTimeout(5);
+
     gson = new GsonBuilder().registerTypeAdapter(Supplier.class, new SupplierSerializer()).create();
   }
 
@@ -87,6 +92,17 @@
     ds.setTimeBetweenEvictionRunsMillis(evictIdleTime / 2);
   }
 
+  void setMaxConnections(int maxConnections) {
+    ds.setMaxActive(maxConnections);
+    ds.setMinIdle(maxConnections / 4);
+    int maxIdle = maxConnections / 2;
+    if (maxIdle == 0) {
+      maxIdle = maxConnections;
+    }
+    ds.setMaxIdle(maxIdle);
+    ds.setInitialSize(ds.getMinIdle());
+  }
+
   /**
    * Create the database if it has not yet been created.
    *
diff --git a/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLModule.java b/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLModule.java
index a75796a..d5c2c18 100644
--- a/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLModule.java
+++ b/src/main/java/com/ericsson/gerrit/plugins/eventslog/sql/SQLModule.java
@@ -45,6 +45,7 @@
     sqlClient.setUsername(cfg.getStoreUsername());
     sqlClient.setPassword(cfg.getStorePassword());
     sqlClient.setEvictIdleTime(cfg.getEvictIdleTime());
+    sqlClient.setMaxConnections(cfg.getMaxConnections());
     return sqlClient;
   }
 
diff --git a/src/main/resources/Documentation/config.md b/src/main/resources/Documentation/config.md
index 4e29811..ce1572c 100644
--- a/src/main/resources/Documentation/config.md
+++ b/src/main/resources/Documentation/config.md
@@ -75,3 +75,7 @@
 :    Interval of time in milliseconds after which an idle database connection is
      evicted from the connection pool. When not specified, the default value is
      set to 60000ms.
+
+plugin.@PLUGIN@.maxConnections
+:    Maximum number of instances in the connection pool to the database. Includes
+     active and idle connections. By default 8.
diff --git a/src/test/java/com/ericsson/gerrit/plugins/eventslog/EventsLogConfigTest.java b/src/test/java/com/ericsson/gerrit/plugins/eventslog/EventsLogConfigTest.java
index 11f7a8d..ff52ebc 100644
--- a/src/test/java/com/ericsson/gerrit/plugins/eventslog/EventsLogConfigTest.java
+++ b/src/test/java/com/ericsson/gerrit/plugins/eventslog/EventsLogConfigTest.java
@@ -20,6 +20,7 @@
 import static com.ericsson.gerrit.plugins.eventslog.EventsLogConfig.CONFIG_EVICT_IDLE_TIME;
 import static com.ericsson.gerrit.plugins.eventslog.EventsLogConfig.CONFIG_LOCAL_PATH;
 import static com.ericsson.gerrit.plugins.eventslog.EventsLogConfig.CONFIG_MAX_AGE;
+import static com.ericsson.gerrit.plugins.eventslog.EventsLogConfig.CONFIG_MAX_CONNECTIONS;
 import static com.ericsson.gerrit.plugins.eventslog.EventsLogConfig.CONFIG_MAX_TRIES;
 import static com.ericsson.gerrit.plugins.eventslog.EventsLogConfig.CONFIG_PASSWORD;
 import static com.ericsson.gerrit.plugins.eventslog.EventsLogConfig.CONFIG_RETURN_LIMIT;
@@ -31,6 +32,7 @@
 import static com.ericsson.gerrit.plugins.eventslog.EventsLogConfig.DEFAULT_DRIVER;
 import static com.ericsson.gerrit.plugins.eventslog.EventsLogConfig.DEFAULT_EVICT_IDLE_TIME;
 import static com.ericsson.gerrit.plugins.eventslog.EventsLogConfig.DEFAULT_MAX_AGE;
+import static com.ericsson.gerrit.plugins.eventslog.EventsLogConfig.DEFAULT_MAX_CONNECTIONS;
 import static com.ericsson.gerrit.plugins.eventslog.EventsLogConfig.DEFAULT_MAX_TRIES;
 import static com.ericsson.gerrit.plugins.eventslog.EventsLogConfig.DEFAULT_RETURN_LIMIT;
 import static com.ericsson.gerrit.plugins.eventslog.EventsLogConfig.DEFAULT_WAIT_TIME;
@@ -60,6 +62,7 @@
   private static final String PLUGIN = "plugin";
   private static final String PLUGIN_NAME = "eventsLog";
   private static final int CUSTOM_EVICT_IDLE_TIME = 10000;
+  private static final int CUSTOM_MAX_CONNECTIONS = 32;
   private static final List<String> urlOptions = ImmutableList.of("DB_CLOSE_DELAY=10");
 
   private SitePaths site;
@@ -95,6 +98,7 @@
     assertThat(eventsLogConfig.getStoreUsername()).isNull();
     assertThat(eventsLogConfig.getStorePassword()).isNull();
     assertThat(eventsLogConfig.getEvictIdleTime()).isEqualTo(DEFAULT_EVICT_IDLE_TIME);
+    assertThat(eventsLogConfig.getMaxConnections()).isEqualTo(DEFAULT_MAX_CONNECTIONS);
   }
 
   @Test
@@ -116,6 +120,7 @@
     assertThat(eventsLogConfig.getStoreUsername()).isEqualTo("testUsername");
     assertThat(eventsLogConfig.getStorePassword()).isEqualTo("testPassword");
     assertThat(eventsLogConfig.getEvictIdleTime()).isEqualTo(CUSTOM_EVICT_IDLE_TIME);
+    assertThat(eventsLogConfig.getMaxConnections()).isEqualTo(CUSTOM_MAX_CONNECTIONS);
   }
 
   private Config customConfig() {
@@ -133,6 +138,7 @@
     config.setString(PLUGIN, PLUGIN_NAME, CONFIG_USERNAME, "testUsername");
     config.setString(PLUGIN, PLUGIN_NAME, CONFIG_PASSWORD, "testPassword");
     config.setInt(PLUGIN, PLUGIN_NAME, CONFIG_EVICT_IDLE_TIME, CUSTOM_EVICT_IDLE_TIME);
+    config.setInt(PLUGIN, PLUGIN_NAME, CONFIG_MAX_CONNECTIONS, CUSTOM_MAX_CONNECTIONS);
     return config;
   }
 }