测试思考--项目沉淀---第一篇

上一篇 / 下一篇  2012-12-28 14:36:33 / 个人分类:经验总结

2012-09-26到2012-12-19,测试历时2个半月的项目淡定的上线了,在这个项目中学到很多东西,也积累了非常多的宝贵经验。share一些我的想法。
一.最好设计的时候就想好多种情况,要慎重对待模块已经测试完成,但是又要加东西的情况,即使是加一句配置。
案例分享:有一个功能,按照配置中指定的key算hash取模决定使用哪个线程处理(业务需要),每个源表一个key去写对应的存储表。后来在其中一个表下面加了一个配置,使用另外一个key去写另外一个存储。
例如:有个src表A,原来是用a这个字段为key去写一个存储名为Z的表,ok。现在在A下面加一个,再用b字段为key去写存储名为X的表。也ok。但是平静中按藏杀机。在收尾测试的时候发现一个现象:另外一个源表B也是用a字段为key写存储名为Z的表(并且B表的a字段和A表的a字段是相同的)与数据源传过来写的顺序不一样。一开始我们一直在怀疑是上游传过来的数据没有保证顺序,认为分发的模块认真的测过是不可能有问题的。后来连数据库的binlog都翻出来了,发现不可能是数据源的问题。抱着试试看的态度嵌入代码进行调试。你猜对了,就是我们分发模块的问题。而问题的导致原因就是后面临时加了“现在在A下面加一个,再用b字段为key去写存储名为X的表。”而这个配置没有影响到它自己这个表A。而影响到了在同一个库另外一个表的并且使用相同字段做key的那个。原因是,在A下面又加了b字段为key写另外一个表后,真正的key变成了a+b。而另外一个表B用的key还是a。很明显使用的key发生不一致了,所以他们根本不可能发到一个线程上处理。
问题分析:
1.为什么临时加这个配置的时候没测出来,而是到后面数据验证测试的时候才发现:只考虑到加了这个配置后对这个表是否生效,而没有考虑到加了这个配置会不会对其他表产生影响。因为这个模块的部分已经结束测试,已经在进行后面的模块测试,临时的加配置没有进行全面的回归。
2.其实数据验证也是到后面才发现的,前面也没发现有什么差异,为什么:
这个就跟业务有关系,首先就算A和B没发到一个线程里面进行处理(先不说时间上的差异),但是B自己里面的东西还是可以满足分发策略计算的逻辑是不会出现任何问题。但是有一个业务要求是:数据库B表的更新会触发A表的更新,我们看数据库的时候发现A和B表的变化时间是相同的(因为只能看到秒级别),再去拿binlog分析问题的时候发现对于B通知A要跟着变是在同一个事务里面做的,数据库的事务是什么不多解释,就是要么都做要么都不做checkpoint是一样的,所以到秒级别的必然看起来是相同的。也就是说B的更新,我们A的计算一定要用B最新的数据,这是业务上的要求。其实我们的程序不能把A的key和B的key分到同一个线程已经是非常的不合理了,但是数据验证的时候其实很长时间都没发现后面计算出来的有差异,为什么呢,因为后面计算的程序会等5s,也就是说在等5s的时候,这个时间内就算没在一个线程中处理没保证顺序足够他们更新到存储,所以后面计算出来的还是对的。而发现这个问题的那天是因为有其他大job在跑给了存储很大的压力导致更新有延时,等待的5s也不足以解决这个问题了,于是这个问题就暴露了。
思考:
怎么从根本上避免,尽量在设计的时候就把考虑到的东西设计加进去。如果后面一定要动,那么要评估风险,并且回归的面积一定要大,覆盖到所有情况。

二.任何给下游使用的标志的东西,一定要清楚下游是如何使用的,而不是你用我给你就好了。
案例分享:我们程序处理完的一些数据会打标志到zookeeper上,和hdfs上来通知下游,代表数据好了哦,你可以拿走使用了。
例如:我程序9点做完一个东西了,然后在hdfs打个9.done,10点做完一个东西在hdfs打个10.done,以此类推。9点作为第一次算完的数据呢会在zookeeper上写一个节点a为9。此处请注意:我们先了hdfs上的done标志,然后在zookeeper上的节点写做完的时间,也就是9。注意:zookeeper的只有第一次会打,也就是9点那个会打,也就是每天只有一次这个写zookeeper的操作。
下游如何使用这个标志呢,它读zookeeper,把时间读出来,然后看那些done标志的时间(看的是没做的时间,这个下游自己会知道哪些做过哪些没做过),比如现在是10点05分,我上次做到8点多的数据,那么我拿到的要做的就是9点和10点的数据。然后下游会拿zk刚才拿到的9点和没做的有done标志的时间比,也就是和9点和10点的比,发现有9这轮,好的,我只做这轮,并且写另外一个zookeeper的节点,写做的这个时间也就是9(其实是包括日期比如20121225090000这种)。如果发现没有,那算了,我把9点10点的数据合并拿过来做掉,并且不会操作zookeeper。
有问题了,问题在哪:大家注意我刚刚说的顺序,我们是先在hdfs上打了done标志,然后写的zk。那么虽然几乎是瞬间或者1,2s。但是如果就刚好在这中间,在还没来得及写zookeeper的时候,下游来了,下游检查我拿到的zookeeper的时间,拿到昨天的时间了,开始找有没有和我没做的done标志一样的呢,能找到就奇怪了,找不到那我就只拉数据做,不写zk。下一次有启动了,下游又来找数据,虽然这次可以拿到zk上今天的时间戳了,但是和这个时间戳相等的数据已经被做掉了,以后都不可能找到相同的了。大家可能觉得,下游不写那个zk拉倒呗,反正数据也没丢还照做呢。可惜不是这样的,首先下游程序本身的不说。下游写的那个zk标志,我去,他的下游还要用,他的下游根据这个zk看是不是今天的新数据,也就是说这个标志决定他拿不拿走数据。说了一堆不懂的人估计也能知道咯大概,就是我们有下游,我的下游有下游。就只是1个写标记的顺序就足以牵动2个下游,恐怖有木有。
问题分析:
1.为什么没有发现这个问题:因为所有人都没有想到,确切的说是考虑的不够全面,我们都只想到下游要用这两个标记,写了就好,但是没有去了解他们是如何使用的,给你就行呗,我管你怎么用,这种思想也得被淘汰了。
2.为什么日常哪的都跑也没发现呢,因为这两个标志就脚本上下的区别时间差太小了,可以说线上没问题是有一定的运气在的。碰巧某天某个集群要试验个东西,把流量切走了,碰巧那天出现了这个问题。
思考:
这个首先就是要有上下游的概念,但是如果遇到上面的情况光是意识就不够了,还得上升到how to use,还得知道人家是怎么用的。再就是真的靠经验了,比如我,我经历过我记得我深刻。

