@VisibleForTesting
  void cleanup(ConnectionPool pool) {
    if (pool.getNumConnections() > pool.getMinSize()) {
      // Check if the pool hasn't been active in a while or not 50% are used
      long timeSinceLastActive = Time.now() - pool.getLastActiveTime();
      int total = pool.getNumConnections();
      // Active is a transient status in many cases for a connection since
      // the handler thread uses the connection very quickly. Thus, the number
      // of connections with handlers using at the call time is constantly low.
      // Recently active is more lasting status, and it shows how many
      // connections have been used with a recent time period. (i.e. 30 seconds)
      int active = pool.getNumActiveConnectionsRecently();
      float poolMinActiveRatio = pool.getMinActiveRatio();
      if (timeSinceLastActive > connectionCleanupPeriodMs ||
          active < poolMinActiveRatio * total) {
        // Be greedy here to close as many connections as possible in one shot
        // The number should at least be 1
        int targetConnectionsCount = Math.max(1,
            (int)(poolMinActiveRatio * total) - active);
        List<ConnectionContext> connections =
            pool.removeConnections(targetConnectionsCount);
        for (ConnectionContext conn : connections) {
          conn.close();
        }
        LOG.debug("Removed connection {} used {} seconds ago. " +
                "Pool has {}/{} connections", pool.getConnectionPoolId(),
            TimeUnit.MILLISECONDS.toSeconds(timeSinceLastActive),
            pool.getNumConnections(), pool.getMaxSize());
      }
    }
  }
@Test
  public void testCleanup() throws Exception {
    Map<ConnectionPoolId, ConnectionPool> poolMap = connManager.getPools();

    ConnectionPool pool1 = new ConnectionPool(conf, TEST_NN_ADDRESS, TEST_USER1,
        0, 10, 0.5f, ClientProtocol.class, null);
    addConnectionsToPool(pool1, 9, 4);
    poolMap.put(
        new ConnectionPoolId(TEST_USER1, TEST_NN_ADDRESS, ClientProtocol.class),
        pool1);

    ConnectionPool pool2 = new ConnectionPool(conf, TEST_NN_ADDRESS, TEST_USER2,
        0, 10, 0.5f, ClientProtocol.class, null);
    addConnectionsToPool(pool2, 10, 10);
    poolMap.put(
        new ConnectionPoolId(TEST_USER2, TEST_NN_ADDRESS, ClientProtocol.class),
        pool2);

    checkPoolConnections(TEST_USER1, 9, 4);
    checkPoolConnections(TEST_USER2, 10, 10);

    // Clean up first pool, one connection should be removed, and second pool
    // should remain the same.
    connManager.cleanup(pool1);
    checkPoolConnections(TEST_USER1, 8, 4);
    checkPoolConnections(TEST_USER2, 10, 10);

    // Clean up the first pool again, it should have no effect since it reached
    // the MIN_ACTIVE_RATIO.
    connManager.cleanup(pool1);
    checkPoolConnections(TEST_USER1, 8, 4);
    checkPoolConnections(TEST_USER2, 10, 10);

    // Make sure the number of connections doesn't go below minSize
    ConnectionPool pool3 = new ConnectionPool(conf, TEST_NN_ADDRESS, TEST_USER3,
        2, 10, 0.5f, ClientProtocol.class, null);
    addConnectionsToPool(pool3, 8, 0);
    poolMap.put(
        new ConnectionPoolId(TEST_USER3, TEST_NN_ADDRESS, ClientProtocol.class),
        pool3);
    checkPoolConnections(TEST_USER3, 10, 0);
    for (int i = 0; i < 10; i++) {
      connManager.cleanup(pool3);
    }
    checkPoolConnections(TEST_USER3, 2, 0);
    // With active connections added to pool, make sure it honors the
    // MIN_ACTIVE_RATIO again
    addConnectionsToPool(pool3, 8, 2);
    checkPoolConnections(TEST_USER3, 10, 2);
    for (int i = 0; i < 10; i++) {
      connManager.cleanup(pool3);
    }
    checkPoolConnections(TEST_USER3, 4, 2);
  }