2025/08/09 12:20 A subtle bug with Go's errgroup

やあ、ロボ子。今日はGoの`errgroup`でハマりやすいバグについて話すのじゃ。

博士、こんにちは。`errgroup`ですか、並行処理でよく使うあれですね。どんなバグがあるんですか?

そう、それなのじゃ。パスワード検証プログラムを例に説明するぞ。新しいパスワードが、古いパスワードと違って、漏洩もしてなくて、十分な長さがあるかチェックするプログラムじゃ。

なるほど。パスワードのハッシュ計算とか、結構時間がかかりますもんね。それを`errgroup`で並行処理するんですね。

その通り!でも、ここに落とし穴があるんじゃ。`errgroup`のコンテキストは、エラーが発生しなくても`Wait`関数の戻り時にキャンセルされるんじゃ。

えっ、そうなんですか?ドキュメントをちゃんと読まないと危ないですね。

そうなんじゃ。例えば、HTTPリクエストを`errgroup`の中で実行すると、リクエストが実行されない、なんてことが起こりうるんじゃ。

それは困りますね。どうすればいいんですか?

HTTPリクエストを`errgroup`の外でゴルーチンとして実行するのが一つの手じゃな。あとは、`errgroup.WithContext(ctx)`で作成された子コンテキストが`checkHaveIBeenPawned`に渡されるのを防ぐために、親コンテキストをシャドーしないようにするんじゃ。

コンテキストのシャドーイング、ですか。GoやRustではよくあるバグの原因ですよね。

`errgroup`はチャネルを使うよりコードがシンプルになるし、パフォーマンスも上がるから便利なんじゃけど、挙動をちゃんと理解してないと痛い目を見るぞ。

APIを使うときは、ドキュメントをしっかり読んで、実装を理解することが大切ですね。

その通り!`Wait`関数が最初にエラーを返したとき、または`Wait`が最初に返されたときに、派生コンテキストがキャンセルされる、というのを覚えておくのじゃ。

勉強になります! 博士、ありがとうございました。

どういたしまして。ところでロボ子、パスワードを全部「password」にしたら、並行処理も`errgroup`もいらなくなるぞ。…って、冗談じゃ!
⚠️この記事は生成AIによるコンテンツを含み、ハルシネーションの可能性があります。