diff --git a/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/pool/SqlConnectionPool.java b/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/pool/SqlConnectionPool.java index 113cec157..0a5c02597 100644 --- a/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/pool/SqlConnectionPool.java +++ b/vertx-sql-client/src/main/java/io/vertx/sqlclient/impl/pool/SqlConnectionPool.java @@ -270,29 +270,35 @@ class PoolRequest implements PoolWaiter.Listener, Completable< @Override public void complete(Lease lease, Throwable failure) { - if (timerID != -1L && !vertx.cancelTimer(timerID)) { - lease.recycle(); - } else { - if (failure == null) { + if (failure == null) { + if (timerID != -1L && !vertx.cancelTimer(timerID)) { + lease.recycle(); + } else { if (afterAcquire != null) { afterAcquire.apply(lease.get().conn).onComplete(ar2 -> { if (ar2.succeeded()) { handle(lease); } else { - // Should we do some cleanup ? - handler.fail(failure); + fail(lease, ar2.cause()); } }); } else { handle(lease); } - } else { - dequeueMetric(metric); - handler.fail(failure); } + } else { + fail(null, failure); } } + private void fail(Lease lease, Throwable cause) { + if (lease != null) { + lease.recycle(); + } + dequeueMetric(metric); + handler.fail(cause); + } + private void handle(Lease lease) { dequeueMetric(metric); PooledConnection pooled = lease.get(); diff --git a/vertx-sql-client/src/test/java/io/vertx/tests/sqlclient/spi/backend/DriverBaseTest.java b/vertx-sql-client/src/test/java/io/vertx/tests/sqlclient/spi/backend/DriverBaseTest.java index efa65d836..9dce86023 100644 --- a/vertx-sql-client/src/test/java/io/vertx/tests/sqlclient/spi/backend/DriverBaseTest.java +++ b/vertx-sql-client/src/test/java/io/vertx/tests/sqlclient/spi/backend/DriverBaseTest.java @@ -14,6 +14,7 @@ import io.vertx.sqlclient.RowIterator; import io.vertx.sqlclient.RowSet; import io.vertx.sqlclient.SqlConnectOptions; +import io.vertx.sqlclient.SqlConnection; import io.vertx.sqlclient.desc.ColumnDescriptor; import io.vertx.sqlclient.impl.RowBase; import io.vertx.sqlclient.spi.connection.Connection; @@ -28,6 +29,8 @@ import org.junit.Test; import java.sql.JDBCType; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiConsumer; import java.util.stream.Collector; @@ -61,83 +64,87 @@ public JDBCType jdbcType() { } } - @Test - public void testSimple() { + private static Connection fakeConnection() { + return new Connection() { + @Override + public TracingPolicy tracingPolicy() { + return null; + } + @Override + public SocketAddress server() { + return null; + } + @Override + public String database() { + return ""; + } + @Override + public String user() { + return ""; + } + @Override + public ClientMetrics metrics() { + return null; + } + @Override + public void init(ConnectionContext context) { + } + @Override + public boolean isSsl() { + return false; + } + @Override + public boolean isValid() { + return true; + } + @Override + public int pipeliningLimit() { + return 1; + } + @Override + public DatabaseMetadata databaseMetadata() { + throw new UnsupportedOperationException(); + } + @Override + public void close(ConnectionContext holder, Completable promise) { + promise.succeed(); + } + @Override + public void schedule(CommandBase cmd, Completable handler) { + if (cmd instanceof SimpleQueryCommand) { + SimpleQueryCommand simpleQueryCmd = (SimpleQueryCommand) cmd; + scheduleQueryCommand(simpleQueryCmd, (Completable) handler); + } else { + handler.fail(new UnsupportedOperationException()); + } + } + private void scheduleQueryCommand(SimpleQueryCommand simpleQuery, Completable handler) { + QueryResultHandler qrh = simpleQuery.resultHandler(); + Collector collector = (Collector) simpleQuery.collector(); + A container = collector.supplier().get(); + BiConsumer accumulator = collector.accumulator(); + RowDescriptorBase rowDescriptor = new RowDescriptorBase(new ColumnDescriptor[]{new VarcharColumnDescriptor("value")}); + Row row = new RowBase(rowDescriptor); + row.addValue("Hello " + simpleQuery.sql()); + accumulator.accept(container, row); + T result = collector.finisher().apply(container); + qrh.handleResult(0, 1, rowDescriptor + , result, null); + handler.succeed(true); + } + }; + } - DriverBase driver = new DriverBase("generic") { + private static DriverBase createDriver( + java.util.function.Function> afterAcquire, + java.util.function.Function> beforeRecycle) { + return new DriverBase<>("generic", afterAcquire, beforeRecycle) { @Override public ConnectionFactory createConnectionFactory(Vertx vertx, NetClientOptions transportOptions) { return new ConnectionFactory<>() { @Override public Future connect(Context context, SqlConnectOptions options) { - return Future.succeededFuture(new Connection() { - @Override - public TracingPolicy tracingPolicy() { - return null; - } - @Override - public SocketAddress server() { - return null; - } - @Override - public String database() { - return ""; - } - @Override - public String user() { - return ""; - } - @Override - public ClientMetrics metrics() { - return null; - } - @Override - public void init(ConnectionContext context) { - } - @Override - public boolean isSsl() { - return false; - } - @Override - public boolean isValid() { - return true; - } - @Override - public int pipeliningLimit() { - return 1; - } - @Override - public DatabaseMetadata databaseMetadata() { - throw new UnsupportedOperationException(); - } - @Override - public void close(ConnectionContext holder, Completable promise) { - promise.succeed(); - } - @Override - public void schedule(CommandBase cmd, Completable handler) { - if (cmd instanceof SimpleQueryCommand) { - SimpleQueryCommand simpleQueryCmd = (SimpleQueryCommand) cmd; - scheduleQueryCommand(simpleQueryCmd, (Completable) handler); - } else { - handler.fail(new UnsupportedOperationException()); - } - } - private void scheduleQueryCommand(SimpleQueryCommand simpleQuery, Completable handler) { - QueryResultHandler qrh = simpleQuery.resultHandler(); - Collector collector = (Collector) simpleQuery.collector(); - A container = collector.supplier().get(); - BiConsumer accumulator = collector.accumulator(); - RowDescriptorBase rowDescriptor = new RowDescriptorBase(new ColumnDescriptor[]{new VarcharColumnDescriptor("value")}); - Row row = new RowBase(rowDescriptor); - row.addValue("Hello " + simpleQuery.sql()); - accumulator.accept(container, row); - T result = collector.finisher().apply(container); - qrh.handleResult(0, 1, rowDescriptor - , result, null); - handler.succeed(true); - } - }); + return Future.succeededFuture(fakeConnection()); } @Override public void close(Completable completable) { @@ -145,19 +152,27 @@ public void close(Completable completable) { } }; } + @Override public SqlConnectOptions parseConnectionUri(String uri) { throw new UnsupportedOperationException(); } + @Override public boolean acceptsOptions(SqlConnectOptions connectOptions) { return true; } + @Override public SqlConnectOptions downcast(SqlConnectOptions connectOptions) { return connectOptions; } }; + } + + @Test + public void testSimple() { + DriverBase driver = createDriver(null, null); Vertx vertx = Vertx.vertx(); @@ -177,4 +192,45 @@ public SqlConnectOptions downcast(SqlConnectOptions connectOptions) { vertx.close().await(); } } + + @Test + public void testAfterAcquireFailureReleasesConnection() { + AtomicBoolean shouldFail = new AtomicBoolean(true); + AtomicInteger beforeRecycleCount = new AtomicInteger(); + RuntimeException hookError = new RuntimeException("afterAcquire failed"); + + DriverBase driver = createDriver( + conn -> { + if (shouldFail.get()) { + return Future.failedFuture(hookError); + } + return Future.succeededFuture(); + }, + conn -> { + beforeRecycleCount.incrementAndGet(); + return Future.succeededFuture(); + }); + + Vertx vertx = Vertx.vertx(); + + try { + Pool pool = driver.createPool(vertx, () -> Future.succeededFuture(new SqlConnectOptions()), + new PoolOptions().setMaxSize(1), new NetClientOptions(), null); + + try { + pool.getConnection().await(); + fail("Should have failed"); + } catch (Exception e) { + assertEquals(hookError, e); + } + + shouldFail.set(false); + SqlConnection conn = pool.getConnection().await(); + assertNotNull(conn); + conn.close().await(); + assertEquals(1, beforeRecycleCount.get()); + } finally { + vertx.close().await(); + } + } }