后记1:
上面说的这两个问题虽然都没有影响到线上,但却是我觉得非常非常恐怖的问题,为什么,因为他们是非常非常小的概率会发生的状况,也就是说,不是那种很明显就能发现的,如果这些问题一直没发现,那么可能跑个半年都没事,突然某天发生了上面两种极端情况,那么处理起来会相当的棘手,2可能还稍微好一点因为是运维脚本,并且下游可以恢复。但是如果是1这种情况,就可能已经在这个系统上做了多个业务的迭代,谁能想到是一直跑的系统本身有问题呢,而且影响到线上,估计要排查很久才可能发现,并且线上恢复的话时间根本不够,想想都后怕。怕的不是很明显的问题,最怕的就是非常不明显,是太隐藏然后突然爆发让人措手不及的问题。

三.不要认为新的系统调用原来的不变的程序是可信的,没有程序没有bug,只不过一直没有合适的场景触发它。
我一直都觉得就算线上的东西也有bug。没人反馈不代表我们的数据是对的,只不过人家没看出来或者没关注;算出的数据是对的不代表程序没有bug,只不过没遇到触发bug的场景。
这次算是验证这点了,这次数据验证的时候就发现一个问题,同时验证了上面两点。第一点不说了,因为影响的数据不多,而且只有在一些特定情况发生,原因是业务中中间可能有过变动,跟当初做那个时候给的限制条件什么的有了出入,没有通知我们,而新系统因为算的更快可能会比老的明显所以fix掉了。
说下第二点,这个bug我觉得还算是很经典啊。
初始化的问题,程序会有个拿字段的时间a和初始化的一个时间time做比较,time呢取的是当前系统时间,也就是程序运行的时间,当a>time的时候才有效。发现的现象就是很多明明a<time(当前时间)的数据也是成有效的了。因为这块使用的原来的程序计算跟本没有动过,第一反应肯定是我们新的程序有问题,但是发现我排查发现我们算的数据是对的啊,奇怪啊,然后开发重新看了下那个没动的处理程序,的确有bug。是什么bug呢,在给time做初始化赋值的时候,在类里面用static private time=****。而我们跑的是mapreduce程序也就是说只会在启动的时候进行赋值。那么老的为什么没问题呢,因为老的是3分钟启动一次,也就是说影响不会超过3分钟,是可以接受范围内的。而我们新的程序是只启动一次,也就是说我5点起的到20点这个time也是5点,也就是说期望判断和程序计算逻辑的时间的比较完全失效了。后来我们在process里面增加重新计算赋值就ok了。
四.处于中游的程序,任何变动通知上下游,不可"以为"。
这个就是全局意识了,而且也是一个非常好的习惯,也能防患于未然,毕竟人家的程序怎么回事人家最清楚。比如我们现在算的快了,产生也快了下游的速度是否跟得上ok吗,比如我们要加一个数据源,上游帮你控制编码什么的,或者上游能不能快速影响你,这都是很好的习惯。
五.good luck

后记2:可以说项目临上线的后一个月基本晚上10点前就没回去过,每天脑袋里都是项目,甚至还不争气的想过要是师姐在,这种优先级的项目,她肯定会把控风险,理清数据关系,分析数据神马,何必我在这一点点摸索,一点点找问题还各种担心。但是整个项目做下来,包括后面无流量的检查等等,不能说完美,但至少一次发上去,0投诉0故障,之前一切的付出都是值的。虽然比较累,但是还是非常喜欢测试这种项目,从设计讨论到代码review到测试到预发到上线,这一个完整的参与学到的东西和经验是无价的,而且非常喜欢和我们的开发一起努力,和他们一起工作你不觉得是无聊枯燥而是非常开心,因为大家在一起为项目努力加油,他们的责任心和能力也让你更加有信心。然后这种项目还得有那么一点点毅力,因为项目时间越长就越拖不起,不像日常1天测完就上线那种你是马上看到效果的,而几个月的东西你要持续的努力有耐心。
  ok总结,告一段落,下一个重构项目马上开始,fighting~~~



TAG:

haiying86的个人空间 引用 删除 haiying86   /   2012-12-28 20:50:37
5
 

评分:0

我来说两句

Open Toolbar