我正在对我们的数据库执行压力测试,该数据库在 MySQL 上处于 hibernate 状态。我使用默认配置的 c3p0 连接池,最大池大小为 15 除外。
interface EntityRepository extends JpaRepository<Entity, UUID> {}
@Service
public class EntityService {
@Autowired
EntityRepository er;
@Transactional(propagation = Propagation.REQUIRED)
public Entity addEntity(Entity r) {
er.save(r);
}
}
public class StressTest {
@Autowired
EntityService rs;
@Test
public void entityStressTest() {
for(int i = 0; i < 100; i++) {
Thread t = new Thread(new Runnable() {
public void run() {
rs.addEntity(new Entity());
}
}
t.start();
}
}
}
每次运行此测试时,我都会创建 5-8 个实体,然后收到以下三个日志消息之一:
[错误] 14:39:23,127 [Thread-20] SqlExceptionHelper - 以下故障引发了 SQLException:com.mchange.v2.resourcepool.ResourcePoolException:尝试使用已关闭或损坏的资源池
[ INFO] 14:48:45,478 [Thread-11] JdbcTransaction - HHH000425:无法关闭 session ;吞咽异常[org.hibernate.service.UnknownServiceException:事务完成时请求未知服务[org.hibernate.stat.spi.StatisticsImplementor]]
[ INFO] 14:49:22,860 [Thread-18] BasicResourcePool - com.mchange.v2.resourcepool.BasicResourcePool@16f7ca - 尝试 checkout 资源被中断,因为池现已关闭。 [主题:主题 18]
我很困惑到底是什么原因造成的
最佳答案
你有竞争条件;这就是导致关闭连接池错误的原因。
在您的测试方法中,您正在创建异步使用连接池的新线程,但测试方法本身并不等待它们完成。因此,在测试方法完成时,其中一些线程可能尚未完成(我猜想很有可能有 100 个线程)。测试方法完成后,Spring 将关闭连接池,因此当线程随后尝试使用该池时,它将出错。
如果您为记录器org.springframework
打开DEBUG
(或可能TRACE
)日志记录并将日志语句添加到您的addEntity(...)
方法,您应该看到它在测试方法完成并且连接池 bean 销毁发生后被调用。
要解决此问题,请使用 CountDownLatch 让主测试方法在返回之前等待线程完成工作:
@Test
public void entityStressTest() {
int numThreads = 100;
final CountDownLatch counter = new CountDownLatch(numThreads);
for(int i = 0; i < numThreads; i++) {
Thread t = new Thread(new Runnable() {
public void run() {
rs.addEntity(new Entity());
counter.countDown();
}
}
t.start();
}
counter.await();
}
关于Hibernate 资源池关闭过早,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14945970/