𝗚𝗼 𝗖𝗼𝗻𝘁𝗲𝘅𝘁 𝗣𝗮𝗰𝗸𝗮𝗴𝗲
多くのGo開発者は、context.Context を十分に理解せずに使用しています。HTTPハンドラー、データベース呼び出し、SDKのメソッドなどでよく目にします。初心者の多くは、どこにでも context.Background() を渡してしまいがちです。
この間違いは問題を引き起こします。Lambda関数がタイムアウトを超えてハングアップしたり、ユーザーが切断した後もデータベースクエリが実行され続けたりすることがあります。
Contextを使用すると、コードを通じてキャンセル信号やデッドライン(期限)を送信できます。これにより、次の3つの問いに答えることができます。
• この操作を継続すべきか? • この操作はいつまでに終了しなければならないか? • この呼び出しと共にどのようなリクエストデータが流れるか?
Context インターフェースには4つのメソッドがあります。
Deadline(): コンテキストがキャンセルされる時刻を返します。Done(): コンテキストがキャンセルされたときにクローズされるチャネルを返します。作業を停止するために、select文の中でこれを使用します。Err(): コンテキストが停止した理由(DeadlineExceededまたはCanceled)を返します。Value(): Trace ID のようなリクエストスコープのデータを取得します。
Contextはツリー構造として機能します。親コンテキストから開始し、子コンテキストを作成していきます。
ルートコンテキスト:
context.Background(): プログラムの開始時に使用します。context.TODO(): リファクタリング中のプレースホルダーとして使用します。
子コンテキスト:
context.WithCancel(): 手動で作業を停止できるようにします。context.WithTimeout(): 指定した時間が経過した後に作業を停止します。context.WithDeadline(): 指定した時刻に作業を停止します。context.WithValue(): User ID などのメタデータを渡します。
重要なルール: 必ず cancel 関数を呼び出してください。子コンテキストを作成したら、すぐに defer cancel() を使用します。これを怠ると、メモリリークの原因になります。
ベストプラクティス:
- コンテキストは関数の第一引数として渡す。
- コンテキストを構造体に保存しない。
context.WithValueはメタデータのみに使用し、データベースクライアントのような依存関係には使用しない。- 長いループ内では
ctx.Err()をチェックして、早期に終了させる。 - キーの衝突を避けるため、コンテキストキーにはカスタム型を使用する。
親コンテキストがキャンセルされると、そのすべての子コンテキストも自動的にキャンセルされます。これにより、システム全体の操作チェーン全体を簡単に停止させることができます。