背景:项目中有一块分析数据的功能,在多线程执行用例的时候,每次一个用例执行完,如果执行失败,都会调用分析模块。而分析的方式就是对执行返回的结果和相关的日志进行错误聚类分析。当一批用例执行结束之后,会发邮件报告。
设计思路:采用数据字典的思路,把每次出现的错误都当作一个错误标签存储起来,如果字典中有,就直接关联此次执行结果;如果字典中没有,就插入标签再进行关联。错误标签包括错误内容和对应的应用,关联关系通过插入关联数据来实现。
问题:
1.单机器:多线程执行case,会有多个线程都在执行分析的操作。如果在较短时间内的几个用例,恰好分析出来的候选标签是同一个,并且从数据字典匹配出来没有此标签,就会导致插入重复的标签。
2.多机器(单机器的问题解决的情况下):因为数据字典是跟业务域挂钩的,不同的业务域对应自己的数据字典。如果访问相同业务域的tag,还是会出现插入重复标签的问题,毕竟访问的都是同一个数据库。
解决方案分析:
(1)直接设置同步
最直接的方式就是在分析的逻辑上加synchronized关键字,控制同步。这样一劳永逸,管你多少个并发,分析的时候一个个分析,一个个做db操作。
但是有一个问题,分析是有比较长的时间损耗的,这中间回去调一些RPC服务。当大批量用例执行的时候,如果此次执行错误的个数很多,很容易出现大批量线程卡在分析模块的情况。那么极端情况下,最后一个线程执行的时间点=用例个数*分析时间。导致此次批量执行时间拉的很长,影响用户体验。
(2)缓存数据
在每一次执行之后,都把需要插入标签的数据都放到缓存中暂时存着。只需要插入关联关系的数据则直接插入,因为每个线程对应一个执行结果id,关联关系的数据不会插入重复。在这一批用例都执行结束之后,统一对缓存中的数据做处理。把需要插入的tag和其相关的执行结果id做map,这样一个tag就对应一组执行结果id。插入tag并把其对应的一组执行结果id都关联到这个tag上。
这样的话,相当于把纯db操作留在了执行完毕和邮件组发送之前去做,分析的时间继续放在执行流程中去做。那么db操作的时间总的来说不会很慢,邮件的发送时间会推迟若干秒,不会产生太大影响。
(3)数据库层做处理
在db层做收口,既然所有的数据都走一个db,那就在db层做点文章。
a)ON DUPLICATE KEY UPDATE
首先在标签表中,给标签内容和对应的应用加个联合唯一索引,在maybatis插入语句中增加ON DUPLICATE KEY UPDATE,利用这种机制,当插入相同数据的时候就update时间或者直接不做其他操作。只要不插入重复数据即可。
b)insert ignore
使用ignore关键字,也是需要给标签内容和对应的应用加个联合唯一索引。ignore监测唯一索引,自动跳过重复数据,插入未重复数据。这样不会报错,可以根据返回的影响行数来判读是否插入数据。
数据层处理的好处是原有的逻辑基本不用改,代码改动量很少。但是需要加组合唯一索引,这反而不太好改。因为之前的设计,为了预防标签的错误内容过长,设置的数据格式为longtext,所以加索引是需要设置前缀的。那么标签内容需要限制长度,否则索引会起不到效果。
总结:
(1)总体来说,第一种方案可以排除掉了。第二种和第三种都可以解决多机器的问题。
(2)第二种方式实现起来比较麻烦,需要改动的地方较多,可以作为保底方案。第三种方案最简洁,但是内部原理和执行的效率需要确认。
(持续更新...)