Closes #4809 - Set a max number of requests per connection.

Implemented as part of #4975.

Added a test case that proves that the connection is closed
when the max usage count is reached.

Improved logging.

Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
Simone Bordet 2020-08-12 16:14:51 +02:00
parent 3c6c5075f8
commit 4a0af046b8
3 changed files with 47 additions and 13 deletions

View File

@ -254,10 +254,10 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
protected Connection activate()
{
Pool<Connection>.Entry entry = pool.acquire();
if (LOG.isDebugEnabled())
LOG.debug("activated '{}'", entry);
if (entry != null)
{
if (LOG.isDebugEnabled())
LOG.debug("activated {}", entry);
Connection connection = entry.getPooled();
acquired(connection);
return connection;
@ -298,16 +298,14 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
Pool<Connection>.Entry entry = (Pool<Connection>.Entry)attachable.getAttachment();
if (entry == null)
return true;
if (LOG.isDebugEnabled())
LOG.debug("releasing {}", entry);
boolean reusable = pool.release(entry);
if (!reusable)
{
if (LOG.isDebugEnabled())
LOG.debug("Released ({}) {}", reusable, entry);
if (reusable)
return true;
remove(connection);
return false;
}
return true;
}
@Override
public boolean remove(Connection connection)
@ -325,9 +323,9 @@ public abstract class AbstractConnectionPool implements ConnectionPool, Dumpable
if (entry == null)
return false;
attachable.setAttachment(null);
if (LOG.isDebugEnabled())
LOG.debug("removing {}", entry);
boolean removed = pool.remove(entry);
if (LOG.isDebugEnabled())
LOG.debug("Removed ({}) {}", removed, entry);
if (removed || force)
{
released(connection);

View File

@ -351,6 +351,42 @@ public class ConnectionPoolTest
assertThat(connectionPool.getConnectionCount(), Matchers.lessThanOrEqualTo(count));
}
@ParameterizedTest
@MethodSource("pools")
public void testConnectionMaxUsage(ConnectionPoolFactory factory) throws Exception
{
startServer(new EmptyServerHandler());
int maxUsageCount = 2;
startClient(destination ->
{
AbstractConnectionPool connectionPool = (AbstractConnectionPool)factory.factory.newConnectionPool(destination);
connectionPool.setMaxUsageCount(maxUsageCount);
return connectionPool;
});
client.setMaxConnectionsPerDestination(1);
// Send first request, we are within the max usage count.
ContentResponse response1 = client.newRequest("localhost", connector.getLocalPort()).send();
assertEquals(HttpStatus.OK_200, response1.getStatus());
HttpDestination destination = (HttpDestination)client.getDestinations().get(0);
AbstractConnectionPool connectionPool = (AbstractConnectionPool)destination.getConnectionPool();
assertEquals(0, connectionPool.getActiveConnectionCount());
assertEquals(1, connectionPool.getIdleConnectionCount());
assertEquals(1, connectionPool.getConnectionCount());
// Send second request, max usage count will be reached,
// the only connection must be closed.
ContentResponse response2 = client.newRequest("localhost", connector.getLocalPort()).send();
assertEquals(HttpStatus.OK_200, response2.getStatus());
assertEquals(0, connectionPool.getActiveConnectionCount());
assertEquals(0, connectionPool.getIdleConnectionCount());
assertEquals(0, connectionPool.getConnectionCount());
}
private static class ConnectionPoolFactory
{
private final String name;

View File

@ -545,7 +545,7 @@ public class Pool<T> implements AutoCloseable, Dumpable
public String toString()
{
long encoded = state.get();
return String.format("%s@%x{hi=%d,lo=%d.p=%s}",
return String.format("%s@%x{usage=%d,multiplex=%d,pooled=%s}",
getClass().getSimpleName(),
hashCode(),
AtomicBiInteger.getHi(encoded),