[インデックス 12189] ファイルの概要
このコミットは、Go言語の net/rpc
パッケージにおいて、接続終了時に発生する読み込みエラー(read error)を適切に抑制するための修正です。具体的には、クライアントが接続を閉じている最中に io.EOF
エラーが発生した場合に、不必要なエラーログが出力されるのを防ぎます。
コミット
commit 91bdbf591fe08c394f5ea3924774968202cde07b
Author: Russ Cox <rsc@golang.org>
Date: Thu Feb 23 22:45:44 2012 -0500
net/rpc: silence read error on closing connection
Fixes #3113.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5698056
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/91bdbf591fe08c394f5ea3924774968202cde07b
元コミット内容
このコミットは、Go言語の標準ライブラリである net/rpc
パッケージのクライアント側で発生していた、接続終了時の不必要なエラーログ出力を修正するものです。具体的には、クライアントがRPC接続を正常に終了しようとしている際に、基盤となるネットワーク接続からの読み込みで io.EOF
エラーが発生した場合に、それが「プロトコルエラー」として誤ってログに記録されてしまう問題を解決します。この修正により、接続が正常に閉じられる過程で発生する予期された io.EOF
は、エラーとして扱われなくなります。
変更の背景
net/rpc
パッケージは、Go言語におけるリモートプロシージャコール(RPC)を実装するための標準的な方法を提供します。クライアントとサーバー間で通信を行う際、クライアントがRPCセッションを終了し、接続を閉じることは一般的な操作です。
このコミットが修正しようとしている問題は、クライアントが接続を閉じている最中に、基盤となるネットワーク接続からデータを読み込もうとした際に io.EOF
(End Of File) エラーが発生することに起因します。io.EOF
は、通常、ストリームの終端に達したことを示す正常な状態であり、エラーとは異なる文脈で扱われるべきです。
しかし、修正前のコードでは、接続が閉じられている状態 (closing
フラグが true
) であっても、io.EOF
エラーが log.Println("rpc: client protocol error:", err)
によって「プロトコルエラー」としてログに出力されていました。これは、開発者にとって誤解を招く可能性のあるノイズの多いログとなり、実際の異常なプロトコルエラーと区別がつきにくくなるという問題がありました。
この修正の目的は、このような正常な接続終了プロセスの一部として発生する io.EOF
を、エラーとして扱わないようにすることで、ログのノイズを減らし、真のプロトコルエラーの検出を容易にすることです。これは、Issue #3113 で報告された問題に対応しています。
前提知識の解説
net/rpc
パッケージ
net/rpc
はGo言語の標準ライブラリの一部で、ネットワーク経由でリモートプロシージャコール(RPC)を実装するための機能を提供します。これにより、異なるプロセスや異なるマシン上で実行されているプログラム間で、まるでローカル関数を呼び出すかのように関数を呼び出すことができます。
- クライアント (Client): リモートの関数を呼び出す側。
- サーバー (Server): リモートからの関数呼び出しを受け付け、実行する側。
- エンコーディング: デフォルトではGoの
encoding/gob
パッケージを使用してデータのシリアライズ/デシリアライズを行います。 - トランスポート: TCPなどのネットワークプロトコル上で動作します。
net/rpc
クライアントは、通常、サーバーへの接続を確立し、その接続を通じてRPCリクエストを送信し、レスポンスを受信します。接続が不要になった場合、クライアントは接続を閉じます。
io.EOF
io.EOF
は、Go言語の io
パッケージで定義されている特別なエラー変数です。これは、入力ストリームの終端(End Of File)に達したことを示します。ファイル読み込み、ネットワークソケットからの読み込み、その他のストリーム処理において、読み込むべきデータがこれ以上存在しない場合に返されます。
io.EOF
は、多くの場合、エラーというよりも「正常な終了条件」として扱われます。例えば、ファイルを最後まで読み込んだ場合や、ネットワーク接続が相手側から正常に閉じられた場合などに io.EOF
が返されます。したがって、io.EOF
が返されたからといって、必ずしも異常事態が発生したわけではありません。
Go言語におけるエラーハンドリングの一般的な考え方
Go言語では、エラーは関数の戻り値として明示的に扱われます。慣習として、関数は通常、最後の戻り値として error
型の値を返します。エラーが発生しなかった場合は nil
を返し、エラーが発生した場合は nil
ではない error
値を返します。
エラーハンドリングの基本的なパターンは以下のようになります。
result, err := someFunction()
if err != nil {
// エラー処理
// 例: log.Printf("Error: %v", err)
return err
}
// 正常処理
このコミットの文脈では、io.EOF
が返された場合に、それが本当に「プロトコルエラー」としてログに記録されるべきなのか、それとも正常な接続終了の一部として無視されるべきなのか、という判断が重要になります。
技術的詳細
このコミットの技術的な核心は、論理演算子の変更にあります。具体的には、src/pkg/net/rpc/client.go
ファイル内の input()
メソッドで、エラーログ出力の条件を制御する if
文が変更されました。
変更前:
if err != io.EOF || !closing {
log.Println("rpc: client protocol error:", err)
}
変更後:
if err != io.EOF && !closing {
log.Println("rpc: client protocol error:", err)
}
この変更は、論理OR演算子 (||
) を論理AND演算子 (&&
) に置き換えるものです。この変更がもたらす意味は以下の通りです。
-
変更前 (
||
) の挙動:err != io.EOF
がtrue
の場合(つまり、io.EOF
以外のエラーが発生した場合)、if
文の条件全体がtrue
となり、ログが出力されます。err == io.EOF
であっても、!closing
がtrue
の場合(つまり、接続が閉じられていない場合)、if
文の条件全体がtrue
となり、ログが出力されます。- 問題は、
err == io.EOF
かつclosing
がtrue
の場合(つまり、接続が閉じられている最中にio.EOF
が発生した場合)にのみ、ログが出力されないという点でした。それ以外の場合、特にio.EOF
が発生してもclosing
がfalse
であればログが出力されていました。しかし、この文脈ではclosing
がtrue
の時にio.EOF
が発生した場合にログが出力されてしまうことが問題でした。
-
変更後 (
&&
) の挙動:if
文の条件がtrue
となるのは、両方の条件がtrue
の場合のみです。- つまり、
err != io.EOF
(エラーがio.EOF
ではない) かつ!closing
(接続が閉じられていない) の場合にのみ、ログが出力されます。 - これにより、以下のシナリオではログが出力されなくなります。
err == io.EOF
の場合(io.EOF
エラーが発生した場合)。この場合、最初の条件err != io.EOF
がfalse
となるため、&&
演算子の性質上、条件全体がfalse
となり、ログは出力されません。これは、接続が閉じられている最中であろうとなかろうと、io.EOF
はプロトコルエラーではないという判断に基づいています。closing
がtrue
の場合(接続が閉じられている最中の場合)。この場合、二番目の条件!closing
がfalse
となるため、条件全体がfalse
となり、ログは出力されません。
この修正により、接続が正常に閉じられる過程で発生する io.EOF
エラーや、接続が閉じられている最中に発生するあらゆる読み込みエラーが、不必要な「プロトコルエラー」としてログに記録されることがなくなります。これは、ログのノイズを減らし、開発者が真の異常なプロトコルエラーに集中できるようにするために重要です。
コアとなるコードの変更箇所
--- a/src/pkg/net/rpc/client.go
+++ b/src/pkg/net/rpc/client.go
@@ -140,7 +140,7 @@ func (client *Client) input() {
}\n \tclient.mutex.Unlock()\n \tclient.sending.Unlock()\n-\tif err != io.EOF || !closing {\n+\tif err != io.EOF && !closing {\n \t\tlog.Println(\"rpc: client protocol error:\", err)\n \t}\n }\
コアとなるコードの解説
変更されたコードは、net/rpc
クライアントの input()
メソッド内にあります。このメソッドは、RPCサーバーからのレスポンスを非同期的に読み取る役割を担っています。
元のコード:
if err != io.EOF || !closing { ... }
この条件文は、「エラーが io.EOF
ではない」または「接続が閉じられていない」場合に、"rpc: client protocol error:"
というメッセージをログに出力していました。
問題は、接続が閉じられている最中 (closing
が true
) であっても、もし err
が io.EOF
以外の何らかのエラーであればログが出力される、という点でした。また、io.EOF
が発生した場合でも、closing
が false
であればログが出力されていました。特に、接続が正常に閉じられる過程で io.EOF
が発生することは予期される動作であり、これを「プロトコルエラー」としてログに出力するのは適切ではありませんでした。
修正後のコード:
if err != io.EOF && !closing { ... }
この条件文は、「エラーが io.EOF
ではない」かつ「接続が閉じられていない」場合にのみ、ログを出力するように変更されました。
この変更により、以下のいずれかの条件が満たされれば、ログは出力されなくなります。
err == io.EOF
の場合:io.EOF
はストリームの終端を示すものであり、接続が正常に閉じられたことを意味する場合があるため、これをプロトコルエラーとして扱うべきではありません。closing == true
の場合: クライアントが意図的に接続を閉じている最中であれば、その過程で発生する読み込みエラー(io.EOF
を含む)は、プロトコルエラーではなく、正常なシャットダウンプロセスの一部と見なされます。
したがって、この修正は、net/rpc
クライアントが接続を正常に終了する際に発生する、予期された io.EOF
や、接続終了プロセス中の読み込みエラーを「プロトコルエラー」として誤ってログに記録するのを防ぎます。これにより、ログのノイズが減り、開発者は真の異常なプロトコルエラーに集中できるようになります。
関連リンク
- Go CL (Change List): https://golang.org/cl/5698056
- GitHub Issue: #3113 (このコミットが修正したとされるIssue番号)
参考にした情報源リンク
- https://github.com/golang/go/commit/91bdbf591fe08c394f5ea3924774968202cde07b
- Go言語の
net/rpc
パッケージに関する公式ドキュメント (一般的な情報源として) - Go言語の
io
パッケージに関する公式ドキュメント (特にio.EOF
について) - Go言語のエラーハンドリングに関する一般的な慣習とドキュメント