删除标记文档的XQuery查询有时会无限期地运行

时间:2014-09-05 01:35:25

标签: grails xquery marklogic

我有一个XQuery查询,用于在每次运行的测试之前从数据库中擦除测试文档。本质上,它会查找某个元素作为文档中的顶级元素(称为“forTestOnly”),如果找到它则会删除该文档。此查询在每次测试之前运行,以确保测试不会相互干扰(我们使用此测试进行大约200次测试)。确切的XQuery是这样的:

xquery version "1.0-ml";

import module namespace dls = "http://marklogic.com/xdmp/dls" at "/MarkLogic/dls.xqy";

let $deleteNonManagedDocs := for $testDoc in /*[forTestOnly]
    let $testDocUri := fn:base-uri($testDoc)
    where fn:not(dls:document-is-managed($testDocUri))
    return xdmp:document-delete($testDocUri)
let $deleteManagedDocs := for $testDoc in cts:search(/*[forTestOnly], dls:documents-query())
    let $testDocUri := fn:base-uri($testDoc)
    return dls:document-delete($testDocUri, fn:false(), fn:false())
return ($deleteManagedDocs, $deleteNonManagedDocs)

虽然它似乎在大多数情况下都能正常工作,但它最近已开始零星地失控。在测试执行期间的某个时刻,它开始运行几乎无限的时间(我通常在600-700秒后停止),但大部分时间不到一秒钟。用于测试的数据库并不大(它有一些基本的种子文档,但没有与生产数据库相比),通常每个测试只会创建一些带有'forTestOnly'的文档(如果不是更少)。

查询看起来很简单,虽然相对快速地连续运行200次可以理解地对数据库造成压力但我无法想象它会导致这种滞后(测试是Grails集成测试和整个执行需要两分多钟)。任何想法为什么长期运行?

作为旁注,我已经证实,当测试停止时,确实是在XQuery开始运行之后,而不是在某种测试接线/执行之前。

非常感谢任何帮助。

1 个答案:

答案 0 :(得分:1)

查询可能看起来很简单,但评估起来并不简单。那些dls函数调用可能正在做任何事情,因此估计复杂性很棘手。使用DLS还意味着我们不知道删除每个文档需要删除多少版本历史。

一种可能性是您发现了一个错误。它可能已经修复,这是您应该始终报告您正在使用的软件的完整版本的一个很好的理由。答案可能就像升级以获取修复一样简单。

另一种可能性是,您的测试套件最终会在一次高级评估中运行所有这些工作,所以一切都在内存中直到最后。这可能会使用足够的内存来驱动服务器进行交换。这可以解释最近的失控"螺旋失控"行为。检查操作系统并查看其内容。

接下来,设置组file-log-level=Debug并在发生其中一个慢速事件时检查ErrorLog.txt。如果您看到XDMP-DEADLOCK消息,则可能会出现同时运行此删除查询的两个或更多副本的问题。 MarkLogic具有自动死锁检测和解决方案,但首先要避免死锁的速度要快。

某些日志记录也可能有助于确定花费的时间。类似的东西:

let $deleteNonManagedDocs := for $testDoc in /*[forTestOnly]
    let $testDocUri := fn:base-uri($testDoc)
    where fn:not(dls:document-is-managed($testDocUri))
    return (
      xdmp:log(text { 'unmanaged', $testDocUri }),
      xdmp:document-delete($testDocUri))
let $deleteManagedDocs := for $testDoc in cts:search(/*[forTestOnly], dls:documents-query())
    let $testDocUri := fn:base-uri($testDoc)
    let $_ := xdmp:log(text { 'managed', $testDocUri })
    return dls:document-delete($testDocUri, fn:false(), fn:false())
return ()

最后,您还应该能够简化查询。由于您要删除所有内容,因此您可以忽略DLS。

xdmp:document-delete(
  cts:uris(
    (), (),
    cts:element-query(xs:QName('forTestOnly'), cts:and-query(())))

如果您在每个测试文档上设置一个集合,这将更简单,更有效:xdmp:collection-delete('test-docs')