進行中の操作をキャンセルする
Goのcontext.Contextを使って、進行中の操作を管理できます。Contextは標準的なGoのデータ値で、それが表す全体的な操作がキャンセルされ、もはや不要になったかどうかを報告できます。context.Contextをアプリケーション内の関数呼び出しやサービス全体に渡すことで、処理が不要になったときに、それらは早期に作業を停止してエラーを返すことができます。Contextの詳細については、Go Concurrency Patterns: Contextを参照してください。
たとえば、次のようなことをしたい場合があります。
- 完了に時間がかかりすぎるデータベース操作を含む、長時間実行される操作を終了する。
- クライアントが接続を閉じるなど、他の場所からのキャンセル要求を伝播する。
Go開発者向けの多くのAPIには、Context引数を取るメソッドが含まれており、アプリケーション全体でContextを簡単に使用できるようになっています。
タイムアウト後のデータベース操作のキャンセル
Contextを使用して、操作がキャンセルされるタイムアウトまたは期限を設定できます。タイムアウトまたは期限付きのContextを導出するには、context.WithTimeoutまたはcontext.WithDeadlineを呼び出します。
次のタイムアウト例のコードは、Contextを導出し、それをsql.DBのQueryContextメソッドに渡します。
func QueryWithTimeout(ctx context.Context) {
// Create a Context with a timeout.
queryCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
// Pass the timeout Context with a query.
rows, err := db.QueryContext(queryCtx, "SELECT * FROM album")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
// Handle returned rows.
}
この例でqueryCtxがctxから導出されているように、あるコンテキストが外側のコンテキストから導出されている場合、外側のコンテキストがキャンセルされると、導出されたコンテキストも自動的にキャンセルされます。たとえば、HTTPサーバーでは、http.Request.Contextメソッドはリクエストに関連付けられたコンテキストを返します。そのコンテキストは、HTTPクライアントが切断するか、HTTPリクエストをキャンセルした場合(HTTP/2で可能)にキャンセルされます。上記のQueryWithTimeoutにHTTPリクエストのコンテキストを渡すと、全体的なHTTPリクエストがキャンセルされた場合、またはクエリに5秒以上かかった場合のどちらでも、データベースクエリが早期に停止します。
注: タイムアウトまたは期限付きで新しいContextを作成したときに返されるcancel関数の呼び出しは、常にdeferしてください。これにより、囲んでいる関数が終了したときに、新しいContextが保持していたリソースが解放されます。また、queryCtxもキャンセルされますが、関数が返される時点では、queryCtxを使用しているものは何もありません。