• home
  • database/sqlのコネクション周りの実装をちゃんと調べた

database/sqlのコネクション周りの実装をちゃんと調べた

Goのプロジェクトをやってる中で、database/sqlのトランザクションがどう管理されているのか不安に思ったので、ちゃんと調べてみた。

これを参考にコードを追った。
ちなみにGoのバージョンは1.9.1
https://qiita.com/methane/items/a9803388ff4a5a985b4d

登場人物(オブジェクト)

  • sql.DB
    • DBを司るオブジェクト。
  • sql.Tx
    • トランザクションを管理するオブジェクト。sql.DB.Begin()すると作られる。
  • sq.driverConn
    • DBのコネクションを司るオブジェクト。コネクションプールから取ってきたコネクション情報が入ってる。
  • driverStmt
    • コネクションプールのステートを司るオブジェクト。こいつをみて、使用中かどうか判断する。

実行している流れ

通常のクエリ

使用時

  1. sql.DB.Query(...)の実行してクエリを実行
  2. sql.Rowsのオブジェクトが帰ってくる
  3. sql.Rows.Close()を実行する。

内部の実行内容

  • Queryの実行時
    • sql.DB.conn(ctx context.Context, strategy connReuseStrategy) (*driverConn, error)が実行される
      • DB.freeconに使用していないコネクションオープン済みのdriverConnが入っている。
      • sql.DB.conn(...)を実行すると、開いているコネクションがあればそれを返し、なかったら開いているコネクションがないか探索して、それでもなければ新しいコネクションを作って返す(MaxConnectionまでの範囲内で。)
    • driverConnと使用中フラグをあげたdriverStmtをDBオブジェクトに詰める
    • 詰めたDBオブジェクトをsql.Rowsに詰めて、sql.Rowsに実行結果を詰めて返す。
  • sql.Rows.Close()実行時
    • sql.RowsにあるDBオブジェクトから辿っていって、driverStmtの使用中フラグを下ろす

トランザクショナルなクエリ

使用時

  • sql.DB.Begin() (*Tx, error)を実行する
  • sql.Txオブジェクトを使って一連のトランザクションでの処理を実行する
  • 終わったらsql.Tx.Commit()/sql.Tx.Rollback()を実行する

内部の実行内容

  • sql.DB.Begin()実行時
    • sql.DB.conn(...)を実行してdriverConnオブジェクトを取得
    • driverConnをと使用中フラグをあげたdriverStmtをsql.TxにつめてTxオブジェクトを返す
  • sql.DB.Commit()/.Rollback()実行時
    • sql.Txから辿って行って、driverStmtの使用中フラグを下ろす

で、どういうことだってばよ。

通常のクエリ

  1. コネクションが開いているやつをコネクションプールから適当に持ってくる
  2. コネクションに紐つくステートメントオブジェクトの使用中フラグをあげる
  3. クエリを流す
  4. 流し終わったら(つまり、sql.Rows.Close()が実行されたら)、使用中のフラグを戻して、コネクションプールに返す。

この場合、常に、オープンな適当なコネクションがくるので、どのコネクションがくるかはわからない。

トランザクショナルなクエリ

  1. 通常と同じようにコネクションが開いているやつをコネクションプールから適当に持ってくる
  2. コネクションに紐つくステートメントオブジェクトの使用中フラグをあげる
  3. フラグをあげたコネクションオブジェクトをトランザクションオブジェクトに詰めて、トランザクションオブジェクト返す
  4. トランザクションオブジェクトのCommit/Rollbackメソッドが実行されたら、ステートメントオブジェクトの使用中フラグを戻してコネクションプールに返す。

参考までにgormの実装は

一部のプロジェクトではORMとしてGORMを採用しているのだが、そこでは何をやっているのか気になったので調べてみた。
結論から言うと(当たり前だけど)、gormはただ単にdatabase/sqlの各メソッドをラップしているだけなので、処理の流れは同じ。
ただ、Rows.Close()を自動でやっているだけ。

ただひとつだけ問題があって、gorm.DB自体がsql.Txとsql.DBの両方の意味をもっているということ。
どちらのオブジェクトもクエリを実行できるオブジェクトなので分けたくなかった、という意図だと思われるが、使っている側からしたらトランザクションを開始したオブジェクトなのかわかんないっていう。