[软件测试] 可测性分析和实践

软件测试中可测性一般是指对系统的可控性、可观测性进行的评估,借以反映系统设计、实现对测试的友好程度和相应的测试成本。可测性在测试阶段会对系统的测试成本及关联产品代码的Patch次数产生重大影响。如何提高可测性成为软件生命周期特别是前期(设计阶段、coding阶段)重要的一环。 本文带领大家探索在实际项目中可测性相关的实战经验和对应的改进措施。 1 提高可测性的切入点 可测性的评估和改进最早开始于两个阶段: a. 新项目的设计阶段; b. 已有项目新功能、新策略的提测阶段。 这些是提高团队设计的系统可测性和维持系统的设计高可测性的关键时间点。测试人员会利用各种场合、机会强化开发人员对于可测性的重视: 可测性的重要性每次设计讨论会, 测试负责人必提醒大家在设计时注意可测性。否则设计出来的功能很可能需要进行重构。 可控性 每次晨会中的讨论环节, 测试负责人会提醒模块设计人员,设计的功能需要必要的外部控制、执行动作支持,否则QA无法精确控制过程及缩短测试耗时。 可观测性每个功能进行测试设计阶段,提前和开发人员沟通必要的功能,观察结果集合可以系统外部获得,错误结果可以被暴露而不是由内部逻辑完全消化。 那可控性和可观测性又指哪些方面呢? 如何向开发人员合理的解释可测性? 1.1 可控性 可控性指系统的状态可受外部控制改变,而不是由内部模块自发的完成。 举个常见的例子: A. 当某文件存在的时候,该模块自动退出; B. 当某pid.lock文件存在时,该模块不能启动,即使启动也退出。 上面的状态改变都是由一个外部的文件控制,拥有可控性。 说到这里,问题来了,拥有可控性就万事大吉了吗? 请大家思考,你在实际项目过程中遇到过哪些有可控性但可控性较差的情况? 1.2 可观测性 可观测性指系统内的重要状态、信息可通过一定手段由外部获得。可观测性不仅能观测系统的输出是否符合设计要求,还影响该系统是否可控。系统的必要状态信息在系统测试控制阶段起决定作用。没有准确的状态信息,测试人员无法判断是否要进行下一步的控制变更。无法控制状态变更,可控性又从何谈起? 口说无凭,我们来看几个作者实际项目中遇到的真实案例。 2 实战分析 [1] 垃圾回收GC 垃圾回收GC模块是常见的系统内模块,相信很多测试人员遇到过下面场景或者类似场景: 开发人员终于在大吼一声后宣布垃圾回收模块完成,她的描述如下: 1) 该模块定时自动触发。触发条件是每天晚上1点。 2) 该模块触发后每秒的处理量是N/s。是根据线上情况得到的经验值,硬编码到代码中。 然后,就没有然后了。 测试人员一阵迷茫,这就是全部的询问换来的基本上是“它都是全自动的了,你还想要什么的”表情。 因此这个新功能完成后的二次返工是必然的了。 首先,该模块的可控性太差。测试环境不可以等待每天晚上1点这个时刻,必须有外部能影响这个”全自动“的手段提供。否则全量的系统测试用例回归会被限定在固定测试时间点且无法调整和更改。 其次,该模块的每秒处理量必须能更改到符合测试环境。测试环境基本上都是真实环境的放缩,特别是分布式系统等大规模应用。测试环境机器无论是数量还是类型都远低于实际环境。这种条件下,参数的定量调整是必须要完成的辅助支持。 再次,没有必要的描述如何判断哪些文件/数据被GC掉了。无法观测到执行结果集带来的后果是无法精确的预期测试结果。 而相应的改进措施就是解决上面提到的问题。 [2] 系统内部状态信息 为了保证存储的数据高可用,分布式系统会采取多机存储副本方法。即一个数据被N(>=2)个机器以一定的算法存储相同的数据副本。这个时候经常会遇到的问题: a) 机器间的数据由于数据复制顺序的不同,会有数据差异。a、b、c三台机器,a、b机器可能已完成一次数据的更新到最新数据版本data1,c还处于老版本data0. … Continue reading “[软件测试] 可测性分析和实践”

分布式存储系统的雪崩效应

