CrashTuner:在云系统中通过对元信息的分析探测故障恢复问题

CrashTuner:在云系统中通过对元信息的分析探测故障恢复问题

摘要

崩溃恢复问题在云系统中是大多数服务问题,很容易引起系统故障。它以非常难被发现而著名,因为这些问题只能在特定的条件下才能够被发现。这篇论文展示了CrashTuner,一个新颖的错误注入特使方法来打败故障恢复错误。我们观察到当一台节点正在访问元信息变量,而元信息变量又引用高级别的系统状态信息(一个节点实例或任务),它经常触发故障恢复问题。因此,通过基于日志对静态程序的分析来自动推断元信息变量来确认崩溃的点。我们的方式是自动化和不需要手动操作规范。

我们应用CrashTuner到五个各自独立的分布式系统:Hadoop2/Yarn,HBase,HDFS,Zookeeper 和Cassandra。CrashTuner能够在17.39个小时完成对每个系统的测试,报告21个之前从来没有发现的新bugs。所有新的bugs被原始的开发人员确认并修复了其中的16个(其中14个是我们修复的)。这些新的bugs能够引起严重的破坏,比如机器宕机或启动失败。

CCS 概念 软件和它的工程 软件测试和排错;云计算;

关键词 故障恢复问题;容错;分布式系统;漏洞检测;错误注入;云计算

1 介绍

在云计算时代,分布式系统已经变成了其支柱。越来越多的应用建立在大规模分布式系统上(比如可扩展的分布式系统计算框架和分布式存储系统框架),对用户提供在线服务。这些系统的高可用是关键的:潜在的分布式系统的失败能够导致云中断,很容易消耗服务提供者上百万美元。

分布式系统的高可用很大程度上取决于这些系统能够容忍节点宕机的程度。大规模分布式系统经常由成千上万个节点组成,节点的硬件故障和软件故障时很常见的。尽管许多先进的故障恢复机制被应用到分布式系统中,能够正确的处理节点宕机仍然具有挑战。它是非常困难的,如果可能的话,对于开发人员来说期望所有的宕机场景和正确的实现相应的恢复机制。这篇论文,我们指的是在故障恢复相关的机制作为故障恢复错误。

在分布式系统中,故障恢复问题在最严重的问题中。许多节点宕机可以通过在分布式系统中实现的先进的故障容错方案来恢复过来。然而,故障恢复问题打破了这样的容错方案,因此很容易导致系统故障。并且,它以很难探测故障恢复错误在内控检测而著名。节点宕机时间需要在特定的条件触发一个问题来注册。结果,故障恢复问题广泛的存在已经发布的分布式系统中。

科技艺术的状态通过错误注入测试探测故障恢复。然而,命中小问题的触发的窗口是很有挑战性的,由于在系统测试大量的状态空间。任意的错误注入是无效的,根据我们的经验和之前的工作。系统的方法经历了状态爆炸的问题。研究人员应用先进的有启发式的,或对手动规范进行重排序来有效限制大的搜索空间。尽管取得了一些成就,这些方法仍旧挣扎在探索分布式系统大量的状态空间。大多数的注入失败不是必须的,非常少的故障恢复问题被报告出来了。

这篇论文展示了CrashTuner,一个新奇的方法来准确发现使节点触发宕机的问题的点。此后,我们用这样的程序的点作为崩溃的点。CrashTuner自动通过推断元信息变量来准确的定位崩溃的点,谁的访问注入点很可能暴漏错误。这是通过基于日志分析静态程序的方法实现的。我们已经应用CrashTuner来测试5个独立的分布式系统;扩展的计算框架Hadoop2/Yarn,分布式key-value存储的HBase,可扩展的文件系统HDFS,集群同步服务Zookeeper,和去中心化的存储系统Cassandra。CrashTuner能够再现66个中的59个存在的崩溃恢复问题,报告21个新的从未发现的崩溃恢复问题。这些问题能够导致严重的破坏,像集群宕机和启动失败。到目前为止,16个新报告出来的问题已经被修复(其中14个事我们修复的)。

观察 分布式系统由节点集群组成。作业和大块的资源被划分成许多小块,然后分配到不同独立的节点。节点的集合和它们所关联的任务和资源一起形成了高层的系统状态视图。图一展示了一个分布式计算框架Hadoop2/Yarn的简单高层视角(通过我们的分析自动构建出来).系统包括一个集群独立的系欸但。一个节点管理一个或多个容器。一共由m个容器(MasterContainer,Container_1,….Container_m)。一个JVM进程创建在每个容器种,每个JVM进程都尝试执行给定的任务。一个用户作业请求被一个应用实例处理。每个作业都被授权给一个主容器。主节点将一个作业分解成m个小的任务并分配每个任务到可用的容器中。

在实践中,上面的高层系统状态信息被存储在不同节点堆内存中,可以通过堆引用来进行访问。例如,在Hadoop2/Yarn中,实例字段NMContext.nodeId引用特定的节点。为了方便起见,我们认为这些变量作为元信息指向高层状态信息。对于更新相应的关键元信息变量是很关键的。否则,一个故障恢复问题可能被触发。

