生产环境一个订单状态错误的排查与反思

  --问题现象--

  生产环境上发现一个奇怪的现象:如果充电用户充入充电金额,金额在充电过程中耗尽,那么订单的状态是A(退款过程中);而如果用户充入充电金额,金额没有在充电过程中耗尽,那么订单的状态是B(订单关闭)。

  这里简单描述一下这个订单状态变化的过程:

  1.用户结束订单时,硬件返回状态,包含耗电量,订单id等;

  2.系统根据用户的计算结果,判断订单时直接关闭(状态B),还是发起退款(状态A);

  3.发起退款时进行计算,并调用支付模块进行退款

  4.退款回调,处理完毕后,订单被置为关闭(状态B)。

-------------------------------------------------------------------------------------------------------------

  --解决过程--

  在现象中,整个退款发起,回调,而后订单被置为关闭的链路应该是正常的。问题可以归咎在金额耗尽,应该设置为(状态B)但是结果却是(状态A)的情况。

  没有什么头绪,去DB中查询到了99%的金额耗尽的订单都被置为了A(退款中),所以很确定这个过程一定是发生在系统结束订单的过程中。于是在日志中寻找了整个结束订单的日志,但没什么思路,日志里的信息并没有什么帮助。

  怀疑订单已经正常的结束,被打上了状态B,但是由于某种原因,很快又被更新成了状态A。

  于是找到系统里,所有更新成状态A的代码位置,打上标记,发布到服务器,看看是否能够印证上述怀疑。但是整个过程中没有发现这个状态本身的更新有什么问题。但在过程中发现了一个细节,订单被设置为退款中A的状态之后,退款却没有真正执行。

  于是想到,是否订单在进入退款计算环节之前和进入之后的计算结果不一致?是不是计算环节有问题?于是将两次计算的结果以及订单的所有字段全部打印出来。再次发到服务器上,并在服务器上查看日志。

  最终发现:在第2步计算的结果始终大于0,所以所有的订单全部都是转换为了状态A,但是进入第3步,校验计算的结果时,发现其实只有部分订单真正地应该退款,所以金额耗尽的那一部分订单就在此停止了,并未回滚,造成订单就此结束,状态错误。为什么2和3结果会错误呢?原因是在2计算完成后,根据硬件返回的状态对订单的字段进行了部分更新,导致2和3的计算依据并不一致。

 -------------------------------------------------------------------------------------------------------------

   --经验总结--

  虽然这个错误看着上文的分析好像很简单,但是在解决的过程中却不是那么显而易见的。所以这里仔细回想整个解决过程,反思为什么会造成这么坎坷的解决过程,争取总结经验,让未来避免或者及时发现这个问题。

  1.最大的问题,还是在于,发起退款函数中的计算校验,在计算出不应该退款时没有返回异常。如果这里返回了异常,回滚了整个过程,那么应该在测试阶段就能够及时发现这个bug,不会拖延到生产环境。要知道异常抛出导致整个订单结束的失败,是很容易让测试人员发现,但这个过程如果错误的完成了,造成数据的不匹配,想发现这个问题其实对测试人员业务水平的考验更大。

  2.函数尽量做到一个函数做一件事,当然这会花费更长的时间,所以怎么保证开发效率的同时也向这个目标靠近是一个平衡术。但这个bug的产生过程中,不同的函数中发生了多次对于订单参数的更新,并进行了save操作,这是不正常的,在编码过程中要避免这种一个连贯的逻辑中多次更新的操作。这不仅降低了代码效率,也容易发生意外情况。在代码的封装过程中,辨别这种情况,并保证一个流程只进行一次实体对象的save操作。

原文地址:https://www.cnblogs.com/bruceChan0018/p/14949200.html