Go の context パッケージとは何か

この記事はフラー Advent Calendar 2020 - Adventar の17日目の記事です。 16日目は shmokmt さん で 「MySQL の Generated Columnsについて」でした。


私がGoを勉強し始めたときに独特な概念だなと思ったのがcontextパッケージでした。

今日はそのcontextパッケージについてまとめようと思います。

※ この記事は先日、社内勉強会で発表した資料の簡単な書き直しになります。

context を使うと何が嬉しいのか

  • リクエストスコープなデータにアクセスしやすくなります
  • goroutineのリソースの開放漏れやタイムアウト処理が実装しやすくなります

context の定義

type Context interface {
   // コンテキストがキャンセルされたらチャネルをクローズ
   Done() <- chan struct{}
   // コンテキストがキャンセルされた理由
   Err() error
   // 時間が設定されている場合は時間を教えてくれる
   Deadline() (deadline time.Time, ok bool)
   // Value(key interface{}) interface{}
}
  • Context はgorotine safeで任意の数のgoroutineにContextを渡すことができます
  • Context の親は子をキャンセルできるが、子は親をキャンセルすることができます

タイムアウトの処理(例)

func main() {
    ctx := context.Background()
    ctx, cancel := context.WithTimeOut(ctx, 3 * time.Second)
    defer cancel()

    go doHeavyProcess(ctx context.Context) {
        for {
                fmt.Println(ctx.Deadline())
                time.Sleep(1 * time.Second)
         }
}

リクエストスコープなデータへのアクセス

WithValue(parent Context, key, val interface{}) Context
Value(key interface{}) interface{}

context はキャンセル/タイムアウト処理だけでなく、リクエストスコープなデータにアクセスする手段も提供します。

ここでいうリクエストスコープなデータというのは、ユーザID, 認証情報(token), トレースID

などのことを指しています。

API Client, DB driver, Loggerなどのリクエストスコープでないことが自明であるものは含めないようにしましょう。

値の取得と設定は色んなで実装してしまうと混乱してしまうので、set/get用の関数を定義して呼ぶことが推奨されています。

type firebaseTokenKeyType struct{}
var firebaseTokenKey firebaseTokenKeyType

func WithFirebaseToken(ctx context.Context, token string) context.Context {
   return context.WithValue(ctx, firebaseTokenKey, token)
}

func GetFirebaseToken(ctx context.Context string {
    // inteface{} が返ってくるので、型アサーションしてあげる
    v, ok := ctx.Value(firebaseTokenKey).(string)
    if !ok {
            return ""
    }
    return v
}

所感

  • ctx は関数/メソッドの第一引数に書く
  • 活用することでgoroutineの開放漏れを防ぐことができる
  • タイムアウト処理はtimeパッケージではなく、contextパッケージを活用するとGoっぽさが出る
  • ctxに保存する情報は認証情報やユーザーIDなどのcredentialsなど(リクエストスコープなデータに限る)
  • ctxのget/set は関数を定義して、それを呼び出して利用するとコードが荒れない
  • setする関数を呼ぶ場所は HTTP Middlewareあたりでやると良さそう

合わせて読みたい

Junpei Tsuji さんのブログの解説が丁寧で非常に参考になりました。

ありがとうございます!

明日は @yudai_watanabe さんで 「リモートワーク下のスクラム開発で気をつけたこと」です。お楽しみに!