contextの使い方とか概念(contextとは)的な話sellGo,ConTeXt,ctxなにこれ?golang使ってるとよく出会うことになるであろう、context.Contextの使い方とcontextのgodocの自分的な和訳。登場する割にあまり使わないので忘れがちなので、大雑把に調べ上げたものを載せとく。(2回忘れたので観念したcontextとはblog.golang.org/contextの紹介が一番わかりいいかなぁという気もするのでそのまま使います。request.(略Goのサーバーで入ってきた各リクエストは個別のgoroutineで処理される。リクエストのハンドラは、データベースやRPCのサービスにアクセスする。リクエスト上で動作するgoroutineはユーザー識別子や認証トークン、リクエストの期限みたいな特定のリクエストの値にアクセスする必要がある。リクエストがタイムアウトされた時、システムがそのリソースを再度利用できるように、すべての動作しているゴルーチンも素早く終了されるべき。Googleではそんなリクエストスコープな値とかキャンセルシグナル,デッドラインやらをリクエストに関係する全てのゴルーチンに渡せるcontextパッケージを開発したよ。packageはデッドラインやキャンセルのシグナル、その他のリクエストスコープな値をAPIの境界を超えてプロセス間で届けるContext型を定義している。サーバーに入ってくるリクエストはContextを作成するべきだし、サーバーへの発信呼び出しはContextを受け入れるべき。リクエスト着信〜レスポンス発信の間の連鎖する関数の呼び出しの間、Contextは伝播されるべき。必要があれば、WithCancel,WithDeadLine,WithTimeout,WithValueを使うことで派生するContextに置き換えても良い。親のContextがキャンセルされた時、全ての派生先のContextもまたキャンセルされる。受け取ったContextから派生させた子のContextと、CancelFuncを返します。派生元(parent)のContextからのchildContextへの参照を取り外し、関連するタイマーをストップします。親がキャンセルされるか、タイマーが発火されるまで、その子と子孫がリークすることになります。vetツールはCancelFuncが全てのコントロールフローでCancelFuncが使われていることをチェックしてくれます。Contextを使うプログラムは、contextの伝播をチェックする静的解析が可能にしパッケージをまたいだインターフェースの一貫性の維持をするために、以下のルールを守る必要がある。構造体の中には、Contextを保持してはならない。Contextが必要な関数には明示的に渡す事。Contextは第1引数であるべきで、だいたいはctxと名付けてるよ。関数側が許容するとしても、nilのContextを渡してはいけない。どのコンテキスト渡して良いか確証が持てない時は、Context.TODOを使って空のContextを渡してね。ContextのValueはAPIやプロセスをまたぐリクエストスコープな値だけに使う。オプショナルな値を関数に渡すためではない。同じContextは別々に実行されているgoroutineで関数渡しても良い。Contextは複数のgoroutineから同時に使われても安全である。大元の処理がキャンセルされたら、派生されたゴルーチンの処理もキャンセルしようね、って話。使い方まとめると、要するに主なユースケースは以下の通りになります。interfacegoblogの説明を見ると大雑把に以下な感じ。interface{}}Contextがキャンセル、タイムアウトされた時に閉じられるチャネルを返すDone()Doneチャネルが閉じられた時になぜ閉じられたか(キャンセルされたか)をerror構造体でとり出せるErr()DeadLineが設定されていればDeadLineを返すDeadLine()Keyに対応した保管値を返すValue()が提供されてる。以下の2つの関数で生成可能です。他にhttp.Request.Context()でRequestからも受け取れる。Background()基本的にはこちらを利用。context.Background()TODO()説明文を見た感じ、使うか使わないかわからないにしてもnilは渡してほしくないからそういう場合は明示的にTODOで空のcontextを作ろうね、っていう説明がなされています。動作的にはbackgroundと同じく空のcontextを返します。context.TODO()リクエストスコープの値の伝播なんでも入れられるからといって、なんでも入れるとただのカオスな運び屋になってしまうので送るものはgoblog例にあがってたみたいな最小(AuthとかDeadLineみたいな)に留めた方が良いかなぁとよく思ってたりしますが、そこらへんはまぁお好みというか決めで。使い方はシンプルです。WithValue()値のセットは以下のように行う。Value()値のGetは以下の通り。ctx.Value(key)キャンセル処理キャンセル処理とはなんぞや、という感じだと思うので、関数の紹介をしつつ簡単なコードを。CancelFunc)親となるContextを渡すと、子のContextとキャンセル用の関数を作ってくれる。第2返り値のcancelFuncで返された子のContextをcancelできる。CancelFunc)timeoutの指定付きのWithCancel。第2引数で指定した時間が経過とcancelが走る。CancelFunc)基本的にWithTimeoutと同じ。違いは指定した時間が経過ではなく指定した時刻になったらなだけ。サンプルcontextInterfaceのDone()とcancelを使うと以下な感じに処理がキャンセルできる。//10秒まってキャンセル。childCtxから派生したchildCtx2もキャンセルが伝播される。構造体の中には、Contextを保持してはならないここの理由が抜けていたのですが、各Contextに基づく処理(キャンセル・値取得)はリクエストスコープのみに影響範囲がとどまるべきである。各リクエストで共有されるようなグローバルスコープの構造体に保持する事によって意図せず別のリクエストによる副作用を受ける可能性があるので、そもそも保持しない事でそれを回避したほうが良い、という事です。