我有一个在Hibernate之上使用Spring的项目。 HSQLDB充当JUnit单元测试的数据库。经常(50%的时间)我们的构建在达到特定端点后挂起。
我测试的一个端点会在退出之前触发异步线程以进行一些清理工作。它不能从端点的开头触发,因为它取决于主线程的结果。请考虑以下代码:
@Transactional
public fooObj findCustomerFoo(Customer cust) {
CustFooCollection custFooCollection = otherService.getCustomerFoo(cust);
cleanupFooService.cleanupFoo(custFooCollection.getBadFoo());
return custFooCollection.getGoodFoo(); //return to the webservice layer that
///populates the response object and kicks it out
}
然后在CleanupFooService中你有了cleanupFoo方法:
@Async
@Transactional
public void cleanupFoo(List<Foo> foo) {
//1) Pulls foo from DB
//2) Runs state change and business logic on foo
//3) Saves objects back in DB
}
代码的想法是findCustomerFoo触发所有必需的代码来拉取我所有的foo对象。在此期间,一些过滤继续进行,并且foos被过滤成两个不同的类别,返回调用者的好foos和需要状态更改和更新的坏foos。
出于性能原因,我们不希望在请求中进行清理,因为那里有一些逻辑和双重检查。这可以安全地发生在幕后。
我们的构建机器最近有一些变化,它暴露了我们的单元测试的问题。通常当这个端点作为测试的一部分被命中时,主线程(运行findCustomerFoo)在第二个线程(cleanupFoo)之前完成。当发生这种情况时,构建会挂起。在日志中我可以看到cleanupFoo已经开始调用数据库但尚未返回。我不知道查询是否在飞行中并等待数据库响应,如果hibernate仍在准备查询,或者hibernate仍在处理结果。我知道该线程当前在存储库方法中来处理进入数据库的过程。
在我们成功的构建中,我可以看到主线程在异步线程之后完成。这是由于Web服务层末尾的日志。在挂起测试中,此日志发生在异步线程中存在的日志之前。
此代码已在生产中运行一段时间而没有打嗝。
我的运行理论是,由于主线程完成JUnit / Spring开始取消HSQLDB导致第二个线程挂起。因为它仍然看到运行代码的线程,它不允许下一个单元测试或类开始运行。
我该怎么做才能阻止这些挂起?最初的测试用例作者将内存数据库在@Before的annoitated测试类中进行了初始化,然后在.sql文件中存在数据。我修复了那些指向xml配置中描述的数据源并使用ScriptUtils来运行种子数据文件。我的理论是,如果我的数据源的设置和拆除由弹簧100%控制,它可能会在退出之前识别线程的存在。但是,执行此操作并删除关闭数据源的后测试方法不起作用。
答案 0 :(得分:0)
首先,数据库必须在操作开始之前处于活动状态,并且在此期间不会关闭。
如果与HSQLDB的连接似乎挂起,那是因为一个连接在一个事务中而另一个连接正在等待它完成。确保每组操作都是提交或回滚的事务。请注意,如果线程正在执行DDL语句,它将等待所有其他连接都已提交,然后在每个DDL操作期间锁定整个数据库。