sequence

This paper mainly studies the fail Fast of HIKARI and Tomcat JDBC pool when the database is suspended in the middle of the process.

The experimental code

	@Test
	public void testDatabaseDownAndUp() throws SQLException, InterruptedException {
		LOGGER.info("begin to wait for database down and up");


		for(int c=0; c<10; c++){ LOGGER.info("execute:"+(c+1));
			Connection conn = null;

			String sql = "select 1";
			PreparedStatement pstmt = null;
			try {
				long start = System.currentTimeMillis();
				conn = dataSource.getConnection();
				LOGGER.info("{} got connection,cost:{}",(c+1),System.currentTimeMillis() - start);
				pstmt = (PreparedStatement)conn.prepareStatement(sql);
				ResultSet rs = pstmt.executeQuery();
				int col = rs.getMetaData().getColumnCount();
				while (rs.next()) {
					for (int i = 1; i <= col; i++) {
						System.out.print(rs.getObject(i));
					}
					System.out.println("");
				}
				LOGGER.info("= = = = = = = = = = = = = = = = = = = = = = = = = = = ="); } catch (Exception e) { e.printStackTrace(); } finally { //close resources DbUtils.closeQuietly(pstmt); DbUtils.closeQuietly(conn); } TimeUnit.SECONDS.sleep(10); }}Copy the code

Disconnect from the database after the first loop output and wait 20 or 30 seconds before resuming.

hikari

The 2018-01-30 15:35:16. 5852-668 the INFO [main] C.E.D emo. HikariDemoApplicationTests: The execute: 1 2018-01-30 15:35:26. 5852-854 the INFO [main] C.E.D emo. HikariDemoApplicationTests: The execute: 2 2018-01-30 15:36:06. 5852-867 the INFO [main] C.E.D emo. HikariDemoApplicationTests: The execute: 3 15:36:46 2018-01-30. 5852-878 the INFO [main] C.E.D emo. HikariDemoApplicationTests: execute: 4Copy the code

From this point of view, if the database is disconnected in mid-stream, Hikari keeps getting database links

Assuming the database never recovers, all business threads requesting database operations will block at connectionTimeout, which will occupy the worker thread

Hikari only has testOnBorrow function, which is a direct while loop. In timeout time, constantly connect with Borrow and validate. Validate successful to return or throw overtime SQLTransientConnectionException anomalies. The timeout for BORROW is connectionTimeout, which defaults to 30 seconds.

Validate Failed to log an exception

The 2018-01-31 16:45:00. 653 WARN 6501 - [the main] org. Postgresql. JDBC. PgConnection: Validating connection. org.postgresql.util.PSQLException: FATAL: terminating connection due to administratorcommandat org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2422) ~ [postgresql - 42.2.1 jar: 42.2.1] at org. Postgresql. Core. The v3. QueryExecutorImpl. ProcessResults (QueryExecutorImpl. Java: 2167) ~ [postgresql - 42.2.1 jar: 42.2.1] at org. Postgresql. Core. The v3. QueryExecutorImpl. Execute (QueryExecutorImpl. Java: 306) ~ [postgresql - 42.2.1 jar: 42.2.1] at org. Postgresql. JDBC. PgStatement. ExecuteInternal (PgStatement. Java: 441) ~ [postgresql - 42.2.1 jar: 42.2.1] at org. Postgresql. JDBC. PgStatement. Execute (PgStatement. Java: 365) ~ [postgresql - 42.2.1 jar: 42.2.1] the at Org. Postgresql. JDBC. PgPreparedStatement. ExecuteWithFlags (PgPreparedStatement. Java: 155) ~ [postgresql - 42.2.1. Jar: 42.2.1] The at org. Postgresql. JDBC. PgPreparedStatement. ExecuteUpdate (PgPreparedStatement. Java: 132) ~ [postgresql - 42.2.1. Jar: 42.2.1] The at org. Postgresql. JDBC. PgConnection. IsValid (PgConnection. Java: 1364) ~ [postgresql - 42.2.1. Jar: 42.2.1] the at Com. Zaxxer. Hikari. Pool. PoolBase. IsConnectionAlive (PoolBase. Java: 160) [HikariCP - 2.7.6. Jar: na] the at Com. Zaxxer. Hikari. Pool. HikariPool. GetConnection (HikariPool. Java: 171) [HikariCP - 2.7.6. Jar: na] the at Com. Zaxxer. Hikari. Pool. HikariPool. GetConnection (HikariPool. Java: 147) [HikariCP - 2.7.6. Jar: na] the at Com. Zaxxer. Hikari. HikariDataSource. GetConnection (HikariDataSource. Java: 123) [HikariCP - 2.7.6. Jar: na] the at com.example.demo.HikariDemoApplicationTests.testDatabaseDownAndUp(HikariDemoApplicationTests.java:109) [test- classes / : na] at sun reflect. NativeMethodAccessorImpl. Invoke0 (Native Method) ~ [na: 1.8.0 comes with _71] the at Sun. Reflect. NativeMethodAccessorImpl. Invoke (NativeMethodAccessorImpl. Java: 62) ~ [na: 1.8.0 comes with _71] the at Sun. Reflect. DelegatingMethodAccessorImpl. Invoke (43) DelegatingMethodAccessorImpl. Java: ~ [na: 1.8.0 comes with _71] the at Java. Lang. Reflect. Method. Invoke (497) Method. The Java: ~ [na: 1.8.0 comes with _71] at org. Junit. Runners. Model. FrameworkMethodThe $1RunReflectiveCall (FrameworkMethod. Java: 50) [junit 4.12. Jar: 4.12] at Org. Junit. Internal. Runners. Model. ReflectiveCallable. Run (ReflectiveCallable. Java: 12) [junit 4.12. Jar: 4.12] at Org. Junit. Runners. Model. FrameworkMethod. InvokeExplosively (FrameworkMethod. Java: 47) [junit 4.12. Jar: 4.12] at Org. Junit. Internal. Runners. Statements. InvokeMethod. Evaluate (InvokeMethod. Java: 17) [junit 4.12. Jar: 4.12] at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallba CKS. Java: 73) [spring - test - 5.0.3. The jar: 5.0.3. RELEASE] the at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallback S.j ava: 83) [spring - test - 5.0.3. The jar: 5.0.3. RELEASE] the at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.ja Va: 75) [spring - test - 5.0.3. The jar: 5.0.3. RELEASE] the at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java : 86) [spring - test - 5.0.3. The jar: 5.0.3. RELEASE] the at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) [spring - test - 5.0.3. RELEASE. The jar: 5.0.3. RELEASE] at org. Junit. Runners. ParentRunner. RunLeaf (ParentRunner. Java: 325) [junit 4.12. Jar: 4.12] at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) [spring - test - 5.0.3. RELEASE. The jar: 5.0.3. RELEASE] the at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) [spring - test - 5.0.3. RELEASE. The jar: 5.0.3. RELEASE] at org. Junit. Runners. ParentRunner$3. The run (ParentRunner. Java: 290) [junit 4.12. Jar: 4.12] at org. Junit. Runners. ParentRunnerThe $1. The schedule (ParentRunner. Java: 71) [junit 4.12. Jar: 4.12] at Org. Junit. Runners. ParentRunner. RunChildren (ParentRunner. Java: 288) [junit 4.12. Jar: 4.12] at org.junit.runners.ParentRunner.accessThe $000(ParentRunner. Java: 58) [junit 4.12. Jar: 4.12] at org. Junit. Runners. ParentRunner$2The evaluate (ParentRunner. Java: 268) [junit 4.12. Jar: 4.12] at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java : 61) [spring - test - 5.0.3. The jar: 5.0.3. RELEASE] the at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:7 0) [spring - test - 5.0.3. The jar: 5.0.3. RELEASE] at org. Junit. Runners. ParentRunner. Run (ParentRunner. Java: 363) [junit 4.12. Jar: 4.12] at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) [spring - test - 5.0.3. RELEASE. The jar: 5.0.3. RELEASE] at org. Junit. Runner. JUnitCore. Run (JUnitCore. Java: 137) [junit 4.12. Jar: 4.12] at the intellij. Takeup. JUnit4IdeaTestRunner. StartRunnerWithArgs (JUnit4IdeaTestRunner. Java: 69) [junit-rt.jar:na] at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) [junit-rt.jar:na] at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) [junit-rt.jar:na] at Sun. Reflect. NativeMethodAccessorImpl. Invoke0 (Native Method) ~ [na: 1.8.0 comes with _71] the at Sun. Reflect. NativeMethodAccessorImpl. Invoke (NativeMethodAccessorImpl. Java: 62) ~ [na: 1.8.0 comes with _71] the at Sun. Reflect. DelegatingMethodAccessorImpl. Invoke (43) DelegatingMethodAccessorImpl. Java: ~ [na: 1.8.0 comes with _71] the at Java. Lang. Reflect. Method. Invoke (497) Method. The Java: ~ [na: 1.8.0 comes with _71] the at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) [idea_rt.jar:na]Copy the code

