Conversation
Signed-off-by: hiroto.toyoda <hiroto.toyoda@dena.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
Included review availability: Your plan provides up to 10 included reviews per hour; 9 remain after this review. WalkthroughThe change exports ChangesBad connection sentinel
Estimated code review effort: 2 (Simple) | ~10 minutes Merge Risk: ⚪ Minimal · up to This PR only exposes the existing bad-connection sentinel without changing connection behavior or error handling, so no actionable merge-blocking risk remains after normal checks and review. 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 2 functions across 4 files. (1 skipped: 1 unsupported.)
✨ 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 |
|
This error is intended to be something users neither need to use nor even need to know about. Why do you want to detect it with |
|
@methane I don't have evidence this is common beyond our case, other than #1303 where someone else also wanted to detect it and was told it's internal — so I can't claim strong demand for a permanent public API commitment. Given that, would you prefer a narrower fix instead: since |
|
Why are you shutting down the database before gracefully shutting down the application? I think this error is useful because it reveals that the shutdown order may be unsafe. |
|
Not a shutdown-ordering issue on our side. This happens on idle connections that the peer (MySQL server's wait_timeout, or a proxy/LB in front of it) had already closed independently, before Close() ever runs. sql.DB can't detect a dead idle connection until it tries to use it — here, via Close()'s COM_QUIT — so this can fire whenever the pool closes a connection the server side already dropped, whether during graceful shutdown or routine idle-connection eviction (ConnMaxIdleTime). It's a routine TCP-level race, not evidence our app closes the DB too early. |
|
ja: その場合、たまたまエラーが発生したのがCloseだったから問題にならなかっただけで危険な構成であること自体には変わりありません。あなたのアプリケーションは長時間のidleのあと、たまたまサーバーから接続がcloseされるのと同時にクエリを実行した場合にリトライ不可能なエラーを起こす可能性があります。SetConnMaxLifetime などを使ってサーバー側のタイムアウトよりも明確に短いタイムアウトをクライアントに持たせるべきです。これはMySQLだけでなくHTTPでも同じです。サーバーから接続をcloseすることは危険で、クライアントはサーバーから接続をcloseされるよりも早いidle timeoutやlifetimeを持つべきです。 en: After a long idle period, your application could issue a query at exactly the same time the server happens to close the connection, resulting in a non-retryable error. You should configure the client with a timeout that is clearly shorter than the server-side timeout, for example by using SetConnMaxLifetime. This is not specific to MySQL; the same principle applies to HTTP as well. Having the server close a connection is inherently risky. The client should use an idle timeout or connection lifetime that expires before the server closes the connection. |
Description
Conn.Close()can returnerrBadConnNoWriteas-is: when the peer has alreadyclosed the connection before
COM_QUITcould be sent,writePacket()returnsthis sentinel, and
Close()does not run it throughmarkBadConn()(Close isnot a retryable operation, so converting it to
driver.ErrBadConnwould bemeaningless there).
Being unexported, this value cannot be identified by callers except by
matching the error string
"bad connection", which is fragile since itdepends on an internal implementation detail. This is a real-world problem
when apps want to distinguish this specific, benign, unpreventable condition
(closing an already-dead idle pooled connection, e.g. during graceful
shutdown) from other, more meaningful close errors.
This PR simply exports
errBadConnNoWriteasErrBadConnNoWrite. It is apure rename: the error value, its message, and every existing code path
(including
markBadConn's conversion todriver.ErrBadConnfor theretry-safe call sites:
Begin/BeginTx/Exec/ExecContext/Prepare/PrepareContext/Query/QueryContext) are unchanged. Callers can now writeerrors.Is(err, mysql.ErrBadConnNoWrite)instead of matching on the errorstring.
Related history
should never reach a caller un-converted, because
markBadConnalwaysturns it into
driver.ErrBadConnfirst.Close()is the one path wherethat assumption doesn't hold, since it intentionally skips
markBadConn.markBadConn) entirely.That PR was split into Reduce "busy buffer" logs #1641/stmt.Close() returns nil when double close #1642, which trimmed the other use sites
(busy-buffer cases) while intentionally keeping the single remaining one
in
writePacket(), precisely because removing it entirely would breakretry-safety signaling for
Query/Execwhen the first write fails withzero bytes sent. This PR does not touch that retained logic at all — it
only makes the existing sentinel usable via
errors.Isby externalcallers.
Checklist