IT加油站

周五专属Bug:PostgreSQL连接池耗尽的排查与修复

8浏览 3小时前 软件教程 MA123617

原文:https://dev.to/devstackhub/the-bug-that-only-happened-on-fridays-38a7(作者 @devstackhub)

🐛 Bug本身

每个周五下午,我们的结账服务开始出现间歇性500错误。不是所有请求,大约每50个中有1个。也不是每个周五,只是大多数周五,从下午2点后开始,到周一早上就消失了。

错误信息像数据库错误那样毫无帮助:

error: sorry, too many clients already


连接池耗尽。但我们配置的连接池是20个连接;在普通的周五下午,我们可能只需要6个,而且我们的指标中没有显示任何流量高峰。消耗连接的不是客户。

🔍 理论1:慢查询持有了连接

这是显而易见的第一个猜测。某个查询耗时太长,一直持有一个连接,最终20个连接都堆满了。

我们在下次事件发生时检查了 pg_stat_activity。没有异常。没有长时间运行的查询,没有锁,没有阻塞的事务。每个连接都是 idle(空闲),而不是 idle in transaction(事务中空闲),也不是 active(活跃)。二十个完全空闲的连接,但PostgreSQL拒绝分配第二十一个。

理论被否定 ❌。如果它们是空闲的,连接池应该重用它们。

🕵️ 理论2:我们自己的代码中有连接泄漏

下一个嫌疑人:在应用程序某处,我们打开了一个连接但忘记释放回连接池。一个经典的泄漏。几周前我们发布了一个新的报告生成端点,它在常规ORM包装器之外运行了一些原始查询,这是一个很容易忘记调用 .release() 的地方。

我们逐行审计了它。每个查询都通过一个 try/finally 释放了客户端。我们在每个 pool.connect()client.release() 调用周围添加了日志,将其发布到预发布环境,并用脚本对报告端点进行了一小时的轰炸。连接按预期打开和关闭。没有泄漏。

理论被否定 ❌。到这个时候,我相当确定我们被诅咒了。

📅 理论3:某个外部任务在冲击数据库

我们扩大了搜索范围。Cron任务?某个两年前设置并忘记的周五定时报告?我们在所有仓库中搜索了 cronschedulesetInterval,发现一个每周分析导出任务在周五下午1点运行。肯定是这个原因。

但该导出使用自己专用的数据库用户,当我们再次检查 pg_stat_activity 时,所有20个空闲连接属于我们的结账服务用户,而不是分析任务。

理论被否定 ❌。又一次。

✅ 实际修复

我们一直忽略的细节:连接是空闲的,但PostgreSQL仍然不重用它们。这不是泄漏。这是一个连接池认为其连接很忙,而数据库知道它们不忙的情况。

我们在负载均衡器后面运行了两个结账服务实例,每个实例都有自己的连接池,上限为20个。没问题,总共最多40个连接,远低于PostgreSQL的100个限制。但几个月前,我们为金丝雀部署实验设置了一个第三个旧实例,并且从未完全停用它。它没有接收实时流量,所以从未出现在我们的请求指标或错误仪表板中。

但它仍在运行,并且它在启动时仍然打开其全部20个数据库连接。真正的凶手是:它的健康检查探针中有一个bug,每隔几秒就打开一个新的原始连接,而不是从连接池中重用一个,并且只在进程重启时清理它们。该进程每周五重启,因为自动缩放策略每周回收空闲实例。泄漏每周一重置自己,这正是为什么我们在周末后永远抓不到它,以及为什么它总是在周五下午回来。

花了两周理论推导的“修复”,实际只花了五分钟应用。我们停用了这个僵尸实例。连接数从周五高峰的94个下降到稳定的12个,500错误再也没有出现。

💡 真正有帮助的

  • application_nameusename 分组查看 pg_stat_activity,而不仅仅是计数。我们假设所有20个空闲连接都是“我们的”,因为它们是空闲的,而不是因为我们检查了所有可能连接的服务的所有权,包括我们已经忘记存在的服务。
  • 问“还有什么在运行”而不是“这段代码有什么问题”。我们尝试的每个理论都假设bug在我们盯着的请求路径中。它不在那里。它存在于我们几个月没考虑的基础设施中。
  • 每周模式是真正的线索,而不是红鲱鱼。我们将“发生在周五”视为一个通用bug的烦人细节。它是整个故事,一直在尖叫“某件每周周期发生的事情”,直到理论3迫使我们去寻找它。

bug不在我们那周编写的代码中。它在一个我们已经完全停止思考的服务中,事后看来,这通常是真正bug隐藏的地方。

原文:https://dev.to/devstackhub/the-bug-that-only-happened-on-fridays-38a7(作者 @devstackhub)

#PostgreSQL #连接池 #bug排查 #数据库 #周期性错误