If the maxPoolSize connection is set up before the test, and the database is interrupted after the test, the next getConnection will fetch the idle connections one by one, and the validate failure will be recorded in the log. Meanwhile, getConnection continues to loop for connections

GetConnection Timeout Throws an exception

java.sql.SQLTransientConnectionException: demo1 - Connection is not available, request timed out after 60007ms.
	at com.zaxxer.hikari.pool.HikariPool.createTimeoutException(HikariPool.java:666)
	at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:182)
	at com.zaxxer.hikari.pool.HikariPool.getConnection(HikariPool.java:147)
	at com.zaxxer.hikari.HikariDataSource.getConnection(HikariDataSource.java:123)
	at com.example.demo.HikariDemoApplicationTests.testDatabaseDownAndUp(HikariDemoApplicationTests.java:98)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at org.junit.runners.model.FrameworkMethodThe $1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallba cks.java:73) at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallback s.java:83) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.ja va:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java :86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunnerThe $1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.accessThe $000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java :61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:7 0) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)  Caused by: org.postgresql.util.PSQLException: Connection to 192.168.99.100:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections. at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:247) at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:49) at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:195) at org.postgresql.Driver.makeConnection(Driver.java:452) at org.postgresql.Driver.connect(Driver.java:254) at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:117) at com.zaxxer.hikari.util.DriverDataSource.getConnection(DriverDataSource.java:123) at com.zaxxer.hikari.pool.PoolBase.newConnection(PoolBase.java:375) at com.zaxxer.hikari.pool.PoolBase.newPoolEntry(PoolBase.java:204) at com.zaxxer.hikari.pool.HikariPool.createPoolEntry(HikariPool.java:459) at com.zaxxer.hikari.pool.HikariPool.accessThe $200(HikariPool.java:70)
	at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:696)
	at com.zaxxer.hikari.pool.HikariPool$PoolEntryCreator.call(HikariPool.java:682)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
