[インデックス 17441] ファイルの概要
このコミットは、Go言語の database/sql
パッケージにおける以前の変更(CL 10726044 / c9bea548fb6f)を元に戻すものです。この元に戻された変更は、データベース接続プールの管理に関するもので、特に SetMaxOpenConns
機能の追加と、アイドル接続プールの実装変更(スライスベースからリストベースへ)を含んでいました。しかし、この変更はビルドを壊し、さらに競合状態(race condition)を引き起こす問題があったため、このコミットで元に戻されました。
コミット
commit 9456adb36b3731e1e4accadddd7cd2abf5a911b8
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date: Thu Aug 29 17:26:00 2013 -0700
undo CL 10726044 / c9bea548fb6f
Breaks build, and has a race.
««« original CL description
database/sql: add SetMaxOpenConns
Update #4805
Add the ability to set an open connection limit.
Fixed case where the Conn finalCloser was being called with db.mu locked.
Added seperate benchmarks for each path for Exec and Query.
Replaced slice based idle pool with list based idle pool.
R=bradfitz
CC=golang-dev
https://golang.org/cl/10726044
»»»
R=golang-dev
CC=golang-dev
https://golang.org/cl/13252046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/9456adb36b3731e1e4accadddd7cd2abf5a911b8
元コミット内容
このコミットによって元に戻された元のコミット (CL 10726044 / c9bea548fb6f) の内容は以下の通りです。
database/sql
パッケージにSetMaxOpenConns
関数を追加し、オープンなデータベース接続の最大数を設定できるようにする機能。Conn
のfinalCloser
がdb.mu
(データベースミューテックス) がロックされた状態で呼び出されるケースを修正。Exec
とQuery
の各パスに対して個別のベンチマークを追加。- アイドル接続プールをスライスベースの実装からリストベースの実装に置き換え。
変更の背景
このコミットの背景には、Go言語の database/sql
パッケージにおけるデータベース接続管理の改善試みがあります。元のコミット (CL 10726044) は、特にオープンな接続数の制限機能 (SetMaxOpenConns
) を導入し、接続プールの効率を向上させることを目的としていました。これは、Issue #4805 で報告された、データベース接続の過剰なオープンによるリソース枯渇やパフォーマンス問題に対処するための一環でした。
しかし、この変更は以下の問題を引き起こしました。
- ビルドの破損 (Breaks build): 変更がGoのビルドシステムと互換性がなく、コンパイルエラーなどを引き起こした可能性があります。
- 競合状態 (Has a race): 導入されたコードに、複数のゴルーチンが同時に同じリソースにアクセスしようとした際に、予期せぬ動作やデータ破損を引き起こす可能性のある競合状態が含まれていました。これは、特に並行処理が重要なデータベース接続プールにおいて致命的な問題となります。
これらの問題が発見されたため、安定性と信頼性を確保するために、元の変更を完全に元に戻すことが決定されました。
前提知識の解説
このコミットを理解するためには、以下のGo言語およびデータベース関連の概念に関する知識が必要です。
database/sql
パッケージ: Go言語の標準ライブラリの一部で、SQLデータベースとの対話のための汎用インターフェースを提供します。このパッケージ自体は特定のデータベースドライバを含まず、ドライバは別途インポートして使用します。- データベース接続プール: データベースへの接続はリソースを消費し、確立に時間がかかるため、アプリケーションは通常、接続を再利用するために接続プールを使用します。接続プールは、使用可能な接続のキャッシュを維持し、必要に応じてアプリケーションに提供します。
- アイドル接続 (Idle Connections): 使用中でないが、将来の再利用のためにプールに保持されているデータベース接続。
- オープン接続 (Open Connections): 現在アクティブに使用されているか、アイドル状態であってもプールに保持されているすべてのデータベース接続。
SetMaxOpenConns
: データベース接続プールが同時に保持できるオープン接続の最大数を設定する機能。これにより、データベースサーバーへの負荷を制御し、リソースの枯渇を防ぐことができます。SetMaxIdleConns
: データベース接続プールが保持できるアイドル接続の最大数を設定する機能。これにより、接続の再利用を促進し、接続確立のオーバーヘッドを削減できます。- ミューテックス (
sync.Mutex
): 複数のゴルーチンが共有リソースに同時にアクセスするのを防ぐための同期プリミティブ。これにより、競合状態を防ぎ、データの整合性を保ちます。 - 競合状態 (Race Condition): 複数のゴルーチンが共有リソースに同時にアクセスし、そのアクセス順序によってプログラムの最終結果が変わってしまうバグ。デバッグが困難なことで知られています。
container/list
パッケージ: Go言語の標準ライブラリで、双方向リンクリストの実装を提供します。- スライス (Slice): Go言語の動的配列。連続したメモリ領域に要素を格納します。
finalCloser
: リソースが最終的にクローズされる際に呼び出される関数やメカニズム。driver.Conn
:database/sql/driver
パッケージで定義されているインターフェースで、データベースへの単一の接続を表します。
技術的詳細
このコミットは、以前の変更によって導入された database/sql
パッケージの主要なデータ構造とロジックを元に戻しています。
元に戻された主な変更点:
DB
構造体の変更:DB
構造体からlist.List
型のfreeConn
(アイドル接続プール) とconnRequests
(接続要求キュー) が削除され、代わりにスライスベースのfreeConn []*driverConn
に戻されました。numOpen
,pendingOpens
,openerCh
,maxOpen
といった、オープン接続数の制限と非同期的な接続オープンに関連するフィールドが削除されました。connectionRequestQueueSize
定数も削除されました。
- 接続プールのロジックの変更:
DB.connectionOpener()
ゴルーチンと、それに関連するopenerCh
を介した非同期的な接続オープンロジックが削除されました。DB.maybeOpenNewConnections()
関数も削除されました。DB.conn()
メソッド内の、maxOpen
制限とconnRequests
を使用した接続取得ロジックが削除され、シンプルなアイドル接続の取得または新規接続のオープンに戻されました。DB.putConnDBLocked()
関数が削除され、DB.putConn()
が直接アイドル接続プールへの追加または接続のクローズを行うようになりました。
SetMaxOpenConns
の削除:DB.SetMaxOpenConns()
メソッドが完全に削除されました。これにより、database/sql
パッケージはオープン接続数の上限を直接管理する機能を持たなくなりました(これはドライバの実装に依存する形に戻ります)。
Close
メソッドの変更:DB.Close()
メソッドのロジックが簡素化され、openerCh
のクローズやconnRequests
の処理が削除されました。アイドル接続を閉じる処理は、スライスベースのfreeConn
を直接イテレートする形に戻りました。
driverConn
構造体の変更:driverConn
構造体からlistElem *list.Element
フィールドが削除されました。これは、アイドル接続プールがリンクリストではなくスライスで管理されるようになったためです。
- テストコードの変更:
TestMaxOpenConns
テストが削除されました。TestConcurrency
および関連するベンチマークテストから、リンクリストベースの接続プールとSetMaxOpenConns
に依存する並行テストの構造が削除されました。fakedb_test.go
におけるNOSERT
コマンドとdoInsert
パラメータの削除。これは、元のコミットで導入された並行テスト用の特殊な挿入ロジックが不要になったためです。
このロールバックにより、database/sql
パッケージは、以前の、よりシンプルな接続管理モデルに戻りました。これは、複雑な接続プール管理ロジックがビルドの破損や競合状態を引き起こしたため、安定性を優先した結果と言えます。
コアとなるコードの変更箇所
このコミットにおけるコアとなるコードの変更箇所は、主に src/pkg/database/sql/sql.go
ファイルに集中しています。
-
src/pkg/database/sql/sql.go
:import
文から"container/list"
が削除。DB
構造体からfreeConn *list.List
,connRequests *list.List
,numOpen int
,pendingOpens int
,openerCh chan struct{}
,maxOpen int
フィールドが削除され、freeConn []*driverConn
に戻された。driverConn
構造体からlistElem *list.Element
フィールドが削除された。connectionRequestQueueSize
変数が削除された。Open
関数におけるDB
の初期化で、openerCh
とconnRequests
の初期化が削除された。Close
関数内のリンクリストとopenerCh
に関連するロジックが削除され、スライスベースのfreeConn
を直接処理する形に戻された。SetMaxIdleConns
関数内のリンクリスト操作がスライス操作に置き換えられた。SetMaxOpenConns
関数が完全に削除された。maybeOpenNewConnections
関数が削除された。connectionOpener
関数が削除された。openNewConnection
関数が削除された。connRequest
型の定義が削除された。conn
関数内の、maxOpen
制限とconnRequests
を使用した複雑な接続取得ロジックが削除され、シンプルなスライスベースのアイドル接続取得または新規接続オープンに戻された。connIfFree
関数内のリンクリスト操作がスライス操作に置き換えられた。putConn
関数内のnumOpen
のデクリメントとmaybeOpenNewConnections
の呼び出しが削除された。putConnDBLocked
関数が削除された。
-
src/pkg/database/sql/fakedb_test.go
:fakeConn.Prepare
およびfakeStmt.Exec
からNOSERT
コマンドの処理が削除された。fakeStmt.execInsert
関数からdoInsert
パラメータが削除され、常に挿入を行うように変更された。
-
src/pkg/database/sql/sql_test.go
:import
文から"math/rand"
が削除された。putConnHook
内のlistElem
のチェックがスライス内の要素チェックに置き換えられた。closeDB
関数内のリンクリスト操作がスライス操作に置き換えられた。numPrepares
関数内のリンクリスト操作がスライス操作に置き換えられた。numFreeConns
関数内のリンクリスト操作がスライス操作に置き換えられた。TestMaxOpenConns
テスト関数が完全に削除された。TestConcurrency
および関連するBenchmarkConcurrent*
テスト関数が大幅に簡素化され、リンクリストベースの接続プールとSetMaxOpenConns
に依存するテストロジックが削除された。
コアとなるコードの解説
このコミットの核心は、database/sql
パッケージの接続プール管理メカニズムを、リンクリストベースの複雑な実装から、よりシンプルでスライスベースの実装へと巻き戻した点にあります。
DB
構造体と接続プール:
元の変更では、DB
構造体は container/list.List
を使用してアイドル接続 (freeConn
) と接続要求 (connRequests
) を管理していました。また、maxOpen
というフィールドでオープン接続の最大数を制限し、openerCh
と connectionOpener
ゴルーチンを使って非同期的に新しい接続を開くメカニズムを持っていました。
このコミットでは、これらの複雑なメカニズムがすべて削除されました。freeConn
は単純な []*driverConn
(スライス) に戻され、接続の追加や削除はスライスの操作(append
やスライスを再スライスする)によって行われます。これにより、接続プールの管理が大幅に簡素化されました。
SetMaxOpenConns
の削除:
SetMaxOpenConns
は、データベースへの同時接続数を制限するための重要な機能でしたが、このコミットで削除されました。これは、この機能の実装がビルドの破損や競合状態を引き起こしたため、一時的にこの機能を取り除くことでパッケージの安定性を回復させることを目的としています。この変更後、オープン接続数の制限は、Goの database/sql
パッケージ自体ではなく、基盤となるデータベースドライバやデータベースサーバーの設定に依存することになります。
conn()
メソッドの簡素化:
conn()
メソッドは、データベース接続を取得する主要な関数です。元の変更では、このメソッドは maxOpen
制限を考慮し、必要に応じて connRequests
を介して新しい接続のオープンを要求する複雑なロジックを持っていました。ロールバック後、conn()
はまずアイドル接続プール (db.freeConn
スライス) から利用可能な接続を探し、見つからなければ直接新しい接続を開くという、より直接的なアプローチに戻りました。
競合状態の解消:
元のコミットが「競合状態を持つ」と明記されていることから、特に db.mu
(データベースミューテックス) のロックとアンロックのタイミング、および複数のゴルーチンが接続プールにアクセスする際の同期に問題があったと推測されます。このロールバックにより、複雑な非同期処理やリンクリスト操作が排除されたことで、これらの競合状態が解消され、パッケージの堅牢性が向上しました。
全体として、このコミットは、機能追加よりも安定性と正確性を優先した、典型的な「リバートコミット」の例です。問題のあるコードを迅速に削除し、既知の安定した状態に戻すことで、さらなる問題の発生を防ぎ、将来の修正のためのクリーンな基盤を提供します。
関連リンク
- 元の変更 (CL 10726044): https://golang.org/cl/10726044
- このコミット (CL 13252046): https://golang.org/cl/13252046
- 関連するIssue (#4805): https://golang.org/issue/4805
参考にした情報源リンク
- Go
database/sql
パッケージのドキュメント: https://pkg.go.dev/database/sql - Go
sync
パッケージのドキュメント (Mutex): https://pkg.go.dev/sync - Go
container/list
パッケージのドキュメント: https://pkg.go.dev/container/list - Go言語における競合状態と同期プリミティブに関する一般的な情報源 (例: Go Concurrency Patterns, Go Memory Modelなど)
- Go言語のコードレビューシステム (Gerrit) の使い方に関する情報