一 分布式存储系统背景 副本是分布式存储系统中的常见概念:将一定大小的数据按照一定的冗余策略存储,以保障系统在局部故障情况下的可用性。 副本间的冗余复制方式有多种,比较常用有两类: Pipeline:像个管道,a->b->c,通过管道的方式进行数据的复制。该方式吞吐较高,但有慢节点问题,某一节点出现拥塞,整个过程都会受影响 分发:client -> a  client ->b client ->c。系统整体吞吐较低,但无慢节点问题 对于冗余副本数目,本文选择常见的三副本方案。 分布式存储系统一般拥有自动恢复副本的功能,在局部存储节点出错时,其他节点(数据副本的主控节点或者client节点,依副本复制协议而定)自动发起副本修复,将该宕机存储节点上的数据副本恢复到其他健康节点上。在少量宕机情况下,集群的副本自动修复策略会正常运行。但依照大规模存储服务运维经验,月百分之X的磁盘故障率和月千分之X的交换机故障率有很大的可能性导致一年当中出现几次机器数目较多的宕机。另外,批量升级过程中若出现了升级bug,集群按照宕机处理需要进行副本修复,导致原本正常时间内可以完成的升级时间延长,也容易出现数目较多的宕机事件。 二 雪崩效应的产生 在一段时间内数目较多的宕机事件有较大可能性诱发系统的大规模副本补全策略。目前的分布式存储系统的两个特点导致这个大规模副本补全策略容易让系统产生雪崩效应: a. 集群整体的free空间较小:通常整体<=30%, 局部机器小于<=20% 甚至10% b. 应用混布:不同的应用部署在同一台物理/虚拟机器上以最大化利用硬件资源 今年火起来的各种网盘、云盘类服务就是a的典型情况。在各大公司拼个人存储容量到1T的背后,其实也在拼运营成本、运维成本。现有的云存储大多只增不减、或者根据数据冷热程度做数据分级(类似Facebook的数据分级项目)。云存储总量大,但增量相对小,为了减少存储资源和带宽资源浪费,新创建的文件若原有的存储数据中已有相同的md5或者sha1签名则当做已有文件做内部链接,不再进行新文件的创建。但即使这样,整体的数据量还是很大。 目前云存储相关业务未有明显的收入来源,每年却有数万每台的服务器成本,为运营成本的考虑,后端分布式存储系统的空闲率很低。而瞬间的批量宕机会带来大量的副本修复,大量的副本修复很有可能继而打满原本就接近存储quota的其他存活机器,继而让该机器处于宕机或者只读状态。如此继续,整个集群可能雪崩,系统残废。 三 预防雪崩 本节主要讨论如何在系统内部的逻辑处理上防止系统整体雪崩的发生。预防的重要性大于事故之后的处理,预测集群状态、提前进行优化也成为预防雪崩的一个方向。 下面选取曾经发生过的几个实际场景与大家分享。 1. 跨机架副本选择算法和机器资源、用户逻辑隔离 现场还原: 某天运维同学发现某集群几十台机器瞬间失联,负责触发修复副本的主控节点开始进行疯狂的副本修复。大量用户开始反馈集群变慢,读写夯住。 现场应对: 优先解决——副本修复量过大造成的集群整体受影响。 a. 处理的工程师当机立断,gdb到进程更改修复副本的条件为副本<2,而非原本的3(replicas_num),让主控节点这个时候仅修复副本数小于2个的文件,即保证未丢失的文件有至少一个冗余副本,防止只有一个副本的数据因可能再次发生的挂机造成文件丢失。 b. 紧急解决这批机器失联问题,发现是交换机问题,a.b.c.d ip网段的c网段机器批量故障。催促网络组尽快修复。 c. 副本修复到>=2之后,Gdb更改检测副本不足周期,将几十秒的检测时间推迟到1天。等待网络组解决交换机问题。 d. 网络恢复,原有的机器重新加入集群。大量2副本文件重新变为3副本,部分3副本全丢失文件找回。 e. 恢复主控节点到正常参数设置状态,系统开始正常修复。 改进措施: 在改进措施前,先分析下这次事件暴露的系统不足: 1) Master参数不支持热修正,Gdb线上进程风险过大。 2) 一定数量但局域性的机器故障影响了整体集群(几十台相对一个大集群仍属于局域性故障)。如上所述,月千分之几的故障率总有机会让你的存储系统经历一次交换机故障带来的集群影响。 案例分析后的改进措施出炉: 1)  Master支持热修正功能排期提前,尽早支持核心参数的热修改。 热修改在上线后的效果可观,后续规避过数次线上问题。 … Continue reading “分布式存储系统的雪崩效应”

如何优雅的打日志 – optimal-logging