我们从5个各自的分布式系统中的4个检测了66个故障恢复问题。我们的研究导致下列观察:

       故障点是访问程序元信息变量。当一个节点再某个崩溃点崩溃,崩溃恢复问题就被触发了。

66个中的14个问题不是时间敏感的,它们在任何的错误注入技术中被频繁的触发。对于接下来的52个故障恢复问题,他们的故障点能够被观测到。这是两个常见的场景:

       预读场景:节点N在节点M读取其元信息前崩溃了,节点M没有发觉它的故障仍继续使用节点N的旧的信息,导致任务失败。

       后写场景:节点N在更新完系统状态后崩溃了。在恢复的过程中,节点N更新需要抛弃和回滚。恢复的过程可能错误的处理崩溃的状态,导致错误的恢复尝试。

CrashTuner的方式:定位崩溃点是关键。我们如何发现崩溃点呢?在CrashTuner中,我们应用日志分析和基于类型的静态程序分析来自动推断元信息变量。在读取元信息变量前或在写入元信息后崩溃点是这些程序点。

一个问题立即被提出来了:这些变量都是元信息变量么?首先,节点引用的变量是元信息变量。我们可以很容易的从运行日志中确认节点引用变量。分布式系统提供了一个丰富的运行日志集来诊断和在线监控。这些日志记录消息和事件,其中包含了诸如节点ID和任务ID的信息。例如,在Hadoop2/Yarn的日志实例的“NodeManager node1 registered as node1:42449”表明节点node1:42439加入了集群。元信息变量如下定义:

节点直接引用或间接引用变量是元信息变量。如果他们出现在相同运行日志中,它们是有关系的。对象字段拥有相同元信息值的变量是元信息变量。

元信息变量准确的影响这高层次的系统状态信息,例如集群的节点和资源/任务与每个节点的联系。我们应用日志分析来发现在运行期间的元信息集合。我们静态分析检查存在的元信息变量的类型,得到其他元信息变量和相等的类型。CrashTuner会应用错误注入来检测每一个崩溃点。在第三部分将会有更详细的方法。

所做贡献:我们做了如下努力。

我们提出一个新奇的方法来检测故障恢复问题。我们的方法不同与现存的其他的错误注入检测技术通过元信息分析定位错误注入点。元信息分析自动推断元信息变量,谁的访问点是错误注入点是可能暴漏问题的。这个方法是全自动的不需要人工干预。

我们开发了CrashTuner,一个简单但是有效的工具来检测崩溃恢复问题。在CrashTuner中,元信息分析通过基于日志的静态程序分析来实现。在一个分开的测试阶段,CrashTuner执行最少的指令对于错误注入测试。这个分析是非入侵式的,适合在线监控。它能非常容易的采用并很有效的探测故障恢复问题。

我们广泛的使用各自分布式系统Hadoop2/Yarn,HBase,HDFS,Zookeeper和Cassandra来评估CrashTuner。CrashTuner能够在17.39个小时完成测试,并报告了21个新的从未发现的崩溃恢复问题,其中包括8个重要的问题。我们提供了补丁对于21个问题中的20个,其中14个已经被接受了。

接下来的论文以一下的方式来被组织:第二部分推动了我们的方法用一个以实验的研究66个故障恢复问题。我们在第三部分提供了CrashTuner的设计和实现。在第四部分优化了它的效率。第五部分检查了相关工作并在第六部分总结了论文。

2 动机

为了更好的理解故障恢复问题,我们从存在的问题研究数据库彻底的检查了故障恢复问题。我们关注我们的研究在下面四个分布式系统中:Hadoop2/Yarn,HDFS,HBase和Zookeeper。两个数据库提供了一共116个崩溃恢复在上述的四个系统中。在这个工作中,我们注意到触发问题出现的事件。因此,忽略了50个故障,因为他们设计多个故障事件的依赖,或要求IO操作。之前的检测技术对于故障恢复涉及到多个事件能够增加我们的方法来解决这些问题。对于剩下的66个问题,我们进行了一个深入的研究。

66个中的14个不是时间敏感的。他们是由于实现或逻辑错误在恢复的过程中。能够在任何时候被触发当一个节点宕机。比如在MR-3463,住系欸但使用host:port的格式来表示一个节点的主机名称。如果主节点宕机,恢复过程会得到一个错误的格式”host”,使得一直的错误。在ZK-131,在恢复的过程中存在数据竞争。当一个节点宕机,恢复的线程发送两个时间消息:一个重置崩溃状态。另一个读取重置的状态。如果读取的状态在重置事件之前被读取了,会触发一个问题。因此,在我们的研究中,我们仅仅讨论剩下的52个事件敏感的故障恢复问题。

表一根据问题的崩溃点来得出特征。所有的52个问题被触发在程序的访问元信息的点上。第二列总结了元信息被访问对于每个问题。在实践中,一个典型的元信息,在HBase中,HRegionServer在系统中表示一个节点,同样的可以通过HServerInfo和HServerAddress类型的变量被引用。这些变量可以被字节数组,字符串和整形的变量所覆盖,所有引用相同类型的元信息,如HRegionServer。第三部分详细介绍了如何指向元信息。

