本文只概述一下此案,不做技术上的深究:
这是一次扑朔迷离的Spark StackOverflow侦破过程,这也是程序员悲情debug的故事。故事起源于一个图算法:KCore算法。该算法的特点决定了需要迭代很多轮才能收敛。在亿级别的数据上,迭代个几百次是家常便饭。当我们翘首期盼迭代收敛结果时,它却引发了一综StackOverflow的奇案。
一开始,该算法在本地小数集上测试通过,没有任何问题,但放到集群上运行后,总是出现StackOverflow的错误。起初,我们认为错误原因不过是典型的RDD的lineage过长,也就是lineage的长度随着算法迭代次数增加而不断变长,最后导致Spark在序列化该lineage的时候调用栈溢出。于是,我们随手拎起checkpoint的宝刀,每迭代几轮就checkpoint一下,以为可以轻而易举地截断lineage,从而解决这个问题。可是当checkpoint加入后,错误仍然出现。迫不得已,我们进行了小米加步枪式的Debug。
在Debug过程中,StackOverflow这种类型的错误,展现了比OutOfMemory更难驯服的个性,尤其是在大规模的分布式集群上。这逼迫我们设法把案发现场转移到单机环境,并通过非常Geek的方式,模拟出算法在分布式环境下的出错情景,重现案发现场,终于追溯到了问题的根源所在:RDD异常的 f 函数闭包和VertexRDD中non-transient成员变量。这两个Bug导致Task的序列化链,可以偷偷穿越被断掉的lineage不断延续,也就是Task的序列化链随着迭代次数的增加而不断增长,最终造成了StackOverflow错误。
整个断案过程耗时一周ÿ