发件人:                           Ma,Guannan 发送时间:                       2014年3月9日星期日 21:40 收件人:                           Ma,Guannan 主题:                               优雅的打日志 原文地址:http://googletesting.blogspot.jp/2013/06/optimal-logging.html 目测需要翻墙。] 作者:Anthony Vallone 翻译: 马冠南 在你的系统故障后,一般需要多久才能找到根本原因? 5分钟? 5天? 如果你的回答是[接近五分钟]。可能你的系统有非常棒的Log输出(机制)。太多时候,这些看起来不是特别重要的处理逻辑如Log输出、Exception处理、测试点等内容都是人们事后才加入的。永远不要低估logging的作用。一个好的日志logging机制可以使调试者需要更少的相关信息来做debug工作。 以下是数年来对我很有帮助的一些方法: 梳理你那不温不火的日志吧: 不要打印太多(无用信息)。 大量的、(打印速度)能让硬盘着火的打印方式说明你没怎么在日志打印上花精力。如果你大量打印, 需要考虑怎么去减少硬盘存取、维护历史日志文件、将他们归档、并且对它们做查询和信息收集。 更重要的是,事后你会发现这些大量打印的无聊东东会让你很难收集到有价值的信息。信息量太大且无用了。 同样,也不要吝啬你的打印语句。 一般情况下,打印日志是为了找到系统的bug或者复现某个问题、或者对某件事情做确认。如果你的日志不能帮你找到bug的原因或者不能帮你确认某件事情确实发生了, 你就需要多打印一些,兄弟,不要太吝啬你的硬盘。 一些适合打印的东西: ·         [启动阶段] 关键的配置参数 ·         [运行阶段] Error级别信息 ·         [运行阶段] Warning级别信息 ·         [运行阶段] 对持久化的信息做了新增或者改动 ·         [运行阶段] 重要的状态变更 ·         [逻辑处理] 对系统非常重要的分支被触发, 可能是逻辑上或者某个等待事件方面比较重要的分支. ·         [逻辑处理] 长时间运行的逻辑中周期性处理的部分被触发和完成时 ·         [逻辑处理] 在上层函数中做言简意赅的日志打印,反应目前的处理逻辑进行到哪一步了. ·         [资源竞争] 某个可能比较耗时的条件的wait操作将被触发 ·         [用户交互] 用户操作 ·         [已知风险] 监测到已知风险被触及 一些不适合打印的东西如下: ·         [内部逻辑] 不要打印函数的入口 = = 除非她十分的重要, 或者在DEBUG level去打印她。 ·         [数据记录] 不要打印for while循环内的数据记录 = = 尽量避免打印一个数据比较多的循环内的数据。 可以在上层的大循环中打印。非常重要且数据量小的可以尝试打印。 ·         [异常处理] 巨大的信息、或者文件类。 可以用某种方法把她们做归类、聚合,总结性的打印日志 ·         [异常处理] 良性、可被正确处理的错误。该类错误可能对日志阅读者产生困惑。可以用DEBUG level的方式打印她。 ·         [异常处理] 重复性的错误日志。不要重复的打印相同、相近的日志。 这样打印会很快的充满你的日志、掩盖真正的错误原因。通过打印一条然后标定他的出现次数是个不错的选择。选择有特殊需要和目的的地方进行细致的打印。 使用多种日志打印级别 不要把所有东西没有区分的打印在同一个层次里面. 最好划分几个层次. 一般的log库都提供数个日志Level.你可以在启动阶段使用其中的几个level. 这能很好的控制日志的详细程度。 经典的几个日志Level有: ·         Debug – 非常详细的打印level、一般只在开发或者调试阶段才会打开这个level. ·         Info – 最受欢迎的level, 一般生产环境都设置成Info Level. ·         Warning – 奇怪或者异常的情况发生时的打印level, 这类日志表明出现了一定问题, 但一般不影响系统的使用。 ·         Error – 发生了错误, 但是系统、进程可以恢复。 ·         Critical – 发生了重大问题。一般系统、进程等受到影响会死掉、重启或者被关闭。 从实践的经验来看,一般两个日志配置项是需要的。 ·         生产环境  – 除了Debug level之外,其他的层次都是需要的。 这种情况下, 一旦发生了错误,一般日志能很好的找到错误原因。 ·         开发、调试环境- 在开发新的feature或者模块或者需要复现比较难的生产环境问题时候, 会激活所有的日志level. 测试程序的日志是同等重要的     打印日志的质量在生产环境和测试环境是同等重要的。如果在测试环境中,某个测试用例失败了。测试code中打印的日志应该能清晰的反映出这次失败  – – 是用例错误导致的失败、还是被测的程序确实产生了问题。如果反映不出,则这个日志打印规则与生产环境是不统一的,需要调整。 测试日志应该包含: ·         测试执行时的环境信息 ·         初始状态 ·         程序setup内容 ·         测试中的关键步骤的checkpoint点 ·         与被测系统的交互 ·         测试用例的期望结果 – 若出错,一并打印实际的结果 ·         环境清除内容和执行结果 提供动态的日志Level的激活和关闭 (比如设定条件触发)     当生产环境发生问题时候,很明显, 大家一般会去聚焦在查找问题、解决问题。但与此同时,请应该把log也考虑进来。如果你发现,诶,这次很难发现这个问题的原因,你就赚到了。这是一个改善你的日志打印的机会。在fix你的bug或者问题之前,先优化下你的日志,让她下一次在问题发生后能很好、很简单的展示错误发生的原因。 如果你不能复现这个问题或者这是个不稳定的问题,优化这个log直到当再次发现问题时能很好的帮助debugger追踪到问题。 … Continue reading “如何优雅的打日志 – optimal-logging”