No.61: 不適切なコンテキストの伝搬
与えられたコンテキストの伝搬が期待しない影響を及ぼす場合があるため、コンテキストの伝搬は慎重に行うべき。
たとえば、何らかの処理を行ってレスポンスを返すHTTPハンドラにおいて、一部の処理をゴルーチンを使って非同期に行う場合、先にレスポンスがクライアントに書き戻されると、コンテキストはキャンセルされてしまい、非同期処理もキャンセルされてしまう。
親コンテキストを伝搬させたくない場合、代わりに、空コンテキストを渡す。
キャンセルは伝搬したくないが、値は渡したい場合(例えば、分散トレースで使うトレースIDなど)、独自のGoのコンテキストを実装して渡す。
独自Goコンテキスト
type detach struct { // 初期コンテキストのラッパーとして振る舞う独自構造体
ctx context.Context
}
func (d detach) Deadline() (time.Time, bool) {
return time.Time(), false
}
func (d detach) Done() <- chane struct() {
return nil
}
func (d detach) Err() error {
return nil
}
func (d detach) Value(key any) any {
// 親コンテキストに対してValue呼び出しを委譲する
return d.ctx.Value(key)
}
使う方
err := publish(detach{ctx: r.Context()}, response)
なるほど。伝搬しない方がいい場合もあるんだな。そんなに慎重になったことなかった。