コンテキスト

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()を呼ぶか、処理を別の関数に切り出してください。