README
¶
Goのsql.DBは、いつプールに戻しているのか
概要
- Goの標準ライブラリの
database/sqlで中心となる sql.DB ではデフォルトでコネクションプールが使われるようになっていて明示的な操作は不要 - どのタイミングでプールに返却されているのかについてパターンを整理
Docker
データベースはPostgreSQLのDockerコンテナを使用する
データベースのコンテナ起動
docker compose up -d --wait
- Docker Composeはプラグイン版
データベースのコンテナ削除
docker compose down
テーブル
- 実行時のセットアップ処理で初期化
- 1テーブル(shop)のみ
- 主キーはデータベース側で採番
erDiagram
shop {
int id PK
string name
datetime created_at
}
サンプルコードの実行
go run . サンプル名
- サンプル名は大文字小文字の区別なし
例
go run . ex0201
*sql.Conn
- *sql.Conn の
Close()でプールに返却されるパターン。トランザクションありでINSERTを連続して2回実行する例
- 処理の流れを追いやすくするため、実装は関心事だけに絞り、ログ出力によりコネクションプールの状態を確認
go run . ex0202
{"time":"2024-10-05T10:18:16.514616341+09:00","level":"INFO","msg":"before","Open":1,"InUse":1,"Idle":0}
{"time":"2024-10-05T10:18:16.514732242+09:00","level":"INFO","msg":"after ","Open":1,"InUse":0,"Idle":1}
conn.Close()の前後でInUseからIdleに移っている。つまりプールに返却されている
*sql.DB/*sql.Tx
- CommitもしくはRollbackでプールに返却されるパターン(*sql.Txに
Close()はない)
Commit
go run . ex0203
{"time":"2024-10-05T10:18:32.800364346+09:00","level":"INFO","msg":"before","Open":1,"InUse":1,"Idle":0}
{"time":"2024-10-05T10:18:32.801725492+09:00","level":"INFO","msg":"after ","Open":1,"InUse":0,"Idle":1}
tx.Commit()でプールに返却されている
Rollback
go run . ex0204
{"time":"2024-10-05T10:18:54.929728177+09:00","level":"INFO","msg":"before","Open":1,"InUse":1,"Idle":0}
{"time":"2024-10-05T10:18:54.930214444+09:00","level":"INFO","msg":"after ","Open":1,"InUse":0,"Idle":1}
tx.Rollback()でプールに返却されている
*sql.Conn/*sql.Tx
- *sql.DBで
BeginTx()した場合と*sql.ConnでBeginTx()した場合とで挙動が異なる - *sql.Connで
BeginTx()した場合はCommit()とRollback()ではプールに返却されず、*sql.ConnのClose()するまでは返却されない
go run . ex0205
{"time":"2024-10-05T10:19:11.595791369+09:00","level":"INFO","msg":"before","Open":1,"InUse":1,"Idle":0}
{"time":"2024-10-05T10:19:11.597110634+09:00","level":"INFO","msg":"after ","Open":1,"InUse":1,"Idle":0,"err":null}
- InUseに残ったままで返却されていない
- エラーも発生していない
DB.ExecContext
- 実行毎にプールに返却されるパターン
go run . ex0206
{"time":"2024-10-05T10:19:24.938171633+09:00","level":"INFO","msg":"before","Open":1,"InUse":0,"Idle":1}
{"time":"2024-10-05T10:19:24.939661816+09:00","level":"INFO","msg":"after ","Open":1,"InUse":0,"Idle":1}
- 前後ともInUseが0になっている
DB.QueryRowContext
row.Scan()でプールに返却されるパターン
go run . ex0207
{"time":"2024-10-05T10:26:25.223398589+09:00","level":"INFO","msg":"before","Open":1,"InUse":1,"Idle":0}
{"time":"2024-10-05T10:26:25.223483481+09:00","level":"INFO","msg":"after ","Open":1,"InUse":0,"Idle":1,"id":1,"name":"shop1"}
DB.QueryContext
rows.Next()が false になったタイミングでプールに返却されるパターン- 処理の流れを追いやすくするため、for文を使わずにループを展開
go run . ex0208
{"time":"2024-10-05T10:26:41.701379244+09:00","level":"INFO","msg":"before","Open":1,"InUse":1,"Idle":0,"id":2,"name":"shop2"}
{"time":"2024-10-05T10:26:41.701453886+09:00","level":"INFO","msg":"after ","Open":1,"InUse":0,"Idle":1}
- もし仮に false になるまで
rows.Next()を呼ばなかった場合はrows.Close()のタイミングで返却されることになる
go run . ex0209
{"time":"2024-10-05T10:27:00.564888592+09:00","level":"INFO","msg":"before","Open":1,"InUse":1,"Idle":0,"id":1,"name":"shop1"}
{"time":"2024-10-05T10:27:00.564959969+09:00","level":"INFO","msg":"after ","Open":1,"InUse":0,"Idle":1}
DB.Close
- DB.Closeはプールにあるコネクションをクローズする
go run . ex0210
{"time":"2024-10-05T10:27:19.849655477+09:00","level":"INFO","msg":"before","Open":5,"InUse":0,"Idle":5}
{"time":"2024-10-05T10:27:19.849937555+09:00","level":"INFO","msg":"after ","Open":0,"InUse":0,"Idle":0}
- OpenとIdleも0になっている
- ただし、InUseのコネクションはクローズされない
go run . ex0211
{"time":"2024-10-05T10:27:33.158506751+09:00","level":"INFO","msg":"before","Open":5,"InUse":3,"Idle":2}
{"time":"2024-10-05T10:27:33.158716381+09:00","level":"INFO","msg":"after ","Open":3,"InUse":3,"Idle":0}
{"time":"2024-10-05T10:27:33.158800542+09:00","level":"INFO","msg":"conn 0","Open":2,"InUse":2,"Idle":0}
{"time":"2024-10-05T10:27:33.159040339+09:00","level":"INFO","msg":"conn 1","Open":1,"InUse":1,"Idle":0}
{"time":"2024-10-05T10:27:33.159085245+09:00","level":"INFO","msg":"conn 2","Open":1,"InUse":1,"Idle":0}
{"time":"2024-10-05T10:27:33.159092198+09:00","level":"INFO","msg":"conn 3","Open":1,"InUse":1,"Idle":0}
{"time":"2024-10-05T10:27:33.159241823+09:00","level":"INFO","msg":"conn 4","Open":0,"InUse":0,"Idle":0}
- Idleが0になってもInUseは3のまま残っている
- クローズされるコネクションはIdleのみで、InUseは
Close()でプールに戻されることなく、直接クローズされている
プールの制御
デフォルトでコネクションプールが使われるようになっているが、明示的にプールに戻すタイミングを制御する用途向けとして sql.Conn が用意されいる
https://github.com/golang/go/blob/go1.23.1/src/database/sql/sql.go#L2139-L2146
https://github.com/golang/go/blob/go1.23.1/src/database/sql/sql.go#L1935-L1937
関連ドキュメント
Documentation
¶
There is no documentation for this package.
Click to show internal directories.
Click to hide internal directories.