Spring Boot 关闭MyManagerFactory非常缓慢

esyap4oy  于 5个月前  发布在  Spring
关注(0)|答案(2)|浏览(34)

My Sping Boot v3.2.0应用使用JDK 21。当测试套件完成时,关闭已创建的EntityManagerFactory bean需要30秒以上,约占测试套件总执行时间的33%。日志显示其中3个bean需要11秒关闭,并且似乎大部分时间都花在等待相关的Hikari连接池关闭上。
当应用程序本身关闭时,不会发生这种延迟。这可能是因为应用程序只创建了此bean的1个示例,但测试套件创建了14个示例(因为测试套件构建的应用程序上下文不同)。
当测试运行时,是否有方法:

  • 减少创建的EntityManagerFactory bean的数量
  • 减少关闭EntityManagerFactory bean所需的时间

测试使用以下Hikari配置

spring.datasource.hikari.maximumPoolSize=2

字符串
测试套件日志的相关部分如下所示

2023-12-07T15:53:41.555Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-12-07T15:53:41.556Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown initiated...
2023-12-07T15:53:41.557Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Shutdown completed.
2023-12-07T15:53:52.831Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-12-07T15:53:52.832Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Shutdown initiated...
2023-12-07T15:53:52.834Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-2 - Shutdown completed.
2023-12-07T15:54:03.861Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-12-07T15:54:03.863Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-3 - Shutdown initiated...
2023-12-07T15:54:03.865Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-3 - Shutdown completed.
2023-12-07T15:54:03.886Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-12-07T15:54:03.887Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-4 - Shutdown initiated...
2023-12-07T15:54:03.888Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-4 - Shutdown completed.
2023-12-07T15:54:03.899Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-12-07T15:54:03.899Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-5 - Shutdown initiated...
2023-12-07T15:54:03.900Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-5 - Shutdown completed.
2023-12-07T15:54:14.916Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-12-07T15:54:14.917Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-6 - Shutdown initiated...
2023-12-07T15:54:14.919Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-6 - Shutdown completed.
2023-12-07T15:54:14.938Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-12-07T15:54:14.939Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-7 - Shutdown initiated...
2023-12-07T15:54:14.940Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-7 - Shutdown completed.
2023-12-07T15:54:14.953Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-12-07T15:54:14.954Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-8 - Shutdown initiated...
2023-12-07T15:54:14.954Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-8 - Shutdown completed.
2023-12-07T15:54:14.955Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-12-07T15:54:14.955Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-9 - Shutdown initiated...
2023-12-07T15:54:14.956Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-9 - Shutdown completed.
2023-12-07T15:54:14.960Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-12-07T15:54:14.960Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-10 - Shutdown initiated...
2023-12-07T15:54:14.960Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-10 - Shutdown completed.
2023-12-07T15:54:14.964Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-12-07T15:54:14.964Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-11 - Shutdown initiated...
2023-12-07T15:54:14.965Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-11 - Shutdown completed.
2023-12-07T15:54:14.968Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-12-07T15:54:14.968Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-12 - Shutdown initiated...
2023-12-07T15:54:14.969Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-12 - Shutdown completed.
2023-12-07T15:54:14.972Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-12-07T15:54:14.972Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-13 - Shutdown initiated...
2023-12-07T15:54:14.972Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-13 - Shutdown completed.
2023-12-07T15:54:14.975Z --- j.LocalContainerEntityManagerFactoryBean : Closing JPA EntityManagerFactory for persistence unit 'default'
2023-12-07T15:54:14.976Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-14 - Shutdown initiated...
2023-12-07T15:54:14.992Z --- com.zaxxer.hikari.HikariDataSource       : HikariPool-14 - Shutdown completed.

> Task :test

SUCCESS: Executed 706 tests in 1m 37s (2 skipped)

lsmd5eda

lsmd5eda1#

你的问题提供了很多先决条件,但是让我们试着弄清楚我们能做些什么。
首先
减少所创建的LogistyManagerFactory bean的数量
EntityManagerFactory是单例的,所以每个应用程序上下文只创建一个bean。如果你有多个BifactyManagerFactory,这表明你有相同数量的应用程序上下文。
所以首先让我们尝试通过缓存来减少它们的数量。我会提取一个抽象类并在其上放置公共注解,例如:

@SpringBootTest
public abstract class BaseTest {
}

字符串
现在,每个涉及Sping Boot 的测试类都应该是TestBase的子类,这将允许缓存应用程序上下文并重用它:

class MyRepositoryTest extends BaseTest {
//...
}

class AnotherRepositoryTest extends BaseTest {
//...
}

class OneMoreRepositoryTest extends BaseTest {
//...
}


如果需要,您可以创建多个抽象测试类,以便将具有相同应用程序上下文的测试分组,例如:

@DataJpaTest
@SpringBootTest
public abstract class BaseJpaRepoTest {
}

@SpringBootTest
@AutoConfigureMockMvc
public abstract class BaseControllerTest {
}


