Add Hibernate 7 batches 2-6, batch 7, and batch 8: mapping styles, JPA annotations, natural IDs, @Immutable, stored procedures, in-memory test databases, JNDI mocking, proxies, associations, temporal mapping, named queries, HQL, Criteria API, EntityManager bootstrapping, Ehcache 3 L2 cache configuration, HikariCP connection pooling, Hibernate Validator CDI integration, aggregate functions, sorting, pagination, interceptors, and Hibernate Search 8 (Hibernate 7.4.5.Final + Spring Boot 4.1.1 + JDK 25)
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
package com.ankurm.hibernatedemo.hikari;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.zaxxer.hikari.HikariConfig;
|
||||
import com.zaxxer.hikari.HikariDataSource;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLTransientConnectionException;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* The failure mode a pool-sizing article should lead with: what actually happens when every
|
||||
* connection in the pool is checked out and something else asks for one more. Not "it blocks
|
||||
* forever" and not a generic timeout -- HikariCP throws a specific, named exception with the pool
|
||||
* name and the timeout value baked into the message.
|
||||
*
|
||||
* <p>Docs: docs/19-hikaricp-connection-pooling.md
|
||||
*/
|
||||
class HikariPoolExhaustionTest {
|
||||
|
||||
private HikariDataSource dataSource;
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
if (dataSource != null) {
|
||||
dataSource.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void requestingOneMoreConnectionThanMaximumPoolSize_failsFastWithSqlTransientConnectionException()
|
||||
throws Exception {
|
||||
HikariConfig config = new HikariConfig();
|
||||
config.setJdbcUrl("jdbc:h2:mem:hikariexhaustion;DB_CLOSE_DELAY=-1");
|
||||
config.setUsername("sa");
|
||||
config.setPoolName("exhaustion-pool");
|
||||
config.setMaximumPoolSize(1);
|
||||
config.setConnectionTimeout(1000); // fail fast for the test, default is 30000ms
|
||||
dataSource = new HikariDataSource(config);
|
||||
|
||||
// Hold the pool's one and only connection open.
|
||||
Connection held = dataSource.getConnection();
|
||||
|
||||
long start = System.currentTimeMillis();
|
||||
Throwable thrown = catchThrowable(() -> dataSource.getConnection());
|
||||
long waitedMs = System.currentTimeMillis() - start;
|
||||
|
||||
System.out.println("RESULT[hikari-pool-exhaustion]: maximumPoolSize=1, connectionTimeout=1000ms | "
|
||||
+ "second getConnection() waited=" + waitedMs + "ms before throwing "
|
||||
+ thrown.getClass().getName() + ": " + thrown.getMessage());
|
||||
|
||||
assertThat(thrown).isInstanceOf(SQLTransientConnectionException.class);
|
||||
assertThat(thrown.getMessage())
|
||||
.as("the exception message names the pool and states it's a connection-timeout, "
|
||||
+ "not a generic SQL error -- this is what to grep application logs for")
|
||||
.contains("exhaustion-pool")
|
||||
.contains("Connection is not available");
|
||||
assertThat(waitedMs)
|
||||
.as("the caller waits roughly connectionTimeout, not forever and not instantly")
|
||||
.isGreaterThanOrEqualTo(900);
|
||||
|
||||
held.close();
|
||||
|
||||
// With the held connection released, a new request succeeds immediately.
|
||||
try (Connection recovered = dataSource.getConnection()) {
|
||||
assertThat(recovered.isValid(1)).isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
private static Throwable catchThrowable(ThrowingCallable callable) {
|
||||
try {
|
||||
callable.call();
|
||||
return null;
|
||||
} catch (Throwable t) {
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private interface ThrowingCallable {
|
||||
void call() throws Exception;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user