← All incidents

[Eval][v1][zh] Payment service connection pool exhaustion after batch job deploy

service: payment-svccreated: 5/25/2026, 10:19:49 PM

Raw incident context

Time: 14:02 UTC. payment-svc p99 latency jumped from 120ms to 4.8s within ~3 minutes.
Error rate climbed from 0.1% to 12% (mostly HTTP 500).
Application logs (last 5min): repeated "FATAL: sorry, too many clients already" and "connection refused"
from payment-svc → postgres-primary.

Postgres metrics:
- CPU: 35% (normal)
- active_connections: 500 / 500 (max_connections)
- waiting_queries: 87
- slow_query_log shows a new query running every 30s: SELECT * FROM ledger_entries WHERE status='pending'
  (no index on status; full table scan over ~12M rows, ~2.4s per execution)

Deploy history:
- 13:50 UTC — payment-svc v2.41 shipped. Changelog mentions "added nightly settlement batch (cron: */30 * * * *)"
- No infra/DB changes in past 7 days.

On-call notes (Slack):
- 14:03 — CS reports failed checkout volume rising
- 14:04 — Pager: PaymentSvcErrorRateHigh
- 14:06 — "DB looks healthy, CPU is fine" (initial mis-diagnosis)

Summary

SEV1

支付服务 payment-svc 在 14:02 UTC 出现 p99 延迟从 120ms 飙升到 4.8s,错误率从 0.1% 升至 12%(HTTP 500),客户报告结账失败。根因是 v2.41 部署引入的定时任务(每30秒全表扫描 ledger_entries 表)耗尽了 PostgreSQL 连接池(500/500),导致正常查询被拒绝。

Severity reasoning: 用户可见的结账失败(12% 错误率),直接影响收入,且连接池耗尽导致服务不可用,符合 SEV1 定义。

deepseek-chat·prompt v1·output: zh·10461ms

Root cause hypotheses

  • highv2.41 新增的定时任务(每30秒执行 SELECT * FROM ledger_entries WHERE status='pending')因缺少索引导致全表扫描,每次执行约2.4秒,占用连接并阻塞其他查询。

    Evidence: 慢查询日志显示该查询每30秒运行一次,耗时2.4秒;active_connections 达到 max_connections 500;应用日志报 'too many clients already'。

  • mediumv2.41 部署导致应用连接池配置错误,例如连接泄漏或最大连接数设置过高。

    Evidence: 部署时间与问题出现时间吻合(13:50 部署,14:02 问题),但无直接日志证据。

  • low外部流量突增导致正常查询量超过数据库处理能力。

    Evidence: 无流量突增报告,且 CPU 仅 35%,说明非计算瓶颈。

Investigation checklist

  1. 确认 PostgreSQL 连接池是否已满,并查看当前活跃查询。
    psql -h postgres-primary -U postgres -c "SELECT count(*) FROM pg_stat_activity WHERE state = 'active';"

    Expected: 返回 500 或接近 500,确认连接耗尽。

  2. 查看慢查询日志,确认全表扫描查询及其频率。
    psql -h postgres-primary -U postgres -c "SELECT query, calls, total_time, rows FROM pg_stat_statements ORDER BY total_time DESC LIMIT 5;"

    Expected: 看到 SELECT * FROM ledger_entries WHERE status='pending' 总耗时最高,且 calls 与部署时间匹配。

  3. 检查 payment-svc 部署的变更日志,确认定时任务配置。
    kubectl get deployment payment-svc -o jsonpath='{.spec.template.spec.containers[0].env}' | grep -i cron

    Expected: 显示 CRON_SCHEDULE 或类似环境变量,值为 */30 * * * *。

  4. 验证 ledger_entries 表上 status 字段是否有索引。
    psql -h postgres-primary -U postgres -c "\d ledger_entries" | grep status

    Expected: 如果无索引,则输出中 status 列没有索引标记。

Mitigation plan

  • 立即禁用定时任务:通过配置中心或环境变量关闭 cron 调度,或直接删除 cron job 定义。

    Risk: 定时任务停止后,结算批处理可能延迟,但无数据丢失风险。

    Rollback: 重新启用定时任务:恢复配置或重新部署旧版本。

  • 临时增加 PostgreSQL max_connections 到 1000,并重启数据库以释放当前连接。

    Risk: 增加连接数可能导致内存压力,但 CPU 仅 35%,风险可控。重启会导致短暂中断。

    Rollback: 将 max_connections 改回 500 并重启。

  • 为 ledger_entries.status 创建索引以优化查询。

    Risk: 创建索引期间可能锁表,影响写入。建议在维护窗口或使用 CONCURRENTLY。

    Rollback: 删除索引:DROP INDEX IF EXISTS idx_ledger_entries_status;

Customer impact

约12%的结账请求失败,用户看到错误页面或支付超时。影响持续至连接池恢复。预计在禁用定时任务并创建索引后5分钟内恢复。

Postmortem draft

Postmortem: payment-svc 高延迟与错误率

摘要

[2-3句话概述]

时间线

  • 13:50 UTC: payment-svc v2.41 部署,引入定时任务
  • 14:02 UTC: p99 延迟飙升,错误率上升
  • 14:04 UTC: Pager 触发
  • [后续缓解时间]

影响

  • 用户影响:12% 结账失败
  • 持续时间:X 分钟
  • 收入影响:估算中

根因

v2.41 新增的定时任务全表扫描 ledger_entries 表,耗尽数据库连接池。

做得好的

  • 监控告警及时
  • 慢查询日志帮助定位

做得不好的

  • 代码审查未发现缺少索引
  • 定时任务未进行性能测试

行动项

  • [ ] 为 ledger_entries.status 添加索引(P0)
  • [ ] 添加连接池监控告警(P1)
  • [ ] 代码审查增加 SQL 性能检查(P2)

Follow-ups

  • P0为 ledger_entries.status 创建索引,并验证查询性能。数据库管理员
  • P1添加 PostgreSQL 连接池使用率告警(>80%)。平台团队
  • P1审查 v2.41 代码,确保定时任务有合理的并发控制和超时。服务负责人
  • P2在 CI/CD 流水线中添加 SQL 性能检查(如 EXPLAIN)。平台团队