首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >MarkLogic Java死锁检测

MarkLogic Java死锁检测
EN

Stack Overflow用户
提问于 2018-11-24 13:23:00
回答 1查看 424关注 0票数 7

我们的一个应用程序遇到了一些令人讨厌的死锁。我很难重新创建这个问题,因为死锁(或堆栈跟踪)没有立即出现在我的java应用程序日志中。

令我惊讶的是,marklogic重试失败请求(例如,由于死锁)。如果您的请求不是多语句请求,这可能是有意义的,但否则我不确定它是否是。

因此,让我们继续处理这个死锁问题。我创建了一个简单的代码片段,在其中我故意创建了一个死锁。代码片段创建一个文档test.xml,然后尝试从两个不同的事务中读取和写入,每个事务位于一个新线程上。

代码语言:javascript
复制
public static void main(String[] args) throws Exception {
        final Logger root = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
        final Logger ok = (Logger) LoggerFactory.getLogger(OkHttpServices.class);
        root.setLevel(Level.ALL);
        ok.setLevel(Level.ALL);

        final DatabaseClient client = DatabaseClientFactory.newClient("localhost", 8000, new DatabaseClientFactory.DigestAuthContext("username", "password"));

        final StringHandle handle = new StringHandle("<doc><name>Test</name></doc>")
            .withFormat(Format.XML);
        client.newTextDocumentManager().write("test.xml", handle);

        root.info("t1: opening");
        final Transaction t1 = client.openTransaction();
        root.info("t1: reading");
        client.newXMLDocumentManager()
            .read("test.xml", new StringHandle(), t1);

        root.info("t2: opening");
        final Transaction t2 = client.openTransaction();
        root.info("t2: reading");
        client.newXMLDocumentManager()
            .read("test.xml", new StringHandle(), t2);

        new Thread(() -> {
            root.info("t1: writing");
            client.newXMLDocumentManager().write("test.xml", new StringHandle("<doc><t>t1</t></doc>").withFormat(Format.XML), t1);
            t1.commit();
        }).start();

        new Thread(() -> {
            root.info("t2: writing");
            client.newXMLDocumentManager().write("test.xml", new StringHandle("<doc><t>t2</t></doc>").withFormat(Format.XML), t2);
            t2.commit();
        }).start();

        TimeUnit.MINUTES.sleep(5);

        client.release();
    }

此代码将生成以下日志:

代码语言:javascript
复制
14:12:27.437 [main] DEBUG c.m.client.impl.OkHttpServices - Connecting to localhost at 8000 as admin
14:12:27.570 [main] DEBUG c.m.client.impl.OkHttpServices - Sending test.xml document in transaction null
14:12:27.608 [main] INFO  ROOT - t1: opening
14:12:27.609 [main] DEBUG c.m.client.impl.OkHttpServices - Opening transaction
14:12:27.962 [main] INFO  ROOT - t1: reading
14:12:27.963 [main] DEBUG c.m.client.impl.OkHttpServices - Getting test.xml in transaction 5298588351036278526
14:12:28.283 [main] INFO  ROOT - t2: opening
14:12:28.283 [main] DEBUG c.m.client.impl.OkHttpServices - Opening transaction
14:12:28.286 [main] INFO  ROOT - t2: reading
14:12:28.286 [main] DEBUG c.m.client.impl.OkHttpServices - Getting test.xml in transaction 8819382734425123844
14:12:28.289 [Thread-1] INFO  ROOT - t1: writing
14:12:28.289 [Thread-1] DEBUG c.m.client.impl.OkHttpServices - Sending test.xml document in transaction 5298588351036278526
14:12:28.289 [Thread-2] INFO  ROOT - t2: writing
14:12:28.290 [Thread-2] DEBUG c.m.client.impl.OkHttpServices - Sending test.xml document in transaction 8819382734425123844

t1t2都不会被逗号。MarkLogic日志确认实际上存在死锁:

代码语言:javascript
复制
==> /var/opt/MarkLogic/Logs/8000_AccessLog.txt <==
127.0.0.1 - admin [24/Nov/2018:14:12:30 +0000] "PUT /v1/documents?txid=5298588351036278526&category=content&uri=test.xml HTTP/1.1" 503 1034 - "okhttp/3.9.0"

==> /var/opt/MarkLogic/Logs/ErrorLog.txt <==
2018-11-24 14:12:30.719 Info: Deadlock detected locking Documents test.xml

如果其中一个请求失败并引发异常,这将不是一个问题,但事实并非如此。MarkLogic Java在大约120秒后重试每个请求,直到120秒和其中一个更新超时:

代码语言:javascript
复制
Exception in thread "Thread-1" com.marklogic.client.FailedRequestException: Service unavailable and maximum retry period elapsed: 121 seconds after 65 retries
    at com.marklogic.client.impl.OkHttpServices.putPostDocumentImpl(OkHttpServices.java:1422)
    at com.marklogic.client.impl.OkHttpServices.putDocument(OkHttpServices.java:1256)
    at com.marklogic.client.impl.DocumentManagerImpl.write(DocumentManagerImpl.java:920)
    at com.marklogic.client.impl.DocumentManagerImpl.write(DocumentManagerImpl.java:758)
    at com.marklogic.client.impl.DocumentManagerImpl.write(DocumentManagerImpl.java:717)
    at Scratch.lambda$main$0(scratch.java:40)
    at java.lang.Thread.run(Thread.java:748)

克服这个问题的可能方法是什么?一种方法可能是设置事务的最长生存时间(比如5秒),但这感觉很烦琐和不可靠。还有其他想法吗?还有什么其他的设置我应该检查吗?

我正在上MarkLogic 9.0-7.2并使用marklogic-client-api:4.0.3

编辑:解决死锁的一种方法是同步调用函数,这实际上是我在我的情况下解决它的方法(参见注释)。但我认为根本的问题仍然存在。在多语句事务中具有死锁不应在120秒超时中隐藏。我宁愿有一个立即失败的请求,而不是我的一个文档上的120秒锁+64个线程失败的重试。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-11-25 04:17:42

死锁通常可以通过重试来解决。在内部,服务器执行内部重试循环,因为死锁通常是短暂的和偶然的,持续时间很短。在您的例子中,您已经构建了一个永远不会成功的情况,对于两个线程来说,任何超时都是相等的。通过在使用REST时避免多语句事务,可以在应用层避免死锁。(这是Java所使用的)。由于客户端管理事务ID的责任以及服务器无法检测客户端错误或客户端标识,REST上的多语句事务不能100%安全地实现。除非您积极主动地处理wrt错误和多线程,否则非常微妙的问题可能而且确实会发生。如果您将逻辑“推送”到服务器(xquery或javascript),那么服务器能够更好地管理事情。

至于这种情况下Java实现重试的“好”或“不好”,无论是哪种方式,都是值得商榷的。(一个看似容易使用的界面的妥协之处在于,许多原本可以选择的东西都是按照惯例来决定的。一般情况下没有一刀切的答案。在这种情况下,我认为死锁更有可能是由‘意外’独立的代码/逻辑引起的,而不是以切线运行的相同代码--在这种情况下重试将是一个不错的选择。在您的示例中,它不是,但在您将代码更改为“不执行”之前,早期的错误仍然会失败。

如果它还不存在,那么对于可配置超时和重试行为的特性请求确实是一个合理的请求。但是,我建议您尽量避免任何导致开放事务的REST调用--这本身就是一个问题,特别是如果您没有预先注意到问题(那么它更有可能在生产中咬您)。与JDBC不同的是,JDBC使连接保持打开状态,以便服务器能够检测客户端断开连接,HTTP和model不存在--这导致了与java中的传统数据库编码不同的编程模型。

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53458583

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档