Node.jsのstream.Writableへの書き込みは送信バッファが完全に空になるまで待たなくてもよい
MongoDBからデータを読み出して、少し加工して、streamに書き込んでいく処理がある この時ボトルネックは、出口つまりインターネット側の速度である
どんどん送信しようとすると
実際には送信されない
Node.jsの送信buffer内に、送信待ちデータとして溜まっていく
code:js
async function writeLine(line) {
while (!streamClosed && res.writableLength > 0) { // 送信バッファが空になるまで待機
console.log('writableLength', res.writableLength)
await delay(10)
}
res.write(line)
res.write('\n')
}
これはリンク先で過去の私が書いている通り、8bitマイコン的な考えであるshokai.icon 8bitマイコン的とは
送信バッファが1byteしかない環境
送信バッファが空ではない場合は、送信バッファへ書き込まずに待つべき
完全に空になっていない状態でも書き込んでよい
writable.writableNeedDrainというフラグがtrueになる
超えた状態で書き込むと
変更内容
ここを
code:js
while (!streamClosed && res.writableLength > 0) { // 送信バッファが空になるまで待機
console.log('writableLength', res.writableLength)
await delay(10)
}
bufferが少しでも溜まっていたら、10 msec待機していた
こうした
code:js
while (!streamClosed && res.writableLength > res.writableHighWaterMark * 0.7) {
console.log('writableLength', res.writableLength)
await delay(10)
}
buffer最大容量の7割を超えていたら、10 msec待機
buffer容量7割までは書き込むようにした
ここで言う「書き込む」はbufferに書き込むだけ
インターネットへの送信はしていない
bufferから読み出してインターネット側に出ていく処理は、Node.jsが非同期的に処理してくれる
効果
1回に書き込むデータ量が大きい場合、それほどパフォーマンスに問題は感じなかった
問題あるのは1回に書き込むデータ量が小さい場合
これまでは、TCPの1パケットに小さな1 chunkしか入っていない
効率が悪い
1回の送信で複数のchunkがまとめて送信されるようになったはず
この値は既にbufferが既に溢れている時にtrueになる
溢れてから待つか
溢れそう(7割)で待つか
正直どっちでも変わらないと思う
結局、ボトルネックは出口であるインターネット側の速度
溢れてから一定時間待っても、溢れる前に一定時間待っても、どちらにせよ送信bufferが詰まっている
どちらも詰まった状態で送信bufferに溜めるのは、もうやってる
一度にbufferにwriteするデータのサイズ次第だと思う