可搜索的索引锁定在手动更新(LockoBtainFailDexception)上



我们有一个在负载平衡器后面运行的Grails项目。服务器上运行的Grails应用程序有三个实例(使用单独的TOMCAT实例)。每个实例都有自己的可搜索索引。由于索引是独立的,因此自动更新不足以使应用程序实例之间的索引一致。因此,我们禁用了可搜索的索引镜像,并且对索引的更新是在计划的石英作业中手动完成的。根据我们的理解,应用程序的其他部分应修改索引。

石英作业每分钟运行一次,并从应用程序更新的数据库中检查,并重新索引这些对象。该作业还检查同一作业是否已经在运行,因此不会同时进行索引。该应用程序在启动后运行良好几个小时,然后在作业启动时突然进行,锁定了LockObtainFailDexception:

22.10.2012 11:20:40 [xxxx.reindexjob]错误无法更新可搜索的索引,class org.compass.core.engine.searchengine.engine.searchengineException: 未能打开Sub Index [Product]的作者;嵌套异常是 org.apache.lucene.store.lockobtainfainfailedexception:锁定获得时间 出去: simplefslock@/home/xxx/tomcat/searchable-index/index/product/lucene-a7bbc72a49512284f54f54f54f54dd7d32849-write.lock

根据日志上次执行作业时,重新索引是没有任何错误完成的,并且工作成功完成。尽管如此,这次重新索引操作会引发锁定异常,就好像先前的操作未完成,并且锁定尚未释放。在应用程序重新启动之前,不会释放锁。

我们试图通过手动打开锁定索引来解决问题,这导致以下错误打印到日志:

22.10.2012 11:21:30 [Manager.IndexWritersManager]错误状态,将索引作者标记为开放,而另一个则标记为 为子索引开放[产品]

此后,这项工作似乎正常工作,并且不会再次陷入锁定状态。但是,这导致应用程序不断使用CPU资源的100%。以下是石英作业代码的缩短版本。

任何帮助都将不胜感激,以解决问题,谢谢。

class ReindexJob {
def compass
...
static Calendar lastIndexed
static triggers = {
    // Every day every minute (at xx:xx:30), start delay 2 min
    // cronExpression:                           "s  m h D M W [Y]"
    cron name: "ReindexTrigger", cronExpression: "30 * * * * ?", startDelay: 120000
}
def execute() {
    if (ConcurrencyHelper.isLocked(ConcurrencyHelper.Locks.LUCENE_INDEX)) {
        log.error("Search index has been locked, not doing anything.")
        return
    }
    try {
        boolean acquiredLock = ConcurrencyHelper.lock(ConcurrencyHelper.Locks.LUCENE_INDEX, "ReindexJob")
        if (!acquiredLock) {
            log.warn("Could not lock search index, not doing anything.")
            return
        }
        Calendar reindexDate = lastIndexed
        Calendar newReindexDate = Calendar.instance
        if (!reindexDate) {
            reindexDate = Calendar.instance
            reindexDate.add(Calendar.MINUTE, -3)
            lastIndexed = reindexDate
        }
        log.debug("+++ Starting ReindexJob, last indexed ${TextHelper.formatDate("yyyy-MM-dd HH:mm:ss", reindexDate.time)} +++")
        Long start = System.currentTimeMillis()
        String reindexMessage = ""
        // Retrieve the ids of products that have been modified since the job last ran
        String productQuery = "select p.id from Product ..."
        List<Long> productIds = Product.executeQuery(productQuery, ["lastIndexedDate": reindexDate.time, "lastIndexedCalendar": reindexDate])
        if (productIds) {
            reindexMessage += "Found ${productIds.size()} product(s) to reindex. "
            final int BATCH_SIZE = 10
            Long time = TimeHelper.timer {
                for (int inserted = 0; inserted < productIds.size(); inserted += BATCH_SIZE) {
                    log.debug("Indexing from ${inserted + 1} to ${Math.min(inserted + BATCH_SIZE, productIds.size())}: ${productIds.subList(inserted, Math.min(inserted + BATCH_SIZE, productIds.size()))}")
                    Product.reindex(productIds.subList(inserted, Math.min(inserted + BATCH_SIZE, productIds.size())))
                    Thread.sleep(250)
                }
            }
            reindexMessage += " (${time / 1000} s). "
        } else {
            reindexMessage += "No products to reindex. "
        }
        log.debug(reindexMessage)
        // Re-index brands
        Brand.reindex()
        lastIndexed = newReindexDate
        log.debug("+++ Finished ReindexJob (${(System.currentTimeMillis() - start) / 1000} s) +++")
    } catch (Exception e) {
        log.error("Could not update searchable index, ${e.class}: ${e.message}")
        if (e instanceof org.apache.lucene.store.LockObtainFailedException || e instanceof org.compass.core.engine.SearchEngineException) {
            log.info("This is a Lucene index locking exception.")
            for (String subIndex in compass.searchEngineIndexManager.getSubIndexes()) {
                if (compass.searchEngineIndexManager.isLocked(subIndex)) {
                    log.info("Releasing Lucene index lock for sub index ${subIndex}")
                    compass.searchEngineIndexManager.releaseLock(subIndex)
                }
            }
        }
    } finally {
        ConcurrencyHelper.unlock(ConcurrencyHelper.Locks.LUCENE_INDEX, "ReindexJob")
    }
}
}

基于JMX CPU样本,Compass似乎在幕后进行一些安排。从1分钟的CPU样品开始,似乎很少有不同的情况在正常情况下和100%的CPU实例:

  • org.apache.lucene.index.indexwriter.dowait()使用大多数CPU时间。
  • 指南针计划的执行器线程显示在线程列表中,这在正常情况下没有看到。
  • 一个指南针执行人线程正在执行提交,在正常情况下,这些线程都没有做commermerge。

您可以尝试增加'Compass.transaction.locktimeout'设置。默认值为10(秒)。

另一种选择是禁用指南针并使其同步。这是用" Compass.transaction.processor.read_committit.concurrentoperations':" false"设置来控制的。您可能还必须将" Compass.Transaction.Processor"设置为" read_comments"

这些是我们目前正在使用的指南针设置:

compassSettings = [
'compass.engine.optimizer.schedule.period': '300',
'compass.engine.mergeFactor':'1000',
'compass.engine.maxBufferedDocs':'1000',
'compass.engine.ramBufferSize': '128',
'compass.engine.useCompoundFile': 'false',
'compass.transaction.processor': 'read_committed',
'compass.transaction.processor.read_committed.concurrentOperations': 'false',
'compass.transaction.lockTimeout': '30',
'compass.transaction.lockPollInterval': '500',
'compass.transaction.readCommitted.translog.connection': 'ram://'
]

这有并发关闭。您可以通过更改" Compass.Transaction.Processor.Read_Committing.Concurrentoperations"设置为" true"来使其异步。(或删除条目)。

指南针配置参考:http://static.compassframework.org/docs/latest/core-configuration.html

READ_COMMITS处理器并发的文档:http://www.compass-prox-project.org/docs/latest/reference/html/core-searchengine.html#core-searchengine-transaction-transaction-read_comment

如果要保持异步操作,也可以控制其使用的线程数。使用Compass.Transaction.Processor.Read_Committing.ConcurrencyLevel = 1设置将允许使用异步操作,但只需使用一个线程(默认为5个线程)。还有Compass.transaction.processor.read_committing.backlog和Compass.transaction.processor.read_committing.AddTimeOut设置。

我希望这会有所帮助。

最新更新