コンテキスト
Go標準ライブラリのdatabase/sqlパッケージを利用して、DB操作のコンテキスト(Context)を指定する方法を説明します。
Contextを使ったDB操作の方法
Contextは処理のキャンセルやタイムアウトをするための仕組みです。
Go 1.8以降:
database/sqlはContext対応メソッドを提供しています。 このページでは、現在のGoで使いやすいContext対応メソッドを前提に説明します。
DBアクセスでContextを使うには、これまで利用してきたメソッド名の末尾に 『Context』をつけたメソッドを利用します。 これらのメソッドは第一引数にContextを渡せることを除けば、 これまでのメソッドと同じように利用できます。
ctx := context.Background()
// Ping
{
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel()
if err := db.PingContext(ctx); err != nil {
log.Fatal(err)
}
}
// Exec
{
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
_, err := db.ExecContext(ctx, "select 1;")
if err != nil {
log.Fatal(err)
}
}
// トランザクション
{
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
tx, err := db.BeginTx(ctx, nil)
if err != nil {
log.Fatal(err)
}
defer tx.Rollback()
sqlIns := `INSERT INTO tasks(name, status) VALUES (?, ?);`
if _, err = tx.ExecContext(ctx, sqlIns, "task1", "open"); err != nil {
log.Fatal(err)
}
if err := tx.Commit(); err != nil {
log.Fatal(err)
}
}
Context対応メソッドを使うと、タイムアウトやキャンセルをDB操作へ伝えられます。 ただし、キャンセルがどれだけ速く反映されるかはDBドライバの対応にも依存します。
注意:
defer cancel()は{}ブロックが終了した時点ではなく、外側の関数が返るときに実行されます。 各ブロックのContextをブロック終了直後に確実にキャンセルするには、deferの代わりにブロックの末尾で直接cancel()を呼ぶか、処理を別の関数に切り出してください。