TT Lab
开始
学习 学习路径 课程

集成与部署

我们的日志全是成功,对方却少了十二笔

在 TT Lab 中继续学习

目标

处理批量发送的请求中只有几笔失败的情形。把逐笔结果记录在我们这边,区分出要重发的失败和要修正的失败,只重发失败的部分,最后制作核对发送方和接收方的笔数与合计的对账表。

为什么重要

批量发送 API 会在对一个请求返回 200 的同时,把逐笔结果放在正文里。从 HTTP 的角度,200 是正确的——2xx 表示收到并接受了请求,而不是请求中的所有条目都成功了。 所以调用的一方如果只看状态码,失败的几笔就会在哪里都没有留下记录。没有错误,也没有告警,积累了好几天,在与对方的数字核对时才会被发现。 修复的顺序有四步。逐笔记录,把失败分成两类,只重发失败的部分,核对两边的数量。尤其是第三点很重要——如果把整批全部重发,已经进去的那些笔就会进去两次。重试的单位不是批,而是笔。 评分器不会相信你写的句子。评分器会把合作方服务器重新启动在评分器所选的端口上,让你的发件箱和发送器对着评分器的数据库实际运行,并与对方账本核对。

步骤

  1. 创建 /root/recon/receiver.py 并在端口 8010 上启动,用 /root/recon/gen_outbox.py 在 /root/recon/outbox.db 中生成 120 笔待发送的数据。
  2. 用 /root/recon/send.py 的 --naive 只看状态码发送,然后把我们的记录与对方账本之间的差异写入 /root/recon/naive.json。
  3. 让 send.py 读取逐笔结果,以 status·reason·attempts 记录到 sent 表中。
  4. 创建 /root/recon/classify.py,把失败原因分为要重发的和要修正的。
  5. 让 send.py 的 --retry 只挑选可以重发的笔进行重发。
  6. 用 /root/recon/recon.py 核对发送方和接收方,生成 /root/recon/recon_result.json。
  7. 把剩余笔的处理计划写入 /root/recon/unresolved.json。
  8. 在 /root/recon/recon_report.md 中分四节进行汇报。

参考

接收批量发送的合作方与 120 笔待发送数据

创建 /root/recon/receiver.py 并在端口 8010 上启动,再创建并运行 /root/recon/gen_outbox.py,在 /root/recon/outbox.db 中放入 120 笔数据。其中必须混有 5 笔账户错误的、3 笔金额为 0 的、4 笔金额为 97 的倍数的。

合作方对请求本身总是返回 200,并把失败放在正文的 results 中。拒绝的判定按金额、账户、重复、暂时挂起的顺序来看。发件箱要把待发送的和发送结果分成两张表——混在一张表里,就无法区分“从未发送过的”和“发送后失败的”。

只看状态码会漏掉什么

在 /root/recon/send.py 中创建 --naive,让它只看状态码,把全部都算作成功。把这个结果与对方账本的差异,以 batches·http_ok·assumed_sent·partner_rows·gap 写入 /root/recon/naive.json。

请求确实成功了。失败在正文里。我们的记录是 120 笔成功,而数一数对方账本中有多少行,差异就会原原本本地显现出来。这一步中不在 sent 表里写入任何内容。

把逐笔结果留在我们这边

让 send.py 读取响应正文中的 results,对每一笔在 sent 表中留下 status·reason·attempts。成功和失败都必须记录。第二次运行不应再次发送已经发送过的笔。

如果只写失败的,就无法区分“从未发送过的”和“发送后成功的”。最好不要假定响应中 results 的顺序与请求顺序相同,而是用 item_id 对应着读取。尚未发送的笔,就是在 outbox 中有而在 sent 中没有的笔。

要重发的与要修正的

创建 /root/recon/classify.py,让它把通过 --reason 接收的原因判定为 {"retryable": ..., "action": ...}。action 有 requeue·fix_data·ignore 三种,对于未知的原因,按不重发的一侧来应答。

暂时挂起的,原样重发就能解决。未知账户和错误金额,无论发送多少次都会得到同样的答复,所以必须由人来修正数据。已经进入对方账本的笔,既没有要重发的,也没有要修正的。如果没有这个分类,无法修正的那些笔就会在队列里永远打转。

不是按批,而是按笔重发

让 send.py 的 --retry 在先前被拒绝的笔中,只挑选可以重发的进行重发,并把结果更新到 sent 中。已经成功的笔绝对不能再次发送。

如果把整批全部重发,已经进去的那些笔就会进去两次。对方如果能防止重复,那是万幸,如果不能,就是我们造成了事故。请调用分类器,只挑出 retryable 的,并且要把 attempts 加上去,之后才能知道这是第几次尝试。

核对发送方和接收方

用 /root/recon/recon.py 对照发件箱、我们的记录和对方账本,生成 /root/recon/recon_result.json。不仅要看笔数,还要看合计以及双方独有的笔,只有全部对得上时,balanced 才是 true。

只核对笔数,就会漏掉“少了一笔、另一笔进去了两次”的情况。把我们记为成功的 id 集合与对方账本的 id 集合互相做减法,立刻就能看出哪些笔只在某一边。也要同时统计是否还有尚未发送的笔。

剩下的笔必须有负责人

在 /root/recon/unresolved.json 中,以 total·by_action·items(item_id·reason·action·owner)写出尚未解决的笔。action 必须与分类器的答复一致,owner 不能留空。

如果只列出“需修正 8 笔”,却不写谁来修正,这 8 笔就会永远留在那里。对账表的最后一栏是人或团队的名称。items 直接取出 sent 表中以被拒绝状态留下的那些笔来制作就可以。

对账报告

在 /root/recon/recon_report.md 中分为 ## 무엇을 놓치고 있었나 ## 건별 결과를 읽고 나서 ## 다시 보낼 것과 고칠 것 ## 대사표와 남은 것 四节来写(韩文,依次意为“漏掉了什么”“读取逐笔结果之后”“要重发的与要修正的”“对账表与剩余项”)。naive.json 和 recon_result.json 中的数字必须写进正文。

读者是那个说“我们的日志里全是成功啊”的人。请先说明,那份日志并没有说谎,而是只看了请求的状态码。然后再展示各原因的笔数和对账表中的两个数字即可。