7 万条短信的乌龙指

乌龙指离我们很远吗?非也。前一段时间 LUG 服务器出故障,就错误地发出了 7 万条短信,把学校短信平台的余额用完了,直到网络中心老师给我打电话才发现。

祸起服务监控脚本。它从数据库里获取站点信息,定时访问被监控的站点,如果发现问题,就会给网站所有者发短信报警。服务监控脚本连接数据库失败时,也会给我发报警短信。早先是不会尝试重连数据库的,因此只会发一次报警,不过监控服务不能在数据库恢复后自动恢复运行。这个 bug 在 blog 故障的时候被发现,于是改成自动重连了,但发报警短信的逻辑没有修改,因此如果一直连不上数据库,就会不停地发。

为防止短信轰炸,发出的短信本来都要经过我这里的“风控”,限制每24小时发往每个手机号的短信数。风控是查询数据库的短信日志表获取最近 24 小时向这个号码发送短信数的,当数据库挂掉的时候,查到的值是 NULL,在 PHP 中隐式类型转换成了0,因此认为没有超过限制,就发出去了。学校短信网关也没有任何“风控”,导致大量的短信涌入运营商网络。

有可能是由于我的手机或者运营商对重复短信的自动屏蔽,我没有收到这些短信,因此没有在第一时间得知这个问题。于是,7 万条短信的 “乌龙指” 就发生了。

这次乌龙指有哪些槽点呢?

  • 自动重连数据库的报警短信在状态改变时发一次就行了,不能在数据库故障期间不停地发。
  • 风控模块在短信日志查询失败时不应该认为已发出 0 条短信,而应该以最坏情况考虑,拒绝此次短信发送请求。
  • 自动重连数据库是吸取 blog 故障的教训临时加的,但修改代码之后没有经过测试,部署到 lug 服务器后首次遇到就造成了乌龙指。
  • 报警不应该只通过短信一种方式,最好是短信+邮件,这样短信出问题了还能用邮件,也能尽早发现这种乌龙指。我没有收到那些重复的短信,不过重复的邮件应该是能收到的。
    我们回顾 8 月 16 日光大 “乌龙指” 事件的槽点:

  • 策略投资部没有纳入风控管理。

  • 订单生成系统中ETF套利模块的“重下”功能 (用于未成交股票的重新申报),设计时错误地将“买入个股函数”写成“买入ETF一篮子股票函数”。
  • 订单执行系统错误地将市价委托订单的股票买入价格默认为“0”,系统对市价委托订单是否超出账户授信额度不能进行正确校验。
  • “重下”功能从未实盘启用,严重的程序错误未被发现。
  • 光大在接到上交所通知后很长一段时间也无法确认问题出在何处。
    一个是自动监控系统,一个是自动交易系统,问题原因如此相似,可见自动系统再给我们带来方便的同时,也埋藏着巨大的风险。这次故障给我敲响了警钟,也希望大家引以为戒。