谈谈企业管理软件领域内那些很难稳定重现故障的处理技巧
前言
我是做企业管理软件的程序员,有一次我遇到一个问题,一段后台作业代码,运行时偶尔会出现运行时异常(runtime exception),但这个异常不是 100% 能重现,运行十次,大概能重现2,3次。而且在系统负载很重的时候,反而一次也不能重现。
更折磨人的是,如果在交互式单步调试模式下,这段代码运行完美,一点问题也没有。既然不能通过单步调试来排错,我的同事们都觉得棘手,最后让我来和这个问题死磕。
后来我采用类似二分查找的方式,把可能引起这个问题的代码层层过滤,最后定位到几行有高度嫌疑的代码,我自己编写了一个测试程序来模拟后台作业执行出错时的运行环境,才找到罪魁祸首:我们的一个模型创建 API,不支持模型创建和模型删除,在一秒钟之内完成。
也就是说,用户在 UI 界面正常操作时,手速再快,也不可能完成在一秒钟之内,做到先创建模型,然后马上删除的操作。但是后台作业是用代码调用 API,在系统负载不高的情况下,一秒钟之内完成创建并且删除的操作,不是一件困难的事情。这个时序问题也解释了为什么这个问题在单步调试模式下无法重现。
下面是文章的正文。
企业管理软件面向的是企业级用户,如果软件出现故障(bug),在某些极端情况下,可能会让企业蒙受巨大的经济损失,故而对软件开发人员在编程规范,软件测试和软件交付之前的验证等各方面都提出了更高的要求。同时,由于企业管理软件自身高度的复杂性,有些故障很难重现或者只能在运行了客户特定业务流程的生产系统上才能重现。这些都给企业管理软件分析和故障处理带来了巨大的挑战。
本文从 Jerry 处理过的一个实际软件故障出发,谈谈自己对企业管理软件里一些棘手故障的处理体会。
在 Jerry 看来,这些棘手故障,可以分为以下几类。
企业管理软件领域内棘手故障的一些表现形式
我在 SAP 成都研究院处理过很多颇让人头痛的软件故障,它们具有下列一项或几项特征。
1. 需要复杂的流程才能重现
例如我处理过的 SAP Business ByDesign 里一个客户发票(Customer Invoice)相关的故障。这个故障只有在每次 release 发票时才能重现。为了 release 发票,我们必须先创建一个销售订单(Sales Order),基于该订单创建 Customer Demand,然后创建捡货任务(Pick Task),生成交货单(Delivery Note),最后才能生成一张新的客户发票。
这些复杂的流程往往也需要系统事先维护好对应的主数据(Master Data)和事务数据(Transaction Data)才能顺利执行。复杂的业务流程增添了故障重现的难度。
2. 故障横跨企业管理软件的多个模块
由于企业管理软件自身的复杂度,终端用户眼中看到的貌似简单的一个故障,背后可能横跨了软件实现的多个模块。
以上述形式 1 描述的故障为例,假设软件帮助文档上描述的支持功能为:客户在销售订单界面上添加了一个新的自定义字段并维护了对应值,该值能够从销售订单,经捡货任务,交货单,最后传递到客户发票上。我们称这种字段值从多个文档间的传递称为 data flow.
那么如果客户在发票页面上,看到这个字段的值为空,客户可能认为是发票模块出了故障。然而,在 data flow 的每个节点对应的模块处理,可能都是造成该故障的罪魁祸首。销售订单和客户发票属于 CRM 模块,而捡货任务和发货单则归属 SCM 的范畴。
在实际开发工作中,这意味着分析该故障往往需要跨团队间协作,因为 CRM 和 SCM 模块往往分属不同的开发团队负责。
3. 故障只能在客户生产系统重现
在企业管理软件交付之前,必定在内部开发,测试和验证系统(validation system)进行过不同层次的测试。即便如此,由于种种客观原因,比如当应用运行在客户生产系统上,基于某些只有该客户才会用到的特定业务流程的配置时,故障才会暴露,而这些配置并没有被企业管理软件供应商的内部系统测试所涵盖到。
这类故障因为只能在客户生产系统重现,在分析和定位问题时更加困难重重,尤其当重现步骤会在客户生产系统进行写操作时,通常只能联系客户相关人员,采用远程桌面+电话会议的方式,让客户相关人员进行操作,然后软件供应商的支持人员在线调试。
4. 故障只能在后台作业模式下重现,在 online 模式运行时一切正常
在企业管理软件领域特别是 ERP 领域,后台作业常常被用来执行一些费时的批处理工作,比如订单批量处理,报表数据分析和聚合汇总等等。后台作业模式不同于挂接了用户界面的 online 模式,给单步调试也带来了困难。
5. 故障只能在软件正常运行模式时才能重现,单步调试时,软件工作一切正常
当故障出现这种特征时,实际给支持人员传递了一个信号:该故障可能与程序特定的执行时序相关。因为程序正常运行,与处于单步调试模式下运行,执行时序显然不同,比如在调试器单步调试时,可能会破坏多线程程序正常的执行时序。
因为缺少了调试器这一强有力的武器,分析该类故障,需要支持人员具有更强的理论分析能力和问题抽象能力。
由于篇幅限制,本文仅举一个实际例子,对上述第五类故障的分析处理流程做一个分享。
Jerry 曾经负责过 SAP CRM IBASE(Installed Base) 模块的维护工作。IBASE 是一个抽象模型,用于描述已在客户位置安装的资源对象,例如设备、机器,服务或软件。IBASE 模型以树形结构,描述了这些对象的层级结构和它们的各个组件,是服务模块的参考基础。
有一天,我收到一个故障报告,另一个团队的同事,使用我所在团队负责的 IBASE API,在同一会话过程内创建 IBASE 组件,修改,随后删除,然后保存,会遇到运行时错误(Runtime Error).
在故障描述里提到的运行时错误的截图如上图所示。
这位同事发现,这个错误只能在后台作业模式下重现,并且不一定每次都能够重现。该故障也无法在单步调试模式下重现。
并不总是能够重现 != 不能重现。
为了分析这个问题,我得先找到能够稳定重现的办法。因为该故障对单步调试大法免疫,我只能另想他法。
逐字逐句阅读故障报告里的描述,发生故障之前的操作流程为:
(1) 创建 IBASE
(2) 修改 IBASE
(3) 删除 IBASE
(4) 保存事务。
出现运行时错误。
因为我就是 IBASE 模块的负责人,所以我三下五除二就写好了一个不到 200 行的程序,在程序里依次调用 IBASE 的创建,修改和删除 API,再保存事务。
程序源代码如下:
执行这个报表,遇到了期望中的运行时错误。这是一个好兆头,因为我现在找到了稳定重现问题的办法。下一步,我需要缩小问题的范围,找出我这 200 行代码里,到底哪一行代码的执行,引起了运行时错误。
Jerry 喜欢称自己开发的这种专门用于分析故障,重现错误的程序,为“脚手架程序”或者“故障触发器”。
因为这 200 行代码是我自己编写的,所以我可以任意修改。
首先把所有代码全部注释掉,只留下 IBASE 创建 API 的调用。执行程序,一切正常。
再解除 IBASE 修改 API 调用代码的注释,让其参与到程序执行中,一切正常。
再反注释 IBASE 删除 API 调用代码,执行程序,出现了运行时错误!
由此说明,这个运行时错误和 IBASE 删除的场景相关。
回到故障提交报告里的运行时错误截图:第 103 行抛出了一个类型为 X 的错误,因为调用函数 CRM_IBASE_COMP_GET_DETAIL, 并没有读取到通过输入参数 i_date 和 i_time 指定的时间戳对应的 IBASE 数据,因此程序决定通过抛出错误的方式来终止执行。
通过运行时错误的上下文调用栈,我找到了 CRM_IBASE_COMP_GET_DETAIL API 没有返回任何 IBASE 数据的原因:下图第 53 行高亮代码的 CHECK 语句,检查当前传入的时间戳(默认为 IBASE 创建时的时间戳)是否小于待读取 IBASE 抬头的 valto(即 valid to,指 IBASE 有效截止日期的时间戳)字段。如果小于,则顺序执行 CHECK 下一条即 54 行。如果大于或等于,则退出数据读取逻辑所在的循环体。
在后台作业运行模式,以及我的脚手架程序执行时,第 53 行时间戳判断条件没有满足,因此退出了循环,导致 CRM_IBASE_COMP_GET_DETAIL 读取失败,所以引发了故障。
要想满足 53 行的判断条件,只有两种可能:
当前时间戳 > IBASE valto 字段值
当前时间戳 = IBASE valto 字段值
需要强调的是,ABAP 编程语言里的时间戳字段,精确到秒,比如 20211024102424 代表 2021年10月24日10点24分24秒。
虽然我的脚手架应用在单步调试模式下也无法重现故障,但是直接执行可以重现。因此,执行执行脚手架应用,在运行时故障页面点击工具栏的 Debugger 按钮,能弹出调试器,查看应用程序抛出运行时错误的各种信息:
这一回,在调试器里,所有的谜题都揭晓了:当前时间戳 = IBASE valto 字段值,因此导致 API CRM_IBASE_COMP_GET_DETAIL 读取失败,抛出运行时错误。
在调用 IBASE 创建 API 时,会把待创建的 IBASE 抬头的 valfr 字段,赋以系统的当前时间戳。
在调用 IBASE 删除 API 时,会把该待删除 IBASE 抬头的 valto 字段,赋以系统的当前时间戳。
为什么在单步调试模式下,无法重现这个错误呢?我们来看一张简单的时序图。
横轴代表时间戳。t3 代表代码 53 行判断语句里的 -valto 字段值, t1 代表代码 53 行判断语句里的 lv_timestamp 字段值。
在单步调试模式下,假设我们从 IBASE 创建 API 开始依次单步执行,则由于按键手速原因,t3 必定大于 t1.
而在后台作业模式以及脚手架程序正常运行情况下,如果 IBASE 创建,修改和删除的 API 执行得足够快速,能够在一秒钟之内完成,则 t3 与 t1 之差小于 1 秒,故 CHECK 语句执行失败,直接返回。
换言之,这个故障提交时,CRM IBASE API 的开发人员,并没有考虑到 IBASE 的创建和删除会在同一秒之内
完成的场景。毕竟正常情况下,客户不可能在 1 秒钟之内,在 UI 完成 IBASE 先创建再删除的操作。这种场景只可能在客户使用 IBASE API 进行一些二次开发场景下才有可能出现。
当然,最后这个问题,也绝非仅仅是把 53 行 CHECK 语句的 < 符号,改成小于等于操作那么简单。我们仔细评估了改动可能带来的其他副作用,并和提交该故障的团队开发人员进行了讨论,最后采取了其他的方式来避免这个故障的发生。
回到这个故障分析过程本身,最开始接到故障时,因为单步调试无法重现,因此Jerry很是一筹莫展了一阵,后来想到编写脚手架程序来稳定重现该故障,这一步是问题分析的突破口。
有了脚手架程序之后,先注释掉所有的 API 调用,再逐步开放 IBASE 创建,修改和删除的代码,最终把问题范围缩小到 IBASE 删除过程。
通过脚手架应用的直接执行触发的运行时错误,利用调试器查看程序抛出错误时的变量值,将问题锁定到时间戳的处理逻辑上,进而找出根源。
这一分析步骤有点像上世纪末本世纪初电脑 DIY 发烧友们遇到组装机无法启动时的排查措施。当组装机无法启动时,只保留电源,主板和 CPU,尝试启动,如果成功,再逐一添加显卡,硬盘等其他设备。当新添加的设备导致系统重新回到无法启动状态,说明该设备有问题。当时发烧友们把这种方式称为“最小系统法”。
而整个分析过程的重中之重,就是把故障报告中无法稳定重现故障的后台作业里执行的内容,抽象成一个不到 200 行的脚手架程序。
《编程珠玑》第五章曾经分享过一个关于故障调试的有趣故事:IBM 研究中心一位程序员,新安装了一台工作站,发现一个故障:他只能采取坐着的姿势登录系统;一旦站起来,就无法登陆系统了。大家知道这个故障最后怎么定位的吗?去读读原书吧!
希望本文能给大家在企业管理软件领域内的故障排除方法带来一些启发,感谢阅读。
相关阅读
Jerry的反省:程序员不要轻易说出"这个功能技术上无法实现"
记一次 SAP 开发工程师给微软 Azure 报 incident 的体验
更多Jerry的原创文章,尽在:“汪子熙”。
谈谈企业管理软件领域内那些很难稳定重现故障的处理技巧相关推荐
- 从一个 SAP CRM 软件实际的故障处理出发,谈谈企业管理软件领域内那些很难稳定重现故障的处理技巧
目录 企业管理软件领域内棘手故障的一些表现形式 1. 需要复杂的流程才能重现 2. 故障横跨企业管理软件的多个模块 3. 故障只能在客户生产系统重现 4. 故障只能在后台作业模式下重现,在 onlin ...
- Eclipse 不为人所知的另一面 - 企业管理软件领域 ABAP 编程语言开发利器
对于 Java 从业人员来说,无论是刚入门的新手还是专家,肯定都正在或者曾经使用过 Eclipse,这是一款优秀的编写 Java 程序的 IDE. 然而可能大多数 Java 开发人员都没有意识到,Ec ...
- 企业管理软件领域的核心竞争力
我是在企业信息化领域做开发派的工作. 站来开发角度来看,企业信息化领域有两个分支: 一个是对开发和技术要求不高,客户购买管理软件,更多的是想引入更好的更标准的行业最佳实践.软件公司常年扎在一个行业,东 ...
- VUE单页面应用的内页很难被百度收录
这两年自己写了几个WEB项目,前端都是用的vue框架,后台大部分用的nodejs,也有用的spring的:但是他们有一个共同点:都是SPA单页面应用,并且使用HEXO框架在二级目录搭建了一个静态博客. ...
- CS很难发论文?这些技巧你都get了吗?
对于任何一个计算机专业的学生而言,科研的重要性都不言而喻.科研不仅是学业的固定要求,而且是评奖评优.保研出国.未来深造的关键一环.然而对于一个零基础小白来说,自己撰写论文谈何容易? 今天,岛主给大家带 ...
- G1调优很难?记住这些经验技巧~
点击关注公众号,Java干货及时送达 G1 GC是启发式算法,会动态调整年轻代的空间大小.目标也就是为了达到接近预期的暂停时间.G1提供了两种GC模式,Young GC和Mixed GC,两种都是St ...
- 智邦国际怎么样?企业管理软件好不好?
近日,第十三届中国杰出管理者年会--中国管理科学领域极负美誉的管理者盛会,在北京隆重举行.600多位来自全国各地的管理.经济.信息.工业等领域专家学者和杰出管理者齐聚一堂,同议年度管理热点,共话中国管 ...
- 为什么软件开发很难外包
" 很多公司和团队选择把整个软件项目或项目中某些模块或过程(比如测试)整体外包给另一家公司或团队.本文将和你一起来探讨为什么公司或团队有外包的冲动,为什么项目外包问题多和我对外包的建议.&q ...
- 为什么BI软件在中国很难做?
数据智能产业创新服务媒体 --聚焦数智 · 改变商业 前几天讲了SaaS在中国很难做(<企业管理SaaS在欧美很普及在中国却很难做的根因和趋势 先定义一下什么是BI软件,它是指面向业务目的,对已 ...
最新文章
- 细述:nginx http内核模块提供的变量和解释
- 双十一,没有买卖就没有伤害!
- Go range实现原理及性能优化剖析
- java 清空stringbuffer_JAVA中清空StringBuffer变量
- 如何基于对话框的project基于改变BCG的
- JAVA程序打包成exe文件详细图解
- YDOOK: USB 转 TTL 模块 连线使用实例教程
- Delphi2007,XE2 调用GetVersionEx在Win10下为6.1的原因及解决方法
- 菜鸟的数学建模之路(五):Logistic模型
- Interior-point methods(内点法)学习笔记
- 高频直流电源在整改、降压和作用方面解决方案
- 项目管理过程组-十五至尊图
- 回旋共振 有效质量的测量
- 十分钟带你解读什么是Promise异步编程
- 京剧猫主题曲用计算机弹奏,京剧猫电脑版
- 零基础可以学计算机编程吗,计算机编程好学吗,零基础小白可以吗,入门应该先学什么?...
- 谭浩强C++ 第一章(第三版)
- 微信PC端不显示头像和表情怎么解决
- 纯前端实现—网页钟表设计
- InputStream读JSON数据时乱码
热门文章
- Matlab中测程序运行时间
- 2020年9月23日 晴(补2020年9月22日 晴)
- 51nod 1113 矩阵快速幂 模板
- 信息熵、相对熵和交叉熵
- ThinkPHP导出百万条数据量
- Baumer工业相机堡盟相机如何使用CameraExplorer软件查看相机图像相关参数如Binning像素合并、ROI图像剪切、PixelFormat像素格式功能等
- 协同过滤(collaborative filtering)
- ERP系统模块完全解析──主生产计划MPS
- 小说《小城恋情》第三十三章
- .NET Core工程编译事件$(TargetDir)变量为空引发的思考