2014年4月20日日曜日

データ・ロギング・システムの検討(socket::send関数の戻り値について)

半年ほど前にちょっと検討したことがあります。
その時わかったこととして、socketの処理時間(特に受信時間?)が随分かかるのと、ファイルキャッシュが強力で、受信する方はかなりの容量受信してしまうが、本格的にプログラムを作って動かしたときの挙動が心配なことがわかったところで、そのまま放置していました。(特にまだ切羽詰まった必要性がなかったもんで)

なんとなくsocketまわりのコードを見ていたら、ちょっとsend関数の挙動について気になることがでてきました。
send関数のman見ると、以下の説明がでてきます。

ssize_t send(int sockfd, const void *buf, size_t len, int flags);

これまでリターンでは"-1"しか気にしてませんでしたが、よくみると「送信できたバイト数を返す」とあります。
これって”必ずしも要求した送信データをすべて1回のAPIコールでは送信しきれないこともあるよ”っていうことです。これまで経験したことないんですが、ここで考えているようなsocket通信に負荷が高いことをやろうとする場合は考えとかないといけないんじゃない?と気づきました。そこんところ注意するなら、以下のようにコーディングすべきです。

while (送信したいバイト数 > 0) {
  送信済バイト数 = send(  );
  送信したいバイト数 -= 送信済バイト数
}

みたいな感じでwhile-loopを組むべきです。
しかし更に問題として、ここでそもそも「一度に送信できない状況って何?」ということがあります。
このsocketが実装された当時は単純にCPUの処理速度やらまわりのHWの性能が遅くて、途中でも制御を返すようにしていたと考えられますが、現在の様にHWの性能が格段に進歩していて、それでも要求されたバイト数を送りきれない、としたら以下の状況が考えられます。

  1. 送信側のバッファが一杯になった
  2. 上記が起きる原因として、受信側の処理が間に合わなくなりAckを十分返しきれてない

従って、単純にwhile-loopで待ってても大丈夫なのか?ということになります。それにあまりsend()で負荷をかけるとkernelが不機嫌になる(暴走する)ことも起きそうで、そうなるとsend自体がerrorで返されることも考えられます。

そもそもデータ・ロギング・システムを作ろうと考えたとき、ロギングデータの抜けを許すか?ということを頭から考えてませんでした。抜けを許さない状況なら、エラーに対処するコード(sendがerrorを返したときの復旧コード)を考えるだけ無駄です。実際、現状自分が考えている用途は試験データの収集を想定しており、途中でログ・データの抜けがあったら試験失敗になり、再試験となります。従ってエラーがでない限りのロギング可能なデータ量が事前にわかっている必要があります。

とりあえず、sendの戻り値は監視するようにしとかないといけませんね。(現状は、エラーの"-1"しかチェックしないことがほとんどです)
もし送りきれない状況が起きたら、システムとしてロギング可能量をオーバーしたのか見極めないと。

あと、現状Webサーバー等でこの種の大量のデータをSocketで送受信しているシステムだと、CPU負荷の問題でもこのような送信不可の状況が一時的に発生することが考えられます。そういうシステムの場合は、抜けが発生したらそれはいさぎよく諦めて再送信してね、という対応になるでしょうから、上記while-loop内に更にtime-outに関する実装も必要になってくるでしょう。

0 件のコメント:

コメントを投稿