fix(db): add max_lifetime to prevent zombie connections causing CONNECT_TIMEOUT#1007
fix(db): add max_lifetime to prevent zombie connections causing CONNECT_TIMEOUT#1007NieiR wants to merge 4 commits intoding113:devfrom
Conversation
📝 WalkthroughWalkthrough在 Docker 构建忽略列表中新增 Changes
Estimated code review effort🎯 3 (中等复杂度) | ⏱️ ~20 分钟 Possibly related PRs
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Add max_lifetime: 30 minutes to force connection recreation, preventing 'write CONNECT_TIMEOUT' errors caused by silently disconnected TCP sockets in the connection pool. Also disable prepared statements to avoid pool reuse issues. Fixes #ISSUE
84f6887 to
e0c0806
Compare
There was a problem hiding this comment.
Code Review
This pull request adds .worktrees to the .dockerignore file and updates the database connection pool configuration in src/drizzle/db.ts to prevent zombie connections by setting a maximum lifetime and disabling prepared statements. Feedback suggests making these new parameters configurable through environment variables to ensure consistency with existing settings and to prevent potential logic conflicts with the idle timeout schema.
There was a problem hiding this comment.
Code Review Summary
This PR addresses a legitimate production issue with zombie database connections. The fix is targeted but introduces minor inconsistencies with project conventions.
PR Size: XS
- Lines changed: 9
- Files changed: 2
Issues Found
| Category | Critical | High | Medium | Low |
|---|---|---|---|---|
| Logic/Bugs | 0 | 0 | 0 | 0 |
| Security | 0 | 0 | 0 | 0 |
| Error Handling | 0 | 0 | 0 | 0 |
| Types | 0 | 0 | 0 | 0 |
| Comments/Docs | 0 | 0 | 0 | 0 |
| Tests | 0 | 0 | 1 | 0 |
| Standards | 0 | 0 | 1 | 0 |
High Priority Issues (Should Fix)
1. [TEST-INCOMPLETE] New connection pool options not verified (Confidence: 85)
File: tests/unit/drizzle/db-pool-config.test.ts
The new max_lifetime and prepare options are not asserted in existing tests. Update test assertions:
expect(postgresMock).toHaveBeenCalledWith(
process.env.DSN,
expect.objectContaining({
max: 20,
idle_timeout: 20,
connect_timeout: 10,
max_lifetime: 1800, // Add
prepare: false, // Add
})
);2. [STANDARD-VIOLATION] Hardcoded value breaks env pattern (Confidence: 90)
File: src/drizzle/db.ts:31, src/lib/config/env.schema.ts
All other pool settings support env overrides. The hardcoded max_lifetime: 30 * 60 is inconsistent. Consider adding DB_POOL_MAX_LIFETIME env variable:
// env.schema.ts
DB_POOL_MAX_LIFETIME: optionalNumber(z.number().int().min(60).max(7200)),
// db.ts
max_lifetime: env.DB_POOL_MAX_LIFETIME ?? 1800,Review Coverage
- Logic and correctness - Fix addresses zombie connections appropriately
- Security (OWASP Top 10) - No security issues identified
- Error handling - No changes to error handling patterns
- Type safety - No new types introduced
- Documentation accuracy - Comments explain the fix well
- Test coverage - Gap in test assertions for new options
- Code clarity - Clear intent and commenting
Automated review by Claude AI
Add max_lifetime: 30 minutes to force connection recreation, preventing 'write CONNECT_TIMEOUT' errors caused by silently disconnected TCP sockets in the connection pool. Also disable prepared statements to avoid pool reuse issues. Fixes #ISSUE
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/drizzle/db.ts`:
- Line 30: The hardcoded prepare: false in the DB connection config should be
controlled by an env var; change the connection option (the prepare setting used
when creating the Postgres.js/Drizzle client) to read from an environment
variable like DB_PREPARE_STATEMENTS (default true), so prepared statements
remain enabled unless DB_PREPARE_STATEMENTS is explicitly set to "false" for
special PgBouncer/transaction-mode cases; update the inline comment around the
prepare key to document that disabling prepared statements is a
PgBouncer-specific workaround (e.g., for old PgBouncer transaction mode) and not
the default best practice.
- Around line 25-29: The patch fixes a problem where the Postgres client options
hard-code max_lifetime to 30 minutes which defeats the library's intended
randomized connection TTL; update the postgres(...) options in the client
initialization (the const client creation) to stop forcing max_lifetime to a
fixed value—either remove the max_lifetime entry to preserve the library's
default randomized TTL behavior or make it configurable via an environment
variable (e.g. env.DB_MAX_LIFETIME) with clear documentation and a sensible
default that only overrides the library if explicitly set; ensure references to
max_lifetime in the options are updated and document the change in comments
where the client is created.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 9ff80ca2-c858-4e55-a3a6-c23950cc1201
📒 Files selected for processing (2)
.dockerignoresrc/drizzle/db.ts
|
Tip: Greploop — Automatically fix all review issues by running Use the Greptile plugin for Claude Code to query reviews, search comments, and manage custom context directly from your terminal. |
问题描述
在高并发或长时间运行的 Docker 环境中,postgres.js 连接池中的连接可能被网络层(Docker bridge/防火墙)静默断开,但应用层仍认为连接有效。当复用这些"僵尸连接"时,会导致 write CONNECT_TIMEOUT 错误,表现为:
根因分析
postgres.js 默认 max_lifetime = 0(连接永不重建)。Docker 网络对空闲 TCP 连接有隐形超时(通常 10-15 分钟),当连接被静默断开后,应用层 socket 仍显示"已连接"。查询时 write() 到已死 socket,触发 10 秒 connect_timeout 后失败。
修复方案
添加 max_lifetime: 30 * 60(30分钟),强制连接定期重建,避免超过 Docker 网络隐形超时。
验证
检查清单
Greptile Summary
This PR fixes zombie connections in Docker environments by adding
max_lifetime: 1800to the postgres.js pool config, ensuring connections are recycled every 30 minutes before Docker's network layer silently kills them. It also addsprepare: falseto disable prepared statements. Tests are updated to assert the new config values.Confidence Score: 5/5
Safe to merge — the core fix is correct and well-tested; previously raised P2 concerns have been discussed in prior threads
No new P0 or P1 findings. The max_lifetime fix directly addresses the described zombie-connection root cause, tests cover all three pool config scenarios, and the .dockerignore change is harmless. Prior thread concerns are P2 level and have already been surfaced to the author.
No files require special attention
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD A[Incoming Query] --> B{Pool has idle connection?} B -- Yes --> C{Connection age > max_lifetime 1800s?} C -- No --> D[Reuse connection] C -- Yes --> E[Gracefully close & create new connection] E --> D B -- No --> F{Pool size < max?} F -- Yes --> G[Create new connection] G --> D F -- No --> H[Wait in queue] H --> D D --> I{Connection idle > idle_timeout 20s?} I -- Yes --> J[Close connection, remove from pool] I -- No --> K[Return to pool]Reviews (2): Last reviewed commit: "chore: format code (fix-db-zombie-connec..." | Re-trigger Greptile