contextパッケージとは何か
- Context は、APIのサーバーやクライアントを使うときに、コンテキストを提供してキャンセルや、タイムアウト、値を渡したり出来る仕組み
流れ
- (*sql.DB)Begin()でトランザクションを開始
- (*sql.Tx)Commit()でコミット、(*sql.Tx)RollBack()でロールバック
package main
import (
"database/sql"
_ "github.com/go-sql-driver/mysql"
"context"
"time"
)
func main() {
db, _ := sql.Open("mysql", "user:password@tcp(host:port)/dbname")
ctx := context.Background()
ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
defer cancel()
tx, error := db.Begin()
if error != nil {
log.Fatal(error)
return
}
defer tx.Close()
_, err := db.ExecContext(ctx, {Query})
if err != nil {
tx.Rollback()
} else {
tx.Commit()
}
}
- defer cancel() 上記はタイムアウトの実装
- 1秒以上かかる場合はDone
- TimeOutの設定は下記
Microsecond = 1000 * Nanosecond
Millisecond = 1000 * Microsecond
Second = 1000 * Millisecond
Minute = 60 * Second
Hour = 60 * Minute
具体的な利用例からcontextとは何かを説明
- Goの典型的な利用例であるWebアプリケーションを考える
- Goのサーバにおいてリクエストはそれぞれ個別のgoroutineで処理される
- そしてリクエストHandlerは新たなgoroutineを生成しバックエンドのDBや別のサーバにリクエストを投げ結果を得てユーザに対してレスポンスを返す.
最初に注意するべきはその処理に適切なTimeoutやDeadlineを設定して処理が停滞するのを防ぐことである
- 例えば別のサーバにリクエストを投げる場合にネットワークの問題でリクエストに時間がかかってしまうことは大いに考えられる.リクエストにTimeoutを設定して早めにレスポンスを返しリトライを促すべきである.
次に注意するべきは生成したgoroutineを適切にキャンセルしリソースを解放することである.(※Must)
- 例えば別のサーバにリクエストを投げる場合に適切なキャンセル処理を行わないとTimeout後もネットワークリソースが使われ続けることになる(CPUやメモリを使い続けるかもしれない)
- この場合net/httpパッケージレベルでリクエストをキャンセルするべきである.
- さらにそのgoroutineは別のgoroutineを呼び出しそれがまた別の…と呼び出しの連鎖は深くなることが考えられる
- その場合も親のTimeoutに合わせてその子は全て適切にキャンセルされリソースは解放されるべきである
contextパッケージはこのキャンセルのためのシグナルをAPIの境界を超えて受け渡すための仕組みである.ある関数から別の関数へと,親から子へと,キャンセルを伝搬させる.