使用这种方法,您将为每组测试(用于存储库、端点等)缓存一个应用程序上下文。
然后,作为这个属性:

spring.datasource.hikari.maximumPoolSize=2


我不认为你应该为测试配置池大小,所以让我们尝试删除这个属性并使用默认值。从另一个Angular 来看,这个属性也是特定于上下文的,因此,如果您有n应用程序上下文,则连接的总数为2*n。假设我们已将上下文的数量减少到1,并且所有测试都重用它,我认为2可能不够做测试
我想到的另一件事是,所有的连接都开始同时关闭,所以看起来你正在以并行模式运行测试。这可能会导致问题,例如,所有的连接都处理同一个表并且是事务性的,或者在@AfterEach中进行一些繁重的操作/清理。
你的步骤是:
1.高速缓存应用上下文
1.摆脱自定义池配置
1.注意事务性/拆卸方法
P.S.如果这没有帮助,请尝试在SessionFactoryImpl.close()中放置一些断点:

// AbstractEntityManagerFactoryBean
@Override
public void destroy() {
    if (this.entityManagerFactory != null) {
        if (logger.isInfoEnabled()) {
            logger.info("Closing JPA EntityManagerFactory for persistence unit '" + getPersistenceUnitName() + "'");
        }
        this.entityManagerFactory.close();
    }
}
//SessionFactoryImpl
public void close() throws HibernateException {
    synchronized (this) {
        if ( status != Status.OPEN ) {
            if ( getSessionFactoryOptions().getJpaCompliance().isJpaClosedComplianceEnabled() ) {
                throw new IllegalStateException( "EntityManagerFactory is already closed" );
            }

            LOG.trace( "Already closed" );
            return;
        }

        status = Status.CLOSING;
    }

    try {
        LOG.closing();
        observer.sessionFactoryClosing( this );

    // NOTE : the null checks below handle cases where close is called from
    //      a failed attempt to create the SessionFactory

        if ( cacheAccess != null ) {
            cacheAccess.close();
        }

        if ( runtimeMetamodels != null && runtimeMetamodels.getMappingMetamodel() != null ) {
            final JdbcConnectionAccess jdbcConnectionAccess = jdbcServices.getBootstrapJdbcConnectionAccess();
            runtimeMetamodels.getMappingMetamodel().forEachEntityDescriptor(
                    entityPersister -> {
                        if ( entityPersister.getSqmMultiTableMutationStrategy() != null ) {
                            entityPersister.getSqmMultiTableMutationStrategy().release(
                                    this,
                                    jdbcConnectionAccess
                            );
                        }
                        if ( entityPersister.getSqmMultiTableInsertStrategy() != null ) {
                            entityPersister.getSqmMultiTableInsertStrategy().release(
                                    this,
                                    jdbcConnectionAccess
                            );
                        }
                    }
            );
            ( (MappingMetamodelImpl) runtimeMetamodels.getMappingMetamodel() ).close();
        }

        if ( queryEngine != null ) {
            queryEngine.close();
        }
        if ( eventEngine != null ) {
            eventEngine.stop();
        }
    }
    finally {
        status = Status.CLOSED;
    }

    observer.sessionFactoryClosed( this );
    serviceRegistry.destroy();
}


这里有多个循环,AbstractServiceRegistryImpl中有一个:

@Override
public synchronized void destroy() {
    if ( active.compareAndSet( true, false ) ) {
        try {
            //First thing, make sure that the fast path read is disabled so that
            //threads not owning the synchronization lock can't get an invalid Service:
            initializedServiceByRole.clear();
            synchronized (serviceBindingList) {
                ListIterator<ServiceBinding<?>> serviceBindingsIterator = serviceBindingList.listIterator(
                        serviceBindingList.size()
                );
                while ( serviceBindingsIterator.hasPrevious() ) {
                    final ServiceBinding<?> serviceBinding = serviceBindingsIterator.previous();
                    serviceBinding.getLifecycleOwner().stopService( serviceBinding );
                }
                serviceBindingList.clear();
            }
            serviceBindingMap.clear();
        }
        finally {
            if ( parent != null ) {
                parent.deRegisterChild( this );
            }
        }
    }
}

jmo0nnb3

jmo0nnb32#

升级hibernatejar,并开始使用BOM导入进行 Boot 。
我们需要更多的信息来了解您的Hikari Pool是如何工作的。您可以随时调用您正在创建的池的数量和大小。如果自动EMF的池很重而且很慢,请通过创建大的池大小来配置您自己的池。确保它们在池之间没有空闲。

hikari:
      connection-timeout: 
      minimum-idle: 
      maximum-pool-size: to define pool size
      idle-timeout: 
      max-lifetime: 
      auto-commit: true
      driver-class-name: com.microsoft.sqlserver.jdbc.SQLServerDriver
      leak-detection-threshold:

字符串

相关问题