Caused by: java.net.ConnectException: Connection refused
	at java.net.PlainSocketImpl.socketConnect(Native Method)
	at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350)
	at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206)
	at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188)
	at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392)
	at java.net.Socket.connect(Socket.java:589)
	at org.postgresql.core.PGStream.<init>(PGStream.java:69)
	at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:158)
	... 16 more
Copy the code

Loop connection timeout, it throws SQLTransientConnectionException

The asynchronous thread PoolEntryCreator supplements new connections in logging exceptions

2018-01-31 16:45:00.915 WARN 6501 -- [onnection adder] unknown.jul. Logger: ConnectException occurredwhileConnecting to 192.168.99.100:5432 java.net.ConnectException: Connection refused at java.net.PlainSocketImpl.socketConnect (Native Method) ~ [na: 1.8.0 comes with _71] the at Java.net.AbstractPlainSocketImpl.doConnect AbstractPlainSocketImpl. Java: (350) ~ [na: 1.8.0 comes with _71] the at Java.net.AbstractPlainSocketImpl.connectToAddress AbstractPlainSocketImpl. Java: (206) ~ [na: 1.8.0 comes with _71] the at Java.net.AbstractPlainSocketImpl.connect AbstractPlainSocketImpl. Java: (188) ~ [na: 1.8.0 comes with _71] the at Java.net.SocksSocketImpl.connect SocksSocketImpl. Java: (392) ~ [na: 1.8.0 comes with _71] at java.net.Socket.connect (589) Socket. Java: ~ [na: 1.8.0 comes with _71] at org. Postgresql. Core. PGStream. < init > (PGStream. Java: 69) ~ [postgresql - 42.2.1. Jar: 42.2.1] the at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:158) ~ [postgresql - 42.2.1 jar: 42.2.1] at org. Postgresql. Core. The ConnectionFactory. OpenConnection (49) ConnectionFactory. Java: [postgresql - 42.2.1. Jar: 42.2.1] at org. Postgresql. JDBC. PgConnection. < init > (PgConnection. Java: 195) [postgresql - 42.2.1. Jar: 42.2.1] at org. Postgresql. Driver. MakeConnection (452) Driver. Java: [postgresql - 42.2.1. Jar: 42.2.1] At org. Postgresql. Driver. Connect (Driver. Java: 254) [postgresql - 42.2.1. Jar: 42.2.1] the at Com. Zaxxer. Hikari. Util. DriverDataSource. GetConnection (DriverDataSource. Java: 117) [HikariCP - 2.7.6. Jar: na] the at Com. Zaxxer. Hikari. Util. DriverDataSource. GetConnection (DriverDataSource. Java: 123) [HikariCP - 2.7.6. Jar: na] the at Com. Zaxxer. Hikari. Pool. PoolBase. NewConnection (PoolBase. Java: 375) [HikariCP - 2.7.6. Jar: na] the at Com. Zaxxer. Hikari. Pool. PoolBase. NewPoolEntry (PoolBase. Java: 204) [HikariCP - 2.7.6. Jar: na] the at Com. Zaxxer. Hikari. Pool. HikariPool. CreatePoolEntry (HikariPool. Java: 459) [HikariCP - 2.7.6. Jar: na] the at com.zaxxer.hikari.pool.HikariPool.accessThe $200(HikariPool. Java: 70) [HikariCP - 2.7.6. Jar: na] at the zaxxer. Hikari. Pool. HikariPool$PoolEntryCreator. Call (696) HikariPool. Java: [HikariCP - 2.7.6. Jar: na] at the zaxxer. Hikari. Pool. HikariPool$PoolEntryCreator. Call (682) HikariPool. Java: [HikariCP - 2.7.6. Jar: na] at Java. Util. Concurrent. FutureTask. Run (FutureTask. Java: 266) [na: 1.8.0 comes with _71] at Java. Util. Concurrent. ThreadPoolExecutor. RunWorker (ThreadPoolExecutor. Java: 1142) [na: 1.8.0 comes with _71] the at java.util.concurrent.ThreadPoolExecutor$Worker. The run (ThreadPoolExecutor. Java: 617) [na: 1.8.0 comes with _71] at Java. Lang. Thread. The run (745) Thread. Java: [na: 1.8.0 comes with _71]Copy the code