崩溃点被进一步分为两个场景1)预读场景,如在读取元信息变量之前。2),后写场景,如在写入一个元信息变量之后。

2.1 预读场景

有37个问题属于这个场景:节点M在不知道节点N的可用性是尝试读取元信息,导致作业失败。图2描述了在Hadoop2/Yarn一个典型的故障恢复错误。当一个作业线程尝试从共享数据节点读取崩溃节点NM@node1的资源时,问题被触发了。因为图二YARN-5918:一个在Hadoop1/Yarn真实世界的故障恢复问题。两个节点设计到这次问题中,RM节点node0和NM节点node1,node1发送心跳信息到node0中,当它还是活着的时候。Node1崩毁后,心跳信息不会被发送。2)node0中的生命监视器线程检测到node1 的崩溃在一个时间周期内。一个LOST事件被发送到恢复线程。3)恢复线程从nodes中移除node1,一个共享数据结构记录所有的可用节点。4)另一个运行的作业线程尝试获取NM@node1的资源。

Node1从nodes中移除,一个null值被返回了一个空指针异常在第三行被提出来了。

如何检测这些问题呢?崩溃点时程序点在读取元信息之前。节点相应的元信息变量被从满足崩毁的节点来触发这个问题。如何发现哪一个节点崩溃?我们开发了一个对于特定节点关于运行时元信息变量在线日志分析。因此,在读取元信息变量之前,我们能够查询它的运行时的值来发现相应的节点。

节点崩溃事件能够在一段事件内被发现。在图2中触发了问题,作业线程等待liveMonitor来检测节点崩溃事件然后发送LOST信息。为了加速测试过程,我们可以设计到期事件间隔为一个小的值来快速的发现这个问题。另外,在我们的实现中,我们没有使系统等待,使用由系统提供的关闭的脚本来让node1离开生产环境的工作。

2.2 后读场景

15个属于这个场景的问题:Node N在更新系统状态时崩溃了,从崩溃的状态恢复失败。图3给了这个场景的问题。如果node1在做一个提交后崩溃,没有恢复需要。如果崩溃发生在提交的之前,恢复过程会复制另一个尝试,这个可以正确的重做这个任务。然而,如果节点3在一个在两个RPC的小事件窗口崩溃,提交的状态是被污染的。恢复的过程一直失败,作业永远不会被完成。

如何发现这样的问题呢?在写完元信息后崩溃是程序的点。为了触发这样的问题,我们需要崩溃存储相应节点的元信息变量,如在图3的node1。相似的,没有在线日志分析,我们查询存储元信息变量来定位相应的节点。在图3中,通过查询运行时存储的在第二行元信息变量,我们得到了目标节点node1.

3 CrashTuner方法

在一个壳中,CrashTuner发现所有的崩溃点,然后执行错误注入来测试每个独自的崩溃点。图4概览了我们的方法。它由两个阶段组成。第一个节点是通过日志分析来定位定位崩溃点。第二个阶段是运用每一个点。一个轻量级的在线日志分析被采用到相关的运行元信息对于一个特定的节点。因此,在一个崩溃点,我们可以通过查询被访问的运行时变量在正确的点进行崩溃。

3.1 确认崩溃点

我们运用一系列的分析来确认系统的动态崩溃点,定义如下:

定义1。一个动态崩溃点表示一个2元组<P,Context>,P是程序的点,Context是调用栈。

我们使用运行时调用栈来区分在执行相同程序点的上下文。因此,当一个同样的点用不同的调用掉被执行时,他们被认为是不同的崩溃点。

确认动态崩溃点的过程在图4中被描述出来,总结如下:

日志分析运行日志来发现元信息变量,比如节点引用变量和他们相关的变量。这些元信息变量在日志中被打印出来以确认。

静态崩溃点分析发现所有其他的元信息变量通过检查存在元信息变量在系统。存在相同类型的字段被认为是原来的变量。以类型为基础的方法是我们得出系统中的变量,没有准确的跟踪成功程序依赖。在读取元信息变量之前的崩溃点称为静态崩溃点。

在运行中,分析工具执行给定的工作量来记录动态崩溃点。这些没有执行的静态崩溃点被抛弃了。

最后,我们获得了一些动态崩溃点。错误注入测试会运用每一个独立的崩溃点来触发错误。下一步,我们会在图3中详细解释分析的细节。

3.1.1日志分析

日志分析是从发现元信息变量和他们的运行值记录中进行挖矿。

我们在测试中检查系统所有的日志陈述。所有的四个分布式系统中,我们研究使用常用日志库如Log4j和SLF4J。这些库提供了通用的拥有如下级别的日志接口:fatal,error,warn,info,debughe trace.因此,我们通过简单的匹配涉及的方法来发现日志。图5(a)给了一系列简单的日志对于我们的例子。他们相应的日志模式从图5(b)中抽取。运行时的日志变量用正则表达式表示。

