Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

黑科技 udf 下仔细剖析:

再次复测一轮后,依然复现了此 bug,此刻心中是绝望的,然而多多少少也唤醒了我们心中的一丝好强之心。为此,我们仔细的过了一遍查询的代码,然而我们惊奇的发现,”单机不会出现并发问题而分布式会在此处出现并发问题“的结论并不正确,这里在分布式中既然是串行的,由此可见。大家的印象依然是不太靠谱的,即使是自己实现的代码,过了很久也可能会忘掉。既然如此,那上个修复显然是没有意义的。bug,此刻心中是绝望的,然而多多少少也唤醒了我们心中的一丝好强之心。为此,我们仔细的过了一遍查询的代码,然而我们惊奇的发现,”单机不会出现并发问题而分布式会在此处出现并发问题“的结论并不正确,这里在分布式中依然是串行的,由此可见。大家的印象依然是不太靠谱的,即使是自己实现的代码,过了很久也可能会忘掉。既然如此,那上个修复显然是没有意义的。


此刻能做什么的?只能耐心的回到本质来分析。我们首先利用黑科技 udf 来打出了 FileReaderManager 的 closedReferenceMap。可以看到,这些文件刚好是没有继续合并的文件,其都存在读引用尚未释放。在分布式查询的实现中,每个查询在协调者节点生成一个 queryId,这些 queryId 在其他节点查询时为了避免冲突又会生成一个数据节点本地的 queryId 并保存到 context 中。所以 udf 打出来的这些 queryId 都是数据节点本地的 queryId,跟前面的 regist queryid 里面的 id 并不相等,虽然他们是一一对应的。但至少,在再次复测前我们无法找到这个对应关系。

...

在协调者节点来看,当我创建 remoteReader 的时候,如果返回 的时候,如果不返回 -1 我才会将这个节点注册到 context 的 remoteNodes 里面,之后 endquery 的时候便会向这个节点发送 ednquery 的 rpc。如果是 -1,那么便不会注册该节点,因为本来也没有数据,不需要去 endquery...哎?是这样吗?

...

  • 凭印象和灵机一动来 debug 是不靠谱的。最不会说慌的还是代码,从流程看起,理清整个流程对于 debug 会有很大的帮助。
  • 每个人的关注点不一样,遇到瓶颈时可以向别人讲解自己的 debug 过程,这样一方面有助于理顺自己的 debug 思路(也可能发现自己想错的一些地方),另一方面也便于其他人用另一个视角来帮助 debug。
  • 对于资源泄露类 bug,最靠谱的还是直接 dump 内存或者通过 runtime 的形式把对应的值打出来。如果这次 debug 我们早早的去看 ClusterQueryManager 的成员变量,可能就更早的发现实际由注册的 的成员变量,可能就更早的发现实际有协调者节点注册的 queryId 没释放。 

拓展:

虽然这个 bug 也解了,但是在更进一步了解了查询流程后,也有了一层隐隐的担忧。因为集群任何一个节点都可能发生宕机,所以这种跨节点管理引用的方式需要多次 rpc 才能释放引用,这是非常危险的,一旦有节点宕机,便可能导致其他节点的部分引用没办法释放。

对于这种困境,我勉强想了想几种可能的集中解决方案:对于这种困境,我勉强想了想几种可能的解决方案:

  1. 参照 2pc + 共识组的设计,将锁的管理共识化,这样便可以保证最终最会有人释放锁。当然这在咱们的场景下几乎是完全不可用的,对性能影响太大了。
  2. 为单机的引用计数提供 ttl 的属性,即超过一段时间不再获取该数据便释放引用。这可能又会有 safety 的问题,毕竟由于 flp 定理,分布式锁是无法保证绝对的 safety 的。

...