Get the source code for the connection

HikariCP 2.7.6 – sources jar! /com/zaxxer/hikari/pool/HikariPool.java

   /**
    * Get a connection from the pool, or timeout after the specified number of milliseconds.
    *
    * @param hardTimeout the maximum time to wait for a connection from the pool
    * @return a java.sql.Connection instance
    * @throws SQLException thrown if a timeout occurs trying to obtain a connection
    */
   public Connection getConnection(final long hardTimeout) throws SQLException
   {
      suspendResumeLock.acquire();
      final long startTime = currentTime();

      try {
         long timeout = hardTimeout;
         do {
            PoolEntry poolEntry = connectionBag.borrow(timeout, MILLISECONDS);
            if (poolEntry == null) {
               break; // We timed out... break and throw exception
            }

            final long now = currentTime();
            if(poolEntry.isMarkedEvicted() || (elapsedMillis(poolEntry.lastAccessed, now) > ALIVE_BYPASS_WINDOW_MS && ! isConnectionAlive(poolEntry.connection))) { closeConnection(poolEntry, poolEntry.isMarkedEvicted() ? EVICTED_CONNECTION_MESSAGE : DEAD_CONNECTION_MESSAGE); timeout = hardTimeout - elapsedMillis(startTime); }else {
               metricsTracker.recordBorrowStats(poolEntry, startTime);
               returnpoolEntry.createProxyConnection(leakTaskFactory.schedule(poolEntry), now); }}while (timeout > 0L);

         metricsTracker.recordBorrowTimeoutStats(startTime);
         throw createTimeoutException(startTime);
      }
      catch (InterruptedException e) {
         Thread.currentThread().interrupt();
         throw new SQLException(poolName + " - Interrupted during connection acquisition", e);
      }
      finally {
         suspendResumeLock.release(); }}Copy the code

