[インデックス 14636] ファイルの概要
このコミットは、Go言語の標準ライブラリである database/sql
パッケージにおけるエラーハンドリングの改善に関するものです。具体的には、データベースドライバが driver.ErrBadConn
を返した場合に、*DB.begin
メソッドがそのエラーをそのまま返すように変更することで、database/sql
パッケージが接続不良を適切に検知し、より堅牢な接続管理を行えるようにしています。
コミット
commit 309eae19235349d39053f06887f0384c5757fa3e
Author: James David Chalfant <james.chalfant@gmail.com>
Date: Wed Dec 12 22:04:55 2012 -0800
database/sql: Alter *DB.begin to return driver.ErrBadConn when driver.Conn.Begin returns driver.ErrBadConn
Fixes #4433
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/6845094
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/309eae19235349d39053f06887f0384c5757fa3e
元コミット内容
database/sql: Alter *DB.begin to return driver.ErrBadConn when driver.Conn.Begin returns driver.ErrBadConn
(database/sql
: *DB.begin
が driver.Conn.Begin
が driver.ErrBadConn
を返した場合に driver.ErrBadConn
を返すように変更)
このコミットは、database/sql
パッケージ内の *DB.begin
メソッドの動作を変更し、基盤となるデータベースドライバの driver.Conn.Begin
メソッドが driver.ErrBadConn
エラーを返した場合に、そのエラーをラップせずに直接返すように修正しています。これにより、database/sql
パッケージが接続の不良状態を正確に認識し、適切な処理(例えば、その接続をプールから削除して新しい接続を確立するなど)を実行できるようになります。
変更の背景
Goの database/sql
パッケージは、データベースとのやり取りを抽象化し、様々なデータベースドライバを統一的に扱うためのインターフェースを提供します。このパッケージの重要な機能の一つに、データベース接続のプールと管理があります。データベース接続は、ネットワークの問題、データベースサーバーの再起動、アイドルタイムアウトなど、様々な理由で無効になることがあります。
driver.ErrBadConn
は、Goの database/sql/driver
パッケージで定義されている特別なエラーで、データベースドライバが「この接続はもう使えない」と database/sql
パッケージに伝えるためのシグナルとして機能します。database/sql
パッケージは、この driver.ErrBadConn
を受け取ると、その接続を無効と判断し、接続プールから削除して再利用しないようにします。これにより、アプリケーションが壊れた接続を使い続けようとすることを防ぎ、より堅牢なデータベース操作を実現します。
このコミット以前は、*DB.begin
メソッド内で driver.Conn.Begin
が driver.ErrBadConn
を返した場合でも、そのエラーは fmt.Errorf("sql: failed to Begin transaction: %v", err)
のように新しいエラーメッセージでラップされていました。エラーがラップされると、database/sql
パッケージは errors.Is
(または Go 1.13 以前の err == driver.ErrBadConn
) を使って元の driver.ErrBadConn
を直接識別することができませんでした。結果として、database/sql
パッケージは接続が不良であることを認識できず、壊れた接続を接続プールに保持し続け、後続の操作で再び失敗する可能性がありました。
この問題は、GoのIssue #4433として報告されており、このコミットはその修正を目的としています。
前提知識の解説
database/sql
パッケージ: Go言語の標準ライブラリの一部で、SQLデータベースとの対話のための汎用的なインターフェースを提供します。特定のデータベースシステムに依存しないコードを書くことを可能にし、データベースドライバを介して実際のデータベース操作を行います。database/sql/driver
パッケージ:database/sql
パッケージがデータベースドライバと通信するためのインターフェースを定義しています。各データベース(PostgreSQL, MySQL, SQLiteなど)に対応するドライバは、このインターフェースを実装する必要があります。driver.Conn
インターフェース:database/sql/driver
パッケージ内で定義されており、単一のデータベース接続を表します。このインターフェースには、トランザクションを開始するためのBegin()
メソッドなどが含まれます。driver.ErrBadConn
:database/sql/driver
パッケージで定義されている特別なエラー変数です。ドライバがこのエラーを返した場合、database/sql
パッケージは、その接続が使用不可能であり、接続プールから削除して再利用すべきではないと解釈します。これは、データベース接続が切断されたり、無効になったりした場合にドライバがdatabase/sql
パッケージに通知するためのメカニズムです。- エラーラッピングとアンラッピング: Go 1.13以降では、
fmt.Errorf
に%w
動詞を使用することでエラーをラップし、errors.Is
やerrors.As
を使って元のエラー(ラップされたエラー)を検査できるようになりました。しかし、このコミットが作成された2012年時点では、このようなエラーラッピングのメカニズムは存在せず、エラーをラップすると元のエラーの型を失うのが一般的でした。そのため、driver.ErrBadConn
のような特定の型のエラーを識別するためには、エラーがラップされていない状態で直接比較する必要がありました。
技術的詳細
このコミットの核心は、database/sql
パッケージが driver.ErrBadConn
を正しく認識できるようにすることです。database/sql
パッケージは、データベース操作中にエラーが発生した場合、そのエラーが driver.ErrBadConn
であるかどうかをチェックします。もしそうであれば、その接続は不良であると判断し、接続プールから削除して、次の操作のために新しい接続を確立しようとします。
*DB.begin
メソッドは、新しいトランザクションを開始するためにデータベース接続を取得し、その接続の Begin()
メソッドを呼び出します。もし Begin()
メソッドがエラーを返した場合、このコミット以前は以下のようなコードでした。
if err != nil {
db.putConn(ci, err)
return nil, fmt.Errorf("sql: failed to Begin transaction: %v", err)
}
ここで fmt.Errorf
を使用してエラーをラップしているため、database/sql
パッケージのより上位の層では、返されたエラーが driver.ErrBadConn
であるかどうかを直接判断できませんでした。
このコミットでは、この行を以下のように変更しています。
if err != nil {
db.putConn(ci, err)
return nil, err
}
この変更により、driver.Conn.Begin()
から返されたエラーが driver.ErrBadConn
であった場合、そのエラーがそのまま *DB.begin
の呼び出し元に伝播されます。これにより、database/sql
パッケージの接続管理ロジックが driver.ErrBadConn
を検出し、不良な接続を適切に処理できるようになります。具体的には、db.putConn(ci, err)
が呼び出された際に、err
が driver.ErrBadConn
であれば、putConn
内部でその接続を不良としてマークし、接続プールに戻さない、あるいはプールから削除するなどの処理が行われます。
この修正は、database/sql
パッケージの堅牢性を高め、データベース接続が予期せず切断された場合でも、アプリケーションが自動的に回復し、新しい有効な接続を使用して操作を続行できるようにするために不可欠です。
コアとなるコードの変更箇所
変更は src/pkg/database/sql/sql.go
ファイルの func (db *DB) begin() (tx *Tx, err error)
関数内で行われています。
--- a/src/pkg/database/sql/sql.go
+++ b/src/pkg/database/sql/sql.go
@@ -426,7 +426,7 @@ func (db *DB) begin() (tx *Tx, err error) {
txi, err := ci.Begin()
if err != nil {
db.putConn(ci, err)
- return nil, fmt.Errorf("sql: failed to Begin transaction: %v", err)
+ return nil, err
}
return &Tx{
db: db,
コアとなるコードの解説
変更された行は以下の通りです。
変更前:
return nil, fmt.Errorf("sql: failed to Begin transaction: %v", err)
この行では、ci.Begin()
から返されたエラー err
を、"sql: failed to Begin transaction: %v"
という新しいフォーマット文字列でラップしていました。これにより、元のエラー err
の型情報(特に driver.ErrBadConn
であるかどうか)が失われ、呼び出し元で driver.ErrBadConn
を直接識別することが困難になっていました。
変更後:
return nil, err
この行では、ci.Begin()
から返されたエラー err
をそのまま返しています。これにより、もし err
が driver.ErrBadConn
であった場合、その情報がそのまま呼び出し元に伝達されます。database/sql
パッケージの接続管理ロジックは、この driver.ErrBadConn
を受け取ることで、接続が不良であることを認識し、その接続を接続プールから削除するなどの適切な回復処理を実行できるようになります。
このシンプルな変更は、database/sql
パッケージがデータベース接続の健全性をより正確に判断し、アプリケーションの信頼性と回復力を向上させる上で非常に重要な役割を果たします。
関連リンク
- Go Issue #4433: https://github.com/golang/go/issues/4433 (このコミットが修正した問題のトラッキング)
- Go CL 6845094: https://golang.org/cl/6845094 (このコミットの変更リスト)
- Go
database/sql
パッケージのドキュメント: https://pkg.go.dev/database/sql - Go
database/sql/driver
パッケージのドキュメント: https://pkg.go.dev/database/sql/driver
参考にした情報源リンク
- Go
database/sql
のdriver.ErrBadConn
に関する解説記事:- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEXdTimuASxddC2McXOnsjq0v9F7o9pyaQwy3lBYJhrU_cbuxwEQ44Onfyd4Or1pJc9LETP_2K-0e57wyFxHTlDx9Y815ZLiBb_149ft1fAiQDw4kxrZuIYk_wWZ_EprDVTSPP7pux3t9BrFc2htQBc8Rrx4C6OzFo0Rfeo3Ec4bkJ8WOzDELJhsvD2Ol-yZ09vRzI2nluxDzAIOs8FtS3DS1lxf6r478_-_TaGBPEzAptC_fdgAceVPP9NWukX9RwSJomBaMJ8n-N1ySPn5kXLSad59ZPJYaV8eWvt0P3qRjYgiIiZGrbEGVBjTYHdZySVwssUsSNn_nEt3gfdup8leApVQ04cWyPt2147S5Z1TX-j2vdGxYAB0okrQHW2IprUlLOoIfwwmz1HZDvUkcCQoNE=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQFTW3lHflMjCB9b99G4wHN0KD5vQKoCQOIP9m3nJIhfgz-HeR4XJN6M3V20ko8fBmvQYp6rzGTMRC03SAIDUxdxdHmx6k59PPJZOtAjxVjiv606qBZUd3DIKWFKfJRoums=
- Goにおけるエラーハンドリングの進化(
errors.Is
など)に関する情報(このコミットの時点では存在しないが、背景理解に役立つ):- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQEuiBdUwK3s_AqwGpt-mrn6F5bF55e5dwzlILbS_x_MyPZxgAOUxM7O3XIBVXrFrCk4T8oMlO9kCbMvCJ3Kd5hD_mVqHVMKmr0sDp7p4QlrjLBiZO5YuKSu0WckvACAPg4IK-M=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQE9KEobdSaMj8MkN_eXG4l0cq-02Ksb7A386vdCGqH803qy-XkeJtF5eK5OoTLnfg2kNPCm30mAsKsZs0FreBEM_IWGFPYXksMUE6cyvDv4xQkbOAUp_xj59RgCLaLiDkFlTc8=
- https://vertexaisearch.cloud.google.com/grounding-api-redirect/AUZIYQGOuGbZnjuaoqaQgTqN0sYlncyp9okX7JOXV_StGn2D4WkqiX7wpv3vZsKLo3jNoqihHUhP5ljhbSqrQSgsfPwm4hrpeYppedIKlICITI66lS7ItE-9OavOQvGwgHgN_KjlshE=