问题描述
nebula-java 3.8 的 ScanEdgeResultIterator.next() 在正常处理响应时,会对同一个 GraphStorageConnection 执行两次 release,导致 next() 一直阻塞、无法返回。
该问题只存在于边扫描方法 ScanEdgeResultIterator 中。点扫描方法 ScanVertexResultIterator 已正确删除重复的 release,没有这个问题。
问题原因
https://github.com/vesoft-inc/nebula-java/pull/605/files#diff-8d4ffb6a9c44959ad0d34a65442ed50ea0d58bce4343ce7b04f815fddfc0e336
PR #605 为连接释放增加了 finally 逻辑:
} finally {
pool.release(leader, connection);
}
但是,ScanEdgeResultIterator 在 worker 末尾仍然保留了原来的连接释放代码:
pool.release(new HostAddress(addr.getHost(), addr.getPort()), connection);
countDownLatch.countDown();
因此,同一个 Storage 连接会被归还连接池两次。
StorageConnPool.release() 最终调用:
GenericKeyedObjectPool.returnObject()
第二次 release 位于 countDownLatch.countDown() 之前。当第二次释放连接无法正常完成时,当前 worker 不会执行 countDown(),外层线程会一直阻塞在:
在 PR #605 中,ScanVertexResultIterator 原有的第二次 release 已经被删除,因此点扫描不存在这个问题;只有 ScanEdgeResultIterator 遗漏了相同的修改。
影响版本
release-3.8
- 提交 Issue 时,
master 分支中也存在相同的重复释放代码。
涉及文件:
client/src/main/java/com/vesoft/nebula/client/storage/scan/ScanEdgeResultIterator.java
复现场景
- 集群中存在三个 Storage 地址。
- 指定扫描单个 partition,此时只有一个
PartScanInfo。
- 调用
ScanEdgeResultIterator.next()。
- 两个没有匹配 partition 的 worker 执行
countDown() 后返回。
- 扫描 leader partition 的 worker 在
finally 中第一次释放连接。
- 随后,该 worker 在执行
countDown() 前再次调用 pool.release(...)。
- 最终
CountDownLatch 的计数停留在 1,next() 永远无法返回。
通过 Arthas 可以看到调用线程一直阻塞在:
ScanEdgeResultIterator.next()
-> CountDownLatch.await()
预期行为
每个从连接池借出的 Storage 连接只应释放一次,ScanEdgeResultIterator.next() 应当正常返回。
实际行为
同一个连接被释放两次,导致 worker 没有执行 countDown(),最终使 next() 一直阻塞。
已验证的解决方法
删除 worker 末尾重复的 release 后,问题消失:
- pool.release(new HostAddress(addr.getHost(), addr.getPort()), connection);
countDownLatch.countDown();
该连接已经在现有的 finally 代码块中释放,无需再次释放。
问题描述
nebula-java 3.8 的
ScanEdgeResultIterator.next()在正常处理响应时,会对同一个GraphStorageConnection执行两次release,导致next()一直阻塞、无法返回。该问题只存在于边扫描方法
ScanEdgeResultIterator中。点扫描方法ScanVertexResultIterator已正确删除重复的release,没有这个问题。问题原因
https://github.com/vesoft-inc/nebula-java/pull/605/files#diff-8d4ffb6a9c44959ad0d34a65442ed50ea0d58bce4343ce7b04f815fddfc0e336
PR #605 为连接释放增加了
finally逻辑:但是,
ScanEdgeResultIterator在 worker 末尾仍然保留了原来的连接释放代码:因此,同一个 Storage 连接会被归还连接池两次。
StorageConnPool.release()最终调用:第二次
release位于countDownLatch.countDown()之前。当第二次释放连接无法正常完成时,当前 worker 不会执行countDown(),外层线程会一直阻塞在:在 PR #605 中,
ScanVertexResultIterator原有的第二次release已经被删除,因此点扫描不存在这个问题;只有ScanEdgeResultIterator遗漏了相同的修改。影响版本
release-3.8master分支中也存在相同的重复释放代码。涉及文件:
复现场景
PartScanInfo。ScanEdgeResultIterator.next()。countDown()后返回。finally中第一次释放连接。countDown()前再次调用pool.release(...)。CountDownLatch的计数停留在 1,next()永远无法返回。通过 Arthas 可以看到调用线程一直阻塞在:
预期行为
每个从连接池借出的 Storage 连接只应释放一次,
ScanEdgeResultIterator.next()应当正常返回。实际行为
同一个连接被释放两次,导致 worker 没有执行
countDown(),最终使next()一直阻塞。已验证的解决方法
删除 worker 末尾重复的
release后,问题消失:- pool.release(new HostAddress(addr.getHost(), addr.getPort()), connection); countDownLatch.countDown();该连接已经在现有的
finally代码块中释放,无需再次释放。