Search code examples
spring-boothikaricptestcontainers

Hikari Pool is not shutdown after spring boot test execution with test container


I have a Postgres test container with a maximum of 100 connections. The test container is reused for different tests.

   @Bean
   @ServiceConnection
   @RestartScope
   public PostgreSQLContainer<?> postgreSQLContainer() {
      return createpostgreSQLContainer( dockerImageName );
   }

The Hikari Pool has a maximum of 25 connections.

If I now run more than 4 Spring Boot test classes in a row, I see that a Hikari pool with 25 connections is created for each test class. However, these pools will no longer be closed also when the test is over. After more than 4 tests, all connections are used up and the tests fail because they can no longer acquire a connection.

How to solve the problem?

EDIT: I once built an MRE that shows the problem. If you run them all TOGETHER in IDE(e.g. Intellij) or maven, you will see the error messages in the logs. The problem is that 2-3 Hikarki pools are opened with 55 connections. But the TestContainer can only open 100.

What I've found out so far is the following:

  1. If you only do repository testing WITHOUT a RestController, then only one HikarkiPool will be created and reused for all tests.
  2. As soon as you add a RestController, the Hikarki pool is no longer reused and multiple ones are created. Then the problem occurs.


Solution

  • The problem is your tests.

    @ExtendWith( SpringExtension.class )
    @SpringBootTest( 
      classes = { 
        TestConfig.class, 
        SQLContainerConfiguration.class,
        TestWebConfig.class }, 
      webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT )
    @ActiveProfiles( "test" )
    @EnableAutoConfiguration
    class ConnectionPoolIssueTest1 {
    
    @ExtendWith( SpringExtension.class )
    @SpringBootTest( 
      classes = { 
        TestConfig.class, 
        SQLContainerConfiguration.class }, 
      webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT )
    @ActiveProfiles( "test" )
    @EnableAutoConfiguration
    class ConnectionPoolIssueTest2 {
    
    @ExtendWith( SpringExtension.class )
    @SpringBootTest( 
      classes = { 
        TestConfig.class, 
        SQLContainerConfiguration.class,
        TestWebConfig.class }, 
      webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT )
    @ActiveProfiles( "test" )
    @EnableAutoConfiguration
    class ConnectionPoolIssueTest3 {
    
    @ExtendWith( SpringExtension.class )
    @SpringBootTest( 
      classes = { 
        TestConfig.class, 
        SQLContainerConfiguration.class }, 
      webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT )
    @ActiveProfiles( "test" )
    @EnableAutoConfiguration
    class ConnectionPoolIssueTest4 {
    
    @ExtendWith( SpringExtension.class )
    @SpringBootTest( 
      classes = { 
        TestConfig.class, 
        SQLContainerConfiguration.class,
        TestWebConfig.class }, 
      webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT )
    @ActiveProfiles( "test" )
    @EnableAutoConfiguration
    class ConnectionPoolIssueTest5 {
    
    @ExtendWith( SpringExtension.class )
    @SpringBootTest( 
      classes = { 
        TestConfig.class, 
        SQLContainerConfiguration.class,
        TestWebConfig.class }, 
      webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT )
    @ActiveProfiles( "test" )
    @EnableAutoConfiguration
    class ConnectionPoolIssueTest6 {
    

    As explained in the Spring Framework Reference Guide the testing support in Spring will use context caching. To determine a key there is quite a long list of things that influence it.

    An ApplicationContext can be uniquely identified by the combination of configuration parameters that is used to load it. Consequently, the unique combination of configuration parameters is used to generate a key under which the context is cached. The TestContext framework uses the following configuration parameters to build the context cache key:

    locations (from @ContextConfiguration)

    classes (from @ContextConfiguration)

    contextInitializerClasses (from @ContextConfiguration)

    contextCustomizers (from ContextCustomizerFactory) – this includes @DynamicPropertySource methods as well as various features from Spring Boot’s testing support such as @MockBean and @SpyBean.

    contextLoader (from @ContextConfiguration)

    parent (from @ContextHierarchy)

    activeProfiles (from @ActiveProfiles)

    propertySourceDescriptors (from @TestPropertySource)

    propertySourceProperties (from @TestPropertySource)

    resourceBasePath (from @WebAppConfiguration)

    In your test setup there are at least 2 different permutations of configuration. Due to differences in the classes one with 2 (TestConfig and SQLContainerConfiguration and one with 3 classes (TestConfig, SQLContainerConfigurationandTestWebConfig`). This will result in at least 2 different instances.

    TIP: You don't need @ExtendWith nor @EnableAutoConfiguration that is only overhead.