图5(c)展示了一个简化的我们的运行日志实例。每个日志实例被各自执行,使用一个特别的日志模式来进行匹配。我们采用这个方法来有效的匹配运行日志。运行时的变量可以被以红色的高亮显示。

为了推断出元信息变量,我们检查运行时的日志变量。这些运行的变量包括主机名或IP地址被认为节点推断变量,node3:4239和node4:4249在图5中。我们依赖其他的运行值来检查他们是否出现在同一个运行时间,如container_...3。图5(d)展示了从日志分析得出的元信息,相关的运行值连接在一起。图准确的反映了在图1中的高层次的系统状态。这些变量拥有的元信息值时运行时的元信息变量。

3.1.2 静态崩溃点分析

元信息的值存储在节点的堆内存中,并通过字段对象引用。为了准确的区分对象字段拥有的元信息值,我们需要计算和跟踪在使用指针分析的变量精确依赖信息。给定分布式系统和分布式系统精确指针分析这是一个令人害怕的任务,仍然是一个开放搜寻话题。因此,我们开发了一个以类型为基础的分析来推断元信息来替代。

定义2 T是一个元信息类型,如果存在一个元信息变量是类型T。子类型和类型T的集合都是元信息类型。类C是一个元信息类型,如果它包含一个字段实例C。元信息类型T的f,C.f设置在c的构造函数中。

定义2定义了一个我们的以类型为基础的分析。直观的,元信息是类型的,变量等于元信息类型T引用同样的元信息类型。我们也认为T包含类C如果存在字段C。类型T的f,仅仅被设置在C的构造方法中。这是为了处理通用的情况当一个C的实例被它的字段C.f唯一索引,类如,RMContainerImpl的对象是被唯一索引的,拥有一个ContainerId,在实践中,两个类交替引用相同的元信息。

为了避免以用许多无关的变量,我们不适用上述通用的规则对于下面的基本类型:Integer,String,Enum,byte[],和File。相反,我们通过日志分析和关于他们的拥有类作为元信息类型来确认云信息。

表2展示了我们例子的元信息类型。没有给所有的元信息类型。带有*的类型声明是从日志分析获取的类型。所有其他类型可以通过静态分析推断出来。推断相同元信息的类型被分到一起。

静态崩溃点是那些元信息类型的访问点。putField和getField的指令对于非集合的字段以直接的方式来区分。集合字段的类型通多通用的API来进行读写。因此,对于集合累心给,我们在表3中检查车技API名字的方法,来寻找与之匹配的读写操作。

优化       如果它仅仅通过c的构造方法来设置它包含的值c,我们抛弃了引用字段C.f。通过定义,类型C也是元信息类型和引用类型C的对象已经被当作崩溃点。因此,它变成了冗余的了来执行错误注入来测试后来引用的C.f。读取没有使用的引用被忽略了。另外,我们过滤掉了谁的值在使用之前有清理检查的读引用。在实现的过程中,检查建议容错方案。

所有以上的优化不保证可靠性,意味着我们可能错过真正的崩溃点。然而,我们随机的的选择3000个优化的崩溃点来为错误注入检测,没有新的问题被发现。

最后,这些在读取元信息字段的程序点是静态崩溃点。为了方便,此后各自称他们为预读和后写点。如果一个读引用仅仅用在了一个方法的返回值,我们提升一个方法的调用点为相应的静态崩溃点。这样的提升简化了相应动态预读点的栈。

3.1.3 分析工具

分析工具生成动态的崩溃点,比如执行不同调用栈的静态崩溃点。哪些没有被执行的静态崩溃点被丢弃。

我们简述在不同工作量的测试系统直到一个恢复的点。从默认的大小开始,我们报纸成倍增加任务量直到没有新的动态崩溃点生成。这个过程很快的经历2到3个迭代转变。我们促成系统在每一个它的崩溃点。在简述的过程中,生成的代码会记录每个集中每个崩溃点和他们的相应调用栈。Java的Thread.currentThrea().getStackTrace()被调用来得到运行时的调用栈。调用栈被使用调用字符串和绑定深度为5的栈。

3.2错误注入测试

我们在每一个动态崩溃点执行了错误注入测试。正如在第二部分讨论的,为了注入正确的错误时间,我们需要明白当击中哪一个动态崩溃点节点崩溃了。如何找到正确的点来崩溃呢?我们应用了一个轻量级的在线日志分析来记录元信息变量和他们所依赖的特定节点。记录的元信息如图5一样简化实现。触发拥有元信息变量能够有效的查询记录元信息来定位目标节点。

3.2.1 在线日志分析

我们使用Logstash代理在集群中的每个节点来收集运行日志。Logstash是一个能够察觉日志文件变化的流行的日志收集工具,然后及时的发送变化到自定义的目标节点中。为了避免发送不必要的数据,只有运行时的元信息的值才会被发送。

