一个疑难bug的解决过程

一个crontab脚本,下载一个文件并把内容入mysql数据库。具体流程如下:

1, wget一个文件。

2,处理文件生成一个中间文件。

3,将中间文件load入库。

05 10 * * * /home/work/local/php5.4/bin/php /home/work/www/new_products1/web/index_cli.php actionads/index

  

bug现象:

在线上,5次运行中,有2次会出错,就是入库的数据会缺失30%以上。

在线上手动运行脚本,没有一次出错。但配置成crontab,5次运行中,就会出错2次。

难点:

这种不可复现的bug,实在是难找原因。我们只能保存当时的现场,但脚本每天只运行一次,一次解决不了bug,可能需要等好几天才能复现这个bug。

解决流程:

1,怀疑是load文件时,丢失了部分数据。

于是解析mysql的binlog日志,找到了load的那一部分,将日志中获取到的文件字节数相加 (mysql load过程中,每下载一部分文件,会记录一下获取到了多少字节),发现文件没有丢失。同时,找到了mysql load时保存的临时文件,也是没有丢失数据的。

2,生成中间文件时,出错。

虽然开始确认了中间文件出错,但找不到为什么出错。。。

文件的开始有约30%的内容是ascii=0代表的字节。于是怀疑是不是磁盘坏道了。。。

一个程序读一个文件,处理后,写另外一个文件,在线下运行一直没有问题,程序里面也没有使用随机数之类的。。。所以我确认这段程序肯定是没有问题的。

3,假设磁盘坏道了,找证据。

我想坏道的话,把系统调用记录下来,应该可以看到原因。于是我把crontab改成:

05 10 * * * strace -f /home/work/local/php5.4/bin/php /home/work/www/new_products1/web/index_cli.php actionads/index 2>>temp.log

发现read write系统调用没有出错的情况。

但同时也发现了一个现象,read源文件时,读到了大量的ascii=0代表的字节。如图:

同时,写中间文件时,也写入了大量的ascii=0所代表的字节:

这就解释了为什么中间文件会有这么多0,原因就是读到的源文件就有这么多0.

4,为什么源文件出错?

还是百思不得其解,下载的文件字节数也没有问题,说明数据没有丢失。为什么到线上就有问题呢?

眼睛盯着屏幕,好像突然发现了问题

*/5 * * * * /home/work/local/php5.4/bin/php /home/work/www/new_products1/web/index_cli.php appcall/index
*/5 * * * * /home/work/local/php5.4/bin/php /home/work/www/new_products1/web/index_cli.php actionads/index

原来有两个crontab,另外一个crontab也是下载一个文件,并且这两个crontab下载到的文件的名字是一样的。

会不会是它们两个冲突了呢?写一个脚本来验证。

wget ftp://xxxx:[email protected]/wenyisheng_tab23/20150904 -O 888  2>/dev/null &
sleep 1
wget ftp://xxxx1:[email protected]/app_diaoqi/20150904 -O 888 2>/dev/null &

第一个wget下载到的文件有20M,第二个只有1M。为了故意生成冲突,我就让中间sleep 1秒种。

发现最后文件888,字节数跟第一个wget获取到的文件一样,但内容不一样,内容中间夹杂了第二个wget的文件。

后记

其实这个bug应该是可以很早就发现的,检查一下下载到的文件内容就可以发现。

我把它想复杂了,一看文件字节数没有问题,就确认这个文件也是没有问题的,所以就把这个线索中断了,被引入了歧途。

其实遇到疑难bug,不要急于下手,可以回顾一下流程,列一下可能出错的地方,一个一个排除,我想应该很快搞定它。

PS:

在追踪bug的时候,发现php yii框架在记录日志的时候,会把日志锁上,如下:

open("/home/work/www/logs/app.log", O_WRONLY|O_APPEND|O_CREAT, 0666) = 4
fstat(4, {st_mode=S_IFREG|0664, st_size=969524, ...}) = 0
lseek(4, 0, SEEK_CUR)                   = 0
lseek(4, 0, SEEK_CUR)                   = 0
flock(4, LOCK_EX)
stat("/home/work/www/logs/app.log", {st_mode=S_IFREG|0664, st_size=969524, ...}) = 0
write(4, "2015-09-04 09:25:01 [-][-][-][in"..., 8192) = 8192

为什么要加锁呢?这就验证了我以前一篇博客中的结论《日志会被写乱吗?》

因为Yii写日志的时候,会把日志记录在内存中,在一次请求处理完以后,统一写到磁盘,这时候日志会比较大,

一次write调用写不完,为了防止其它请求把日志写乱,就把日志文件加锁了。

我想这样会影响并发性能。

时间: 2024-11-16 14:01:58

一个疑难bug的解决过程的相关文章

记录一个前端bug的解决过程

人在江湖飘,哪能不挨刀. 我挨了重重一bug.严格来讲这可能是我职业生涯以来的首个悲惨经历,因为凭我的知识储备和经验,基本上任何可重现的bug都是可解的.然而这个bug却困扰了我三个月之久,它具有以下生理特征: 后台日志能统计到异常,偶发.低频 报异常的用户设备不具有规律性,什么手机都有 我们自己无法复现,任何设备.任何环境都没复现 打电话回访报异常的用户,确实存在问题 客服未接到用户主动反馈这个异常 此bug并不是js报错,而是一个业务逻辑的错误.表现是,用户提交的数据莫名缺失.场景是以下这个

Win10 UWP系列:关于错误 0x80073CF9及一个小bug的解决

原文:Win10 UWP系列:关于错误 0x80073CF9及一个小bug的解决 最近一直在开发XX的uwp版本,也是边摸索边做,最近遇到几个比较奇怪的问题,记录于此. 1.项目可用部署到PC,但无法部署到手机,提示以下错误: 错误 : DEP0001 : 意外错误: Install failed. Please contact your software vendor. (Exception from HRESULT: 0x80073CF9 为了方便开发,我将常用的类库引用好.默认的几个页面做

STM32 .ld链接文件分析及一次bug解决过程

目录 STM32 .ld链接文件分析及一次bug解决过程 问题描述 解决办法 ld文件解析 后续 STM32 .ld链接文件分析及一次bug解决过程 问题描述 原子板的代码中含有一个关于使用外部SRAM的功能,由于本人的开发板的SRAM只有512K,因此稍微修改了一下代码,同时使用GCC进行编译,但是这里却报错了,源码如下: //内存池(4字节对齐) __align(4) u8 mem1base[MEM1_MAX_SIZE]; __align(4) u8 mem2base[MEM2_MAX_SI

一个图文混排问题的解决过程

需求如所示:左边的是效果图,右边的是完成后的图                 具体需求描述:用户的回复一条就是一个cell,字数不定.当存在某种条件时,需要在文字(可能换行)的最后一个字后面添加一个能点击的删除按钮小图标,文字支持emoji表情符号. 解决过程: 1.一看到这种文字中需要穿插图片的就想到了富文本中的NSTextAttachment对象,它可以包装一个image后生成一个富文本,再给UILabel显示就能完成显示需求,但是,该对象只能包装UIImage对象,无法监听图片的点击事件

一个struts2登录bug的解决

点登录的时候,在url后面总会加上一个;jsessionid=xxx 使找不到页面 的404 Bug ,百思不得其解,最后终于找到解决方案,实验最终成功解决了这个bug,下面是解决方案 1,增加依赖   <dependency>            <groupId>org.tuckey</groupId>            <artifactId>urlrewritefilter</artifactId>            <v

思考一个问题的解决过程

环境:CentOS 6.2 问题描述: MySQL返回错误提示为: Can't connect to local MySQL server through socket '/tmp/mysql.sock' (2) 项目中需要实现代码导出MySQL数据库中表结构的功能,于是我先单独写这个功能,然后整合上去,单独的小程序可以了,但是整合上去却无法连接MySQL数据库,返回的错误码和提示如标题. 问题分析: 这个问题出现的奇怪之处是,单独的小程序可以使用,但是整合到项目中却不可以使用,而且错误提示是/

关于在for循环里调用ajax时只能取到最后一个数据的bug的解决方法

首先,造成这中情况的原因在与ajax的异步请求机制,for循环的运行速度远大于ajax异步请求的速度,这就造成了ajax运行时,需要的在for循环里的参数已经运行到最后一个了,所以取到的都是最后一个参数数据. 解决方法:将ajax的请求封装成一个单独的方法,然后在for循环里调用该方法.

Arduino下LCD1602综合探究(中)——如何减少1602的连线,LiquidCrystal库,LiquidCrystal库中bug的解决方法

一.前言: 上文中,笔者系统的阐述了1602的两种驱动方式,并简单的提到了Arduino的LiquidCrystal库.本文紧接上文,对以下两个问题进行更加深入的探讨:如何能够使1602对Arduino的端口占用降到最少?LiquidCrystal库到底应该如何学习?在Arduino下LCD1602综合探究(下)中,笔者拟介绍一个1602的综合性实验,以期对前文的内容加以综合应用和总结.闲话少说,下面开始进入正文. 二.减少1602对Arduino的端口占用 <1>不使用I2C 在上文中,笔者

MySQL数据库ab主从复制出错及解决过程

MySQL数据库ab主从复制出错及解决过程 一.mysql主从服务器报错描述:Slave_IO_Running=NO,Slave_SQL_Running=YES,Last_Errno=0 mysql slave stop ; mysql slave start; mysql show slave status ; 如果Slave_IO_Running=YES ...解决过程 :1 如果:Slave_IO_Running=NO,Slave_SQL_Running=YES,Last_Errno=0m