go の channel 制御の学び
0時に寝て2時に起きて5時に起きて6時に起きた。久しぶりに夢をみたような気がする。
go の channel とクローズ制御⌗
rabbitmq/amqp091-go を使った pubsub の consumer の実装で次のような for ループでメッセージを取得していた。この場合 deliveries
の channel が閉じられると内側の for ループが終了して、外側の for ループの接続処理へ遷移する。
for {
// コネクションの接続処理
for d := range deliveries {
// メッセージ処理
}
}
この処理を context を使ってキャンセルできるよう、次に書き換えた。channel の Receive operator のドキュメントによると、channel は2値を返すことができて、戻り値の2番目の ok
の値を調べることでその channel がクローズされているかどうかを判定できる。この仕組みを知っていれば select で ok を調べてエラー処理を実装できる。そうしないと deliveries
の channel が閉じられれたときに select では常にゼロ値のメッセージが返るようになって意図したメッセージ処理とならない。
for {
// コネクションの接続処理
for {
select {
case <-ctx.Done():
// context がキャンセルされたら終了処理
if err := s.ch.Cancel(cid, false); err != nil {
return fmt.Errorf("failed to cancel channel: %w", err)
}
return nil
case d, ok := <-deliveries:
if ok {
// メッセージ処理
} else {
// エラー処理
break
}
}
// コネクションが閉じていればループを抜けて再接続
if s.conn.IsClosed() || s.ch.IsClosed() {
log.Info("the session was closed, try to reconnect", nil)
break
}
}
}
同じ channel からメッセージを取り出すループ処理でも用途によって使い分けがあるなぁと今更ながら学んだ。というか、自分でバグを埋め込んで自分で直した (´・ω・`)
マージまで約1ヶ月⌗
先日送った amqp091-go の pr が最終的にはほぼそのままマージされた。約1ヶ月ぐらいの議論やレビューがあって取り込まれた。新しいリリースバージョン (次は 1.9.0?) が出たらうちのアプリケーションでもこの新しいメソッドを使うようにして実績を積ませる。この pr は「あったら便利」程度の機能なのでそれほど重要ではない。
この成功体験をもって次は本命の go-ldap の pr のマージに向けて勢いをつける。こっちの pr は業務に直結しているので本気でやり取りしている。