自定义的存储以FIFO的顺序处理收到的日志。为了效率,而不是构建如图5所示的图,我们记录运行时的节点的值到HashSet中,通过HashMap关联特定节点的元信息。图6记录了我们例子的元信息。收到的值node3:42439 和 node4:42439被插入了节点集中自从他们匹配主机名称。日志实例3发送两个相关的元信息值:container_..._3和node3:42439.因此HashMap更新了键值对<container_..._3,node3:42439>.当执行两个在日志实例4attemp_..._4和container_..._3的值时.我们查询每一个在HashMap的值来获取相关的节点。,然后更新相应的记录。我们抛弃没有关联任何节点的值。

3.2.2 触发器

触发器引导系统来在一个如图7所示的动态崩溃点注入崩溃事件。预读点和后写点被不同的感知。解释如下,图7展示了两种类型的一起感知。在我们的实现中,我们仅仅在同一时间感知一个动态崩溃点。

对于预读,我们通过关闭RPC然后等待一段时间。等待作为一个超时的事件被处理。对于后写点,我们感知一个崩溃的RPC。两个RPC通过参数被调用。控制中心是一个分开的节点来处理感知RPC。如果动态崩溃点没有被运用,我们查询输入运行元信息的值来获取相关连的节点。如果没有这样的节点存在,就简单的返回。另外,我们可以随机的选择一个节点来崩溃。然而,这个替代的方法没有影响我们实验的结果。在第五行,我们触发了一个脚本库来崩溃或关闭一个节点。

最后,我们报告了接下来的三个bug中的一个。第一个:作业失败。第二个:系统悬挂。第三个:在日志中存在异常。当前,我们没有报告小没有引起未预期外的错误。如何开发测试oracles以一个安静的错误是一个重要的值得分开调查的话题。

3.3 实现细节

我们实现了我们的静态分析在WALA和用Javasist执行了指令。流行的日志收集框架Logstash对于日志收集被用在运行日志中。实现由9933行java代码和550行Shell代码组成

日志分析: 我们采用[58]的方法用日志模式来高效匹配运行日志匹配。一个反转的下标建立作为每个日志模式的hash,能够很快的进行快速计算匹配分数未每个运行的日志实例。更高的匹配分数,更可能的匹配日志实例。对于给定的日志实例,我们选择10个日志模式使用最高的分数。然后我们根据10个日志模式来解析日志实例,来发现准确的匹配。

代码植入 我们展现崩溃点作为程序点在wala前后指令。然而,javasist在源代码级别执行指令。一个语句可以传播到多个源代码行,一个WALA指令可能指向在中间的语句的源代码行。那样的情况下,植入的类不会被编译。因此对于预读点,我们尝试植入源代码行和它的执行源代码行直到植入的类被编译。

在线日志分析 我们权衡从离线日志分析结果和实现了过滤的抽取运行值的元信息变量从日志实例。

过滤器是由通过离线分析元信息变量的toString方法得出的正则表达式组成。对于我们的解释的例子,对于类型节点变量的过滤器。因此,当在图5(c)执行日志实例1和日志实例2时,匹配过滤器的值被抽取并发送到自定义的日志收集中。

