Spring 使用 Hibernate Search 时,由于 lucene 锁定导致启动集成测试失败

Spring Boot Integration Test failes due to lucene lock when using Hibernate Search

在我的 Spring Boot 1.5.10.Final 项目中,我使用 Hibernate Search ORM 5.6.4.Final。除了集成测试,它工作正常。有一个测试 class 有几个 测试搜索逻辑的测试方法。如果我 运行 只是这个测试 class 一切正常。 Spring 启动正在启动并创建索引。如果我运行这个测试class 与所有其他集成测试一起,每个测试 class 将抛出 LockObtainFailedException 并且 Hibernate Search 测试将失败。

org.apache.lucene.store.LockObtainFailedException: Lock held by this virtual machine: ...LieferantEntity\write.lock
    at org.apache.lucene.store.NativeFSLockFactory.obtainFSLock(NativeFSLockFactory.java:127) ~[lucene-core-5.5.5.jar:5.5.5 b3441673c21c83762035dc21d3827ad16aa17b68 - sarowe - 2017-10-20 08:57:09]
    at org.apache.lucene.store.FSLockFactory.obtainLock(FSLockFactory.java:41) ~[lucene-core-5.5.5.jar:5.5.5 b3441673c21c83762035dc21d3827ad16aa17b68 - sarowe - 2017-10-20 08:57:09]
    at org.apache.lucene.store.BaseDirectory.obtainLock(BaseDirectory.java:45) ~[lucene-core-5.5.5.jar:5.5.5 b3441673c21c83762035dc21d3827ad16aa17b68 - sarowe - 2017-10-20 08:57:09]
    at org.apache.lucene.index.IndexWriter.<init>(IndexWriter.java:776) ~[lucene-core-5.5.5.jar:5.5.5 b3441673c21c83762035dc21d3827ad16aa17b68 - sarowe - 2017-10-20 08:57:09]
    at org.hibernate.search.backend.impl.lucene.IndexWriterHolder.createNewIndexWriter(IndexWriterHolder.java:126) ~[hibernate-search-engine-5.6.4.Final.jar:5.6.4.Final]
    at org.hibernate.search.backend.impl.lucene.IndexWriterHolder.getIndexWriter(IndexWriterHolder.java:92) ~[hibernate-search-engine-5.6.4.Final.jar:5.6.4.Final]
    at org.hibernate.search.backend.impl.lucene.AbstractWorkspaceImpl.getIndexWriter(AbstractWorkspaceImpl.java:117) ~[hibernate-search-engine-5.6.4.Final.jar:5.6.4.Final]
    at org.hibernate.search.backend.impl.lucene.AbstractWorkspaceImpl.getIndexWriterDelegate(AbstractWorkspaceImpl.java:203) ~[hibernate-search-engine-5.6.4.Final.jar:5.6.4.Final]
    at org.hibernate.search.backend.impl.lucene.LuceneBackendQueueTask.applyUpdates(LuceneBackendQueueTask.java:81) [hibernate-search-engine-5.6.4.Final.jar:5.6.4.Final]
    at org.hibernate.search.backend.impl.lucene.LuceneBackendQueueTask.run(LuceneBackendQueueTask.java:46) [hibernate-search-engine-5.6.4.Final.jar:5.6.4.Final]
    at org.hibernate.search.backend.impl.lucene.SyncWorkProcessor$Consumer.applyChangesets(SyncWorkProcessor.java:165) [hibernate-search-engine-5.6.4.Final.jar:5.6.4.Final]
    at org.hibernate.search.backend.impl.lucene.SyncWorkProcessor$Consumer.run(SyncWorkProcessor.java:151) [hibernate-search-engine-5.6.4.Final.jar:5.6.4.Final]
    at java.lang.Thread.run(Thread.java:745) [na:1.8.0_121]

我使用默认设置。如果我将 exclusive_index_use 设置为 false,那么它可以正常工作,但测试执行速度非常慢。 对我来说,索引似乎是在 Spring Boot 启动期间初始化的,并且会干扰测试。

是否可以将 Spring 引导集成测试与 Hibernate Search 一起使用,以便在测试之间干净地释放锁? 或者,我正在寻找一种方法来为所有不使用 Hibernate Search

的集成测试禁用 Hibernate Search 索引

我也已经尝试过 属性 近实时和不同的锁工厂作为原生的、简单的和单一的,没有运气。

首先:除非您是 Lucene 大师,否则不要使用 exclusive_index_use。这很危险,可能不会如你所愿。

既然我们已经解决了这个问题...据我了解,您正在尝试在同一台机器上并行执行集成测试。这意味着集成测试可能会竞争访问完全相同的索引,并将写入相同的索引。如果您的测试执行冲突写入(一个测试在另一个测试完成之前擦除另一个测试添加的文档),这可能会导致不可预测的结果。

如果您确实需要并行执行测试,我建议在隔离环境中执行每个测试:

  • 专用数据库,或至少专用数据库架构
  • 专用 Lucene 索引
  • 等等

对于 Hibernate Search,您将不得不找到一种方法在每次测试执行中使用不同的物理索引。

有两种方法可以做到这一点:

  1. 只是为了测试,不要将索引存储在文件系统上,而是直接在堆中,通过将 hibernate.search.backend.directory.type 设置为 local-heap(Hibernate Search 6+)或 hibernate.search.default.directory_providerlocal-heap(Hibernate Search 5 及以下版本)。

它非常容易实现,但您应该注意一些缺点:

  • 您的测试环境将不再与您的生产环境完全相同
  • 索引将在测试执行完成后丢失,这可能会使 post-mortem 调试具有挑战性(您将无法再使用 Luke 检查索引的状态)
  • 如果您的集成测试在索引中存储了大量内容,您可能会得到 OutOfMemoryError.
  1. 如果解决方案 1 的缺点对您来说太多了,您可以继续使用文件系统来存储索引,但对每个测试执行使用不同的配置,设置索引基本路径(hibernate.search.backend.directory.root for Hibernate Search 6+,或 hibernate.search.default.indexBase(对于 Hibernate Search 5 及以下版本)到每个测试执行的一些唯一路径。您将不得不在 Spring 中找到如何做到这一点,但我会惊讶地发现这是不可能的。也许 Spring 允许您在属性中使用插值,例如 hibernate.search.backend.directory.root = /tmp/it/#{testName}?

请参阅有关目录配置的文档(here for Hibernate Search 6+, or here for Hibernate Search 5 了解有关如何配置索引存储的更多信息。