《架构师》2019年11月
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

战争故事

一边是调整参数与查看统计数据,另一边是修复底层问题根源。

如果没有战争故事(war story,指一段令人难忘的经历,往往涉及危险、困难或者冒险因素),这篇文章又怎么会完整?我很喜欢回顾这类经历,分享环节马上开始。

这是个关于搜索与SQLAlchemy的故事。在BNEF,我们需要处理大量由分析师们撰写的研究报告。每当报告发布时,我们都会收到一条消息;在收到消息之后,我们会通过SQLAlchemy进入数据库,获取我们需要的全部信息,进行转换,并将结果发送至solr实例进行索引。但这时候,我们发现了奇怪的AF bug。

每天早上,连接数据库的操作都会失败,消息提示“MYSQL服务器不存在”。有时候连下午都会出现这种状况。由于下午时段的使用量最大,所以我首先进行了一番检查。没问题,机器的运行状态一切正常。我们全天会向数据库发出数千次请求,都没有失败。那么,为什么负载强度这么低的情况反而会出问题呢?

哦,可能是我们在事务结束后没有关闭会话?所以失败其实来自同一段会话,只不过下一个请求出现在很长一段时间之后,这就引发了超时——因为此次服务器已经关闭了。快速查看代码,我们通过上下文管理器检查了每一次在__exit__()上调用session.close()的读取操作。

经过一整天的排查,没发现任何问题。在第二天早上,我又遇到了同样的情况。错误发生的一秒之后,其他三项索引请求都成功了。这明显就是会话未能正确关闭的典型表现。好了,相信大家能够脑补出接下来的完整故事。

SQLAlchemy mysql语言中的Session.close()无法关闭底层数据库连接,除非使用NullPool。是的,这就是修复方案。

引发这个bug的原因很简单,这是因为我们不会在夜间以及午餐时段发布研究报告。此外,我们也吸取到另一个教训——大多数堆栈溢出问题的答案(我是从谷歌上查来的),正是bug本身会调整会话的超时时间,或者控制每条SQL语句所能发送数据量的参数。这些对我来说都没有意义,因为它们与问题的根源无关。我检查了查询大小是否在限制范围之内,而且由于会话本身正在关闭,所以也不会发生超时状况。

我们当然可以把超时时间从1个小时增加到8个小时来快速“修复”这个bug。但这显然解决不了问题,到第二天早上,又会有研究报告引发的错误出现在我们面前。

一边是调整参数与查看统计数据,另一边是修复底层问题根源。这就是我们的日常生活。