运行时元信息的值 对于非集合字段的字段,元信息的值通过调用他们相应的toString方法来获得。对于集合类型的字段,得到元信息的值的方法不一样,具体根据给定的读写操作。例如,给的那个m.add(e),e.toString()被认为存储在元信息中。对于如v = m.get((key)的读操作我们得到两个相关的值v.toString()和key.toString().

3.4 一些限制

CrashTuner不是可靠的,不能保证崩溃恢复问题的消失。在测试下,CrashTuner的效率依赖系统的日志质量。例如,CrashTuner不能重现HBASE-13546,HBASE-14621和YARN-4502的三个问题。因此CrashTuner不能通过特们的元信息变量来发现他们。尽管如此,CrashTuner仍能够重现66个存在的问题中的59个,并报告21个新崩溃问题的bugs。

以日志为基础的实现使得CrashTuner能够使得发布到云系统变得很容易。然而,它也影响系统的效能因为有限的日志信息。例如,CrashTuner没有检测任何新的问题在zookeeper中,仅仅290个运行日志实例被生成,节点仅仅被以整数来表述。由另外可替代的方法来分析原信息,如静态分析和基于侵入式的方法。这些方法不依赖日志。然而,静态分析方法经历了高错误和基于设备的方法对于发布是困难的。我们可能需要挑选正确的权衡准确率,效率和通用性。

这篇论文不以涉及多个崩溃事件和IO操作为目标。然而,CrashTuner能够以存在的技术客服这些问题被扩展。比如,目标崩溃一致性问题和涉及多个崩溃事件的目标问题。这些工作会被我们未来的工作所覆盖。

4 评估

我们使用在表4中的5个广泛使用的开源分布式系统:Hadoop2/Yarn,HDFS,HBase,Zookeeper和Cassandra来评估CrashTuner,注意Cassandra在我们的研究中没有被包含。所有的系统都在他们最新的版本中被测试。我们使用默认的配置对于所有的系统,除了hadoop2/Yarn.为了在线hadop2/Yarn中的问题,配置需要被设置成“enable opportunistic”.我们来使用常用的工作量来评估系统。WordCount,TestDFSIO,PE,和压力测试。另外,我们使用”curl“增加工作量来测试我们的查询通过web接口。

所有的实验被执行在一个拥有三个独立节点集群。每个节点由CentOS6.5系统在Intel处理器和32GB内存。评估会回答下面的几个研究问题。

RQ1,在检测问题的时候CrashTuner的效率如何,特别是对于新的问题?

RQ2,和其他的错误注入测试方法,CrashTuner如何?

RQ3,CrashTuner的效率?

4.1 RQ1:效率

我们评估了CrashTuner在生产环境中存在的问题效率是如何的,和它在探测新问题的能力。

4.1.1 重现存在的问题

我们尝试重新在表1中的所有问题。对于每个问题,我们第一检查是否CrashTuner能够正确的定位它的相应的崩溃点?如果成功,我们注入一个崩溃事件在崩溃点来出触发问题。

CrashTuner能够成功的触发52个问题中的45个问题。有7个问题不能够被重现。对于HBASE-13546,HBASE-14621和YARN-4502访问的变量是节点字段的子域,不能直接打印日志。因此CrashTuner不能直接定位崩溃点。为了暴漏这些问题,可能需要手动声明来确认这些元信息中的变量。对于其他的三个问题,HABSE-7111,HBASE-5722和HBASE-5635,CrashTuner未能关联元信息和对应的节点,在低层系统zookeeper中。在HDFS-4596,当访问一个MD5文件,它的名字没有关联任何的节点实例。为了触发这4个问题,我们需要引入另外的日志来关联元信息变量对于正确的目标节点。

4.1.2 探测新的问题

CrashTuner探测了21个新从未被报告过的的问题。包括8个关键的问题。所有的问题被原始的开发者确认,它们中的16个已经被确认修复。

当提交一个问题,我们常常要求提供一个单元测试来暴漏它们。因为我们不允许修改源代码来注入发生在崩溃点的错误,所以它是机智的。在书写单元测试时仅仅支持错误注入在调用一个公共的方法。经常的,一个问题不能够被暴漏,如果它的崩溃点在一个方法中间。

例如,在YARN-9238,变量appAttemptId指向一个尝试实例来执行给定的应用。如果当前的尝试失败,恢复过程会尝试另一个尝试。CurrentAttemp变量指向尝试实例对于每个应用。因此,如果当前尝试的节点崩溃,字段currentAtemp被重置一个新的尝试节点通过恢复线程。在图8中,第四行得到currentAttrmp的值,它是一个新的没有初始化状态的尝试节点。然而,在第八行中,代码未意识到崩溃和使用它作为 一个旧的尝试节点,导致中断。

CrashTuner成功的暴漏这个问题通过崩溃节点appAttempId所关联的,在读取第四行的currentAttempt变量。崩溃后,字段currentAttemp被重置,一个新的尝试节点被返回了。暴露错误。然而,通过默认方法创建的单元测试不能暴露问题。如果崩溃事件在分配方法的入口被处理了,第二行的清洁检查会检测节点崩溃和避免错误。因此,在我们的单元测试中,我们手动重置currentAttemp字段在调用分配方法之前,为了模拟在系统状态在崩溃点。

第5-6行展示了我们的补丁,在使用它之前校验了currentAttempt。通常来说,通过检查崩溃点和日志找到问题的发生的原因并找到相应的解决方案是很容易的。我们总共提供了20个补丁,接受了15个,还有5个补丁在检查之中,一个补丁被拒绝。在这14个被接收的补丁中,8个补丁介绍了情节检查和另外7个对于检查异常和事件。HBASE-22017是一个因为在HReginerServer崩溃恢复过程中的数据竞争问题。原始的开发人员拒绝的补丁是我们解决的数据竞争问题再服务器处理过程。相反,它们创建了一个新的问题,并在客户端解决了这个问题。我们没有提供HBASE-22041的补丁包将在下面讨论。

HBASE-22041 这个bug在RegionServer向HMaster报告并在向Zookeeper注册前被触发了。如果RS之后崩溃了,zk会检测到崩溃然后启动一个恢复进程。在线服务器列表会正确的更新。然而,如果崩溃发生在两个消息之间,启动线程未能从RS中读取数据,就会永远保持重试,引起系统挂起。

当尝试解决这个问题时,我们发现了下面的注释://我们应该重试多少次。原始的开发人员已经意识到这个问题了。但是由于一些原因,它们没有修复它。我们不能知道正确的重试次数的阈值。因此我们没有给这个bug打补丁。

YARN-9164, 当一个作业完成或失败,completeContainer释放每个节点的作业的资源。在第三行,getScheNode方法被调用来获取拥有资源的节点。然而,如果目标节点在第四行之前崩溃,在第五行返回的空值会抛出一个NullPointerException。主节点不能处理这个异常,立即停止,导致整个集群失败。

CrashTuner成功的确认了NodeId作为元信息类型和节点作为元信息变量。通过定义,崩溃点是在第二行访问节点。因为在第二行,读引用直接被返回了。CrashTuner提倡崩溃点是方法getSchedNode的掉用点。总共有43个调用点,相应的有43个潜在静态崩溃点,它们中的30个被优化了,因为返回值不是未使用就是被合理检查了。最后分析器从13个静态崩溃点生成2个动态崩溃点,包括在第四行之前的问题触发。其他的动态崩溃点没有暴漏错误。

4.1.3 超时时间

CrashTuner报告了4个超时问题。这些问题是真的问题还是假问题是具有争论的。尽管任务最终被完成了,它们很大程度上降低了系统。

三个超时问题存在Hadoop2/Yarn中。在第一个问题中,当映射尝试完成任务,记录任务尝试实例它的字段successAttempt。Reduce任务未能读取先从map任务出来的数据,因为崩溃和会花费很长时间进行重试,这就超出了我们默认的超时阈值了。在另外2个问题中,应用尝试被初始化成字段结合容器和它相应的节点。如果节点在设置字段之后崩溃,应用尝试状态会陷入运行运行状态。最终,过了10分钟,卡住的应用会被AbstractLivelinessMonitor杀掉。

有一个相似的超时问题在Hbasezhong ,这个问题会使得Region陷入OPENING状态,在它在10分钟以后被杀掉。

4.1.4 修复的复杂性

表6比较了新发现的存在的问题的补丁。每个补丁的代码行数和每个bug的的补丁数几乎相同,在新发现的问题和存在的问题中建议相似的复杂度。然而,对于新发现的问题,修复一个问题的平均复杂度和每个问题的注释行数是相当的少。这是因为大多数的注释是讨论如何重现问题。一旦这个问题被重现,它通常很简单的来发现问题的原因和修复这个问题。在我们的问题提交的过程中,我们详细的介绍了如何重现每个问题并为它们提供了补丁,这极大的加速了修复的过程

讨论 CrashTuner为所有的系统检测bug,除了zookeeper。不像我们其他的4个在多个节点传播全局状态系统。Zookeeper保存了整个系统的状态的副本。结果,每个CrashTuner发现40个动态崩溃点,它们就仅仅触发4个不同类型的IO异常,并全被系统给处理了。

4.2 RQ2:可选方法的比较

我们比较CrashTuner和其他两个可替代的错误注入检测方法:随机错误注入方法和OpernStack方法来在IO事件周围错误注入。

4.2.1 任意崩溃注入

在这个实验中,每个系统被以正常的工作量来得到它的运行时间T为叙述。我们通过在每个系统中运行3000次来执行任意的错误注入测试,对于每一次在一个节点的注入崩溃时间在[0,T]的时间范围内随机选择。

表7给了结果。

任意的错误注入测试可以成功过的触发3个bugs:YARN-9194,HBASE-21470和MR-7178,每个bug各自被触发了2,12和2次。所有的bug都被CrashTuner发现。在一个启动一个新节点的过程中一个节点崩溃触发了三个bug。因为它是消耗时间的来启动一个新的耗时的启动,随机的方法有一个很好的方法来击中相关大的触发bug的窗口。综述,随机的方法能够触发一个bug在17.03小时内,平均是937.5次运行中。另一方面,CrashTuner能够在1.7小时或50.29次运行中发现一个bug,这是更加高效的。

4.2.2 IO错误注入

表8中总结了每个基准的IO点的数量。IO类是那些实现了java.io.Closeable的类,网络和文件流类。IO方法是IO类的以以下关键词read,write,flush和close的公共方法。我们使用同样的分析策略来获得动态IO点,如拥有调用contexts的静态IO点。在这个实验中,我们在每个动态IO点的前后注入崩溃事件。结果在表9中。

IO错误注入只能触发一个bug:YARN-9201.。这个问题同样的被CrashTuenr提出了。综上,IO错误注入能够触发一个bug在平均24.15小时和平均750次运行中。大多数被CrashTuner触发的bug在IO错误注入中是不能够被触发的。因为真实的崩溃点距离IO点是很远的。

惊奇于这个结果,我们彻底的检查了在IO错误注入测试日志。许多测试导致异常。例如,在HDFS中,当在写日志文件时,名称节点崩溃后,节点恢复因为破坏了的文件抛出一个LogHeaderCorruptException。然而,异常被系统很好的处理了。经常的,开发人员介绍了对于IO操作的异常处理。结果,IO错误经常可以被容忍,IO错误注入不能有效的触发一个新的问题。

4.3 RQ3:高效

表10比较了元信息类型,字段和访问点对于每个系统类型,字段和访问点总数的数量。通过对日志分析和基于类型的静态分析,2.2%访问点访问元信息变量。静态优化和进一步的分析对于程序中总的访问点各自减少了静态和动态崩溃点到0.58%和0.19%。最后,Hadoop2/yarn有453个动态崩溃点,HBase有257个动态崩溃点,HDFS有237个,zookeeper有40个,Cassandra有69个。

表11展示了在测试每个系统中CrashTuner的次数。分析时间包括在运行系统生成日志的时间,日志分析时间也是静态分析时间。分析时间在第三列被给出,最多三次运行就足够找多所有的动态崩溃点。列4是一个一个的测试所有的奔溃点的总时间。

CrashTuner是非常有效的。它在5分钟完成了对所有所有的分析。在测试阶段,CrashTuner花费了17.22小时对Hadoop2/Yarn测试了453个动态崩溃点。我们的指令和在线日志分析在测试每个崩溃点时没有引入任何性能损失。

4.3.1 优化

在3.1.2中开始的,我们抛弃了以下三种情况中的任意字段引用:1)字段仅仅在构造方法中设置。2)读引用仅仅在日志中使用或是没有使用。3)在使用时进行合理性检查。表12展示了通过优化每个崩溃点的数量。三个优化点一起减少了崩溃点的数量通过3.7.6.极大的 提升了效率。

未来评估CrashTuner的稳定性,我们随机的选取了3000个优化后的崩溃点和3000个对于错误注入测试非元信息访问点。然而,没有新的bug被触发。

4.4 讨论

我们使用了Java实现了CrashTuner并用5个用java语言写的在Hadoop生态系统分布式系统。然而,这个方法可以广泛的应用在不同的分布式系统中。我们研究了Kubernets中的与调度相关的关键崩溃恢复,Kubernets是一个流行的以Golang语言写的分布式资源管理系统。表13展示了节点崩溃在当程序点访问元信息14个bug被触发。Kubernets动态分配和回收节点,导致元信息频繁被更新。当一个节点崩溃,它的元信息仍旧分散在系统中。发现崩溃节点的所有信息和相应的更新他们是很困难的,这经常导致bugs。

我们相信在分析分布式系统时元信息时很适合抽象的。崩溃恢复的根本原因是1)使用旧的元信息来处理坏的元信息。这篇论文解释了对于错误注入测试来分析元信息的效率。它也能静态检测和分布式系统模型检查对于更有效的检查问题。

5 相关工作

崩溃恢复的问题和检测 在崩溃恢复问题有许多经验性的研究。Mesbahi等指出了2.4%节点梅泰诺在谷歌集群中崩溃,这可能引起很多的问题。TaxDC展示了63%的分布式并发问题足以在节点崩溃和其他错误中的展示。研究人员进行外部的经验研究在崩溃恢复的相关问题。两篇文章各自的研究了节点变化问题和异常相关的问题。这些文章为我们提供了方法。

最近的研究聚焦在崩溃恢复问题的检测,通过错误注入测试,错误被随机或系统的注入。系统的方法依靠我们的对错误注入的说明。他们提供特定语言的领域提供用户发现错误注入顺序,场景等等。分布式模型检查拦截消息和在系统中的事件,然后极大的改变他们的序列。尽管很厉害,他们仍然遭受状态空间爆炸问题。FCatch建立了错误事件问题模型作为一个特殊的并发问题。他们通过指令和通过争取执行路径预测崩溃恢复问题。Aspirator是一个对于异常问题处理的静态检查,他们可能通过节点崩溃事件被触发。然而,许多崩溃恢复问题表明他们没有涉及其他异常处理。静态检测这些问题并有一个好的准确率是很困难的。这篇论文介绍了一个新的崩溃恢复错误注入检测方法通过自动推断元信息变量。谁的访问点可能是崩溃点。

DCatch解决了分布式系统的happen-befor关系并采用了动态分析来检测分布式的并发问题。CloudRaid在分布式系统中检测了并发问题,通过打乱之前的顺序。这两个工作可以结合我们的方法来解决更多在崩溃恢复过程中的并发问题。

分布式系统的日志分析 日志分析被广泛的应用在日志分析,监控和诊断分布式系统。Xu 通过对控制台日志应用机器学习技术检测异常执行。DISTALYZER通过异常执行和正常执行比较来研究系统组件的性能。Iprof从日志和配置文件延迟抽取请求ID和时序信息。Stitch组织日志实例到任务和子任务中来配置在真个分布式软件栈的不同组件。CloudRaid雇用日志分析对于分布式系统并发问题的检测。我们挖掘日志来发现在分布式系统的元信息,为了检测崩溃恢复问题。

6结论

我们展示了CrashTuenr,一个新的错误注入检测方法对于崩溃恢复问题的检测。CrashTuner通过元信息分析准确的确认错误注入点,自动的推断谁的访问点是可能暴露错误的错误注入点元信息变量。我们估计CrashTuner对于五个独立的分布式系统CrashTuner可以成功的重现66个存在的问题中的59个,并检测21个之前从未发现的新问题。这些问题可以引起严重的服务器破坏,如集群故障和启动失败。

在我们未来的工作中,我们计划进一步扩展CrashTuner来处理崩溃一致性问题和涉及多个崩溃事件的深层问题。

感谢

我们感谢Haryadi Gunawi的指导和其他匿名的审查者有价值的输入。我们感谢Ting Yuan对kubernets的研究。这篇文章由中国的National Key R&D支持。中国国家自然科学创新基金研究组织,和中国国际自然科学基金。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值