tomcat

The 2018-01-30 15:39:23. 5894-913 the INFO [main] com. Example. JpaDemoApplicationTests: The execute: 1 2018-01-30 15:39:33. 5894-939 the INFO [main] com. Example. JpaDemoApplicationTests: The execute: 2 2018-01-30 15:39:43. 5894-949 the INFO [main] com. Example. JpaDemoApplicationTests: The execute: 3 15:39:53 2018-01-30. 5894-955 the INFO [main] com. Example. JpaDemoApplicationTests: Execute: 4 15:40:03 2018-01-30. 5894-962 the INFO [main] com. Example. JpaDemoApplicationTests: The execute: 5 15:40:13 2018-01-30. 5894-968 the INFO [main] com. Example. JpaDemoApplicationTests: execute: 6Copy the code

Tomcat JDBC testOnBorrow pool, testOnReturn testOnConnect, when testWhileIdle attributes to configure the test connection, if is testOnBorrow, If there are idle connections, borrow and validate will be carried out at the same time. If the last validate time is within validation Interval, validate will be carried out by default; otherwise, validate will be carried out, and reconnect will be carried out if validate fails. New SQLException(“Failed to validate a newly established connection.”) If there are no free connections and the pool is not full, create a new one. If there are no free connections and the pool is full, the while polls the free queue, if no connection is taken, continues if maxWait is not exceeded, and throws poolhaustedexception if maxWait is exceeded. If there are no idle connections and the connection pool is full, the BORROW timeout is 30 seconds by maxWait default.

In this example program, every time the loop will return the connection and then sleep, so there are always idle connections in the connection pool, but testOnBorrow fails to check, so we perform reconnect and throw an exception in reconnect

Reconnect threw an exception

org.postgresql.util.PSQLException: Connection to 192.168.99.100:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections.
	at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:262)
	at org.postgresql.core.ConnectionFactory.openConnection(ConnectionFactory.java:51)
	at org.postgresql.jdbc.PgConnection.<init>(PgConnection.java:215)
	at org.postgresql.Driver.makeConnection(Driver.java:404)
	at org.postgresql.Driver.connect(Driver.java:272)
	at org.apache.tomcat.jdbc.pool.PooledConnection.connectUsingDriver(PooledConnection.java:310)
	at org.apache.tomcat.jdbc.pool.PooledConnection.connect(PooledConnection.java:203)
	at org.apache.tomcat.jdbc.pool.PooledConnection.reconnect(PooledConnection.java:361)
	at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:821)
	at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:651)
	at org.apache.tomcat.jdbc.pool.ConnectionPool.getConnection(ConnectionPool.java:198)
	at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:132)
	at com.example.JpaDemoApplicationTests.testDatabaseDownAndUp(JpaDemoApplicationTests.java:296)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:497)
	at org.junit.runners.model.FrameworkMethodThe $1.runReflectiveCall(FrameworkMethod.java:50) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.ja va:75) at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java :86) at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunnerThe $1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.accessThe $000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java :61) at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:7 0) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191) at org.junit.runner.JUnitCore.run(JUnitCore.java:137) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:497) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)  Caused by: java.net.ConnectException: Connection refused at java.net.PlainSocketImpl.socketConnect(Native Method) at java.net.AbstractPlainSocketImpl.doConnect(AbstractPlainSocketImpl.java:350) at java.net.AbstractPlainSocketImpl.connectToAddress(AbstractPlainSocketImpl.java:206) at java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:188) at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:392) at java.net.Socket.connect(Socket.java:589) at org.postgresql.core.PGStream.<init>(PGStream.java:61) at org.postgresql.core.v3.ConnectionFactoryImpl.openConnectionImpl(ConnectionFactoryImpl.java:144) ... 44 moreCopy the code

Get the source code for the connection

Tomcat JDBC – 8.5.23 – sources. The jar! /org/apache/tomcat/jdbc/pool/ConnectionPool.java

    /**
     * Thread safe way to retrieve a connection from the pool
     * @param wait - time to wait, overrides the maxWait from the properties,
     * set to -1 if you wish to use maxWait, 0 if you wish no wait time.
     * @param username The user name to use for the connection
     * @param password The password for the connection
     * @return a connection
     * @throws SQLException Failed to get a connection
     */
    private PooledConnection borrowConnection(int wait, String username, String password) throws SQLException {

        if (isClosed()) {
            throw new SQLException("Connection pool closed.");
        } //end if

        //get the current time stamp
        long now = System.currentTimeMillis();
        //see if there is one available immediately
        PooledConnection con = idle.poll();

        while (true) {
            if(con! =null) { //configure the connection andreturn it
                PooledConnection result = borrowConnection(now, con, username, password);
                borrowedCount.incrementAndGet();
                if(result! =null)returnresult; } / /if we get here, see if we need to create one
            //this is not 100% accurate since it doesn't use a shared //atomic variable - a connection can become idle while we are creating //a new connection if (size.get()  < getPoolProperties().getMaxActive()) { //atomic duplicate check if (size.addAndGet(1) > getPoolProperties().getMaxActive()) { //if we got here, two threads passed through the first if size.decrementAndGet(); } else { //create a connection, we're below the limit
                    return createConnection(now, con, username, password);
                }
            } //end if

            //calculate wait time for this iteration
            long maxWait = wait;
            //if the passed in wait time is -1, means we should use the pool property value
            if (wait==-1) { maxWait = (getPoolProperties().getMaxWait()<=0)? Long.MAX_VALUE:getPoolProperties().getMaxWait(); } long timetowait = Math.max(0, maxWait - (System.currentTimeMillis() - now)); waitcount.incrementAndGet(); try { //retrieve an existing connection con = idle.poll(timetowait, TimeUnit.MILLISECONDS); } catch (InterruptedException ex) {if (getPoolProperties().getPropagateInterruptState()) {
                    Thread.currentThread().interrupt();
                }
                SQLException sx = new SQLException("Pool wait interrupted.");
                sx.initCause(ex);
                throw sx;
            } finally {
                waitcount.decrementAndGet();
            }
            if (maxWait==0 && con == null) { //no wait.return one if we have one
                if(jmxPool! =null) { jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY,"Pool empty - no wait.");
                }
                throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"]" +
                        "NoWait: Pool empty. Unable to fetch a connection, none available["+busy.size()+" in use].");
            }
            //we didn't get a connection, lets see if we timed out if (con == null) { if ((System.currentTimeMillis() - now) >= maxWait) { if (jmxPool! =null) { jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.POOL_EMPTY, "Pool empty - timeout."); } throw new PoolExhaustedException("[" + Thread.currentThread().getName()+"] " + "Timeout: Pool empty. Unable to fetch a connection in " + (maxWait / 1000) + " seconds, none available[size:"+size.get() +";  busy:"+busy.size()+"; idle:"+idle.size()+"; lastwait:"+timetowait+"]."); } else { //no timeout, lets try again continue; } } } //while }Copy the code

summary

If there are no idle connections and the connection pool is full, new connections cannot be created

  • Hikari is blocking the connectionTimeout time, throw SQLTransientConnectionException didn’t get the connection
  • Tomcat JDBC pool blocks max-wait time and throws poolhaustedexception if no connection is made

If there are idle connections

  • Hikari is in the connectionTimeout time cycle for the next free connection calibration, the calibration failure continues to get a free connection, raise SQLTransientConnectionException until timeout
  • Tomcat JDBC pool will connect to borrow and validate the connection. If the reconnect fails, connect to reconnect and validate the connection. SQLException(“Failed to validate a newly established Connection.”)

Tomcat testOnBorrow=true, assuming the database is hung but still has free connections. The comparison shows that

When hikari obtains a connection, it will loop validate the idle connections one by one within connectionTimeout, and finally timeout throws an exception. Subsequent connections will block the connectionTimeout time and then throw an exception

If the connection fails to reconnect, the connection will reconnect. If the connection fails to reconnect, the connection will reconnect. The Connection refused will fail fast. The Connection refused will fail fast

If hikari connectionTimeout is too large, it will block the service thread when the database is suspended, and the Tomcat JDBC pool will fail fast.

In addition, hikari asynchronously establishes minIdle connections, while Tomcat synchronously establishes initial-size connections.