zoukankan      html  css  js  c++  java
  • 一场小型产品发布事故是如何酿成的

    问题描述

    早上一到公司,接到运维部门同事报的一个高优先级问题工单,描述如下:

    “报销单号关联单子的审批记录没有了,但财务已做帐,导致相关审批人员需要重新审批。”

    在正规大型互联网,数据丢失可算是严重级别为最高级0的问题了吧。

    分析原因

    这种问题从产品上线6个月前到现在都没出现过,突然出现了,肯定是什么原因导致的。
    事出一定有原因,找出原因是重中之重。数据丢失铁定是由于在代码中有删除数据的行为。
    于是优先锁定所有有删除操作的地方,由于整个项目仓储层是Dapper来实现的,所以通过表关键词全局搜索,根据肉眼逐条排查包含Delete的语句的方法,记录下来作为嫌疑对象。于是从200多个搜索结果中,得到一组7个方法的嫌疑清单,接下来分两手行动。

    • 主线1.日志中查找出蛛丝马迹。看是否是在某种特殊情况下, 用户非常规操作造成。由于系统有记录所有操作的日志功能,但数据量无疑是巨大的,可以以时间为主线,根据单号,主键,嫌疑方法等关键词来过滤记录。
    • 主线2. 逐个代码审查删除逻辑前后上下文的相关代码,看是否存在漏洞造成误删的情况。

    经过几小时的排查,得到的结果令人沮丧。

    • 关于日志,排查中发现日志有一些缺失,有一些行为数据并没有记录下来,而且记录的数据有一些是不准确的,比如方法执行时间居然30000毫秒,实际上只有几时毫秒。没能从中找到关键信息。
    • 关于嫌疑清单,7处含删除逻辑的方法中均有限制条件,所有行为没有异常情况。

    于是得出一下疑点:
    疑点1. 日志为什么有错误?
    疑点2. 误删逻辑并不存在于这7处,那问题会在哪里?

    先说说疑点2的分支解决方案:
    分支1. 由于这次报的错误在三个月前均没有发现,在上周五发布的时候就出现了这个问题,所以可以怀疑是否是在这次发布造成的影响。根据错误数据产生的时间,推测位于某个发布节点的前后。
    分支2. 查看数据库系统的日志功能,操作系统日志,是否有记录删除行为。
    分支3. 咨询产品主要开发人员,程序功能上是否存在别处的删除逻辑行为。
    分支4. 嫌疑列表范围不够,要扩大排查范围,逐个逐条排查关键词相关的位置,大约200个地方。
    分支5. 向用户收集相关操作线索(操作行为较复杂),根据用户填写的数据,模拟用户的操作过程,尝试重现错误的发生。
    分支6. 是否存在人为操作数据的情况,运维人员常常会帮用户直接修改数据

    上述解决方案涉及到角色包含DBA,开发,测试,运维,项目经理,客户,用户。 软件工程中的含,客户管理,项目管理,代码审查,版本管理,UAT测试,发布规范,系统架构等。
    这在大型项目中一定是一个大事件,幸运的事这个项目不大,实际使用用户才2000人。

    正如你们期待的,分支1~6 均未找到Root Cause。

    • 小公司的项目,追求速度,发布不规范,所以没有记录任何发布日志,在上线前也没有系统的UAT测试,发布频次高,修复速度快,有时候甚至一天发布2~3次,更改正式库数据操作也比较频繁。
    • 系统和数据库系统自带的日志功能并没有精细化记录所有数据的操作,只有一些关键数据,所以参考意义不大。
    • 产品开发人员只对自己做的功能有认知,对出现这个问题原因,他们也表示不理解
    • 所有相关信息筛查过,均为发现问题,所以才导致往下的排查 素手无策
    • 用户对产品没有开发人员熟悉,自己的操作也是界面上能看到的操作,所有操作都是正常 合理的,理论上不存在非常规行为。
    • 理论上不存在,在企业内部要但法律责任,开发,测试,运维人员没有动机。
    • 模拟用户操作
      所以,各方面压力就来了,客户的领导直接找到我们大Boss,大Boss来询问具体情况,我们也如上述一一交代,尚未有可行解决方案。

    妥协解决方案

    数据丢失,一定是某个地方让其丢失,正如我们学的哲学,这是事物的,不以人的意志为转移的。

    我们要做的仅仅是为了找出这个地方,那么除了监控所有行为,产生更精细化的日志,追踪用户所有操作,别无他法。
    所以我给的方案是在所有删除逻辑前后插入审计日志, 后续再次发现这个问题时 在跟踪查询。

    另一个日志审计记录不全的问题,是由于在框架底层设计时某次不经意将局部使用对象改为了全局静态单例对象,导致数据和时间记录的异常。
    这属于低级错误,不再赘述。 我们在改底层架构时一定要谨慎,谨慎,再谨慎。

    转折点

    很多事情很难解释,往往在你一筹莫展的时候,突然问题就自然而然解决了。

    在第二天的UAT环境,不断的模拟测试用户操作的数据,测试日志,不经意间被一个开发人员看到。她指出“为什么审批历史记录中,每一项后跟着一个撤回按钮,不是测试才会有么”。
    瞬间理解了,为什么审批的记录信息一次性丢失, 90%以上找到了问题点。
    于是去审计日志表查询撤回共计74条。排重后约30多条记录被撤销影响。
    解释一下:
    这个测试功能是给测试人员和UAT人员使用的功能,我们产品被多家企业使用,在产品上除企业特殊业务功能外的其它功能都是一样的。
    所以会存在产品升级引发业务相关问题,这个问题是其中之一,只不过比较严重罢了。

    之所以我没有意识到这个问题是由于在平常工作时主要时间花费在实现核心功能,项目管理,程序架构上,反而忽略了对团队的代码审查
    最终找到影响点:
    后台代码完全不可能通过表的关键词找到,因为它调用了存储过程... 万万没想到。
    产品开启前,我们就规定不使用存储过程,以确保各个数据库的兼容性,便捷性。没想到这里有一处调用了存储过程,原因还是由于没有审查代码漏过了。

    教训

    1. 不能因为发布频繁,就不做日志管理,版本管理,可以不像大公司有一个专门的配置管理人员,我们可以在关键点上做到即可。例如,写发布日志,只需记录关键点(时间/版本/功能点描述)
    2. 不能因为发布多就不做测试,至少要做到发布后各个功能的抽样检查,以最小的代价来确保功能不受影响。
    3. 后台鉴权代码非常重要,尤其是增删改行为,一定要有足够的授权验证和方法执行条件检查,假如后台有权限验证,即使前台功能放开了,后台也无法正常执行,那么这个事故也不可能发生。这在企业内部系统中往往是够用就行,安全性方面太欠考虑了。
    4. 代码审查必须要做,每天花少量时间过一下昨天的所有签入,这样在全局上就能意识到这里有这个功能,出现问题也可以第一时间联想到。
    5. 日志功能要稳定齐全,否则缺少部分日志数据,对问题调查会带来麻烦。
    6. 产品升级前的测试,还是务必要做的。
    7. 改底层架构时,要足够充分的测试。

    结束语

    创业就像爬山系了根绳子,中间任何一处断了,就挂了。成功的条件是所有条件都需要满足,是充分不是必要。
    这次的事故也是如此,所有的因素加一起(6个分支),才导致最终的发生。
    虽然道理,我们都懂。然而身处不同的环境,面对的情况不同,事情不是由我们来决定的。
    小公司看似不成章法,但是所做的事都是切实的解决了企业的各种业务问题,及时的,高效的。
    重要的是挑关键的点,抓住就好,也不可能用大公司的那套来适应小公司。

  • 相关阅读:
    在VS Code中配置GO开发环境并调试
    go文件操作实践[读写zip tar xlsx文件]
    go 文件操作实践[读写json xlm gob txt]
    go inject 实践
    go的反射reflect
    go goroutine channel 和C# Task BlockingCollection 以及python该如何实现
    beego redis作为缓存
    beego Session redis存储以及是否阻塞
    Beego generate自动生成代码 以及注解路由 @router
    bee go用base64Captcha生成base64的验证码
  • 原文地址:https://www.cnblogs.com/mcxie/p/6230514.html
Copyright © 2011-2022 走看看