Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Streams APIとTCPフロー制御 / Web Streams API and TCP ...
Search
tasshi
October 31, 2024
Programming
2
450
Streams APIとTCPフロー制御 / Web Streams API and TCP flow control
Nihonbashi.js #9 のLT資料です。
イベント情報はこちら↓
https://nihonbashi-js.connpass.com/event/332328/
tasshi
October 31, 2024
Tweet
Share
More Decks by tasshi
See All by tasshi
2024年のkintone API振り返りと2025年 / kintone API look back in 2024
tasshi
0
220
GitHub Projectsを自動化するGitHub CLIテクニック / Automate GitHub Projects with GitHub CLI
tasshi
0
530
SDK開発チームのプロダクトオーナーが考えていること / Product management of SDK
tasshi
1
1.3k
Node.js製CLIツールのE2Eテストに取り組んでいます / Working on E2E testing of Node.js CLI tools
tasshi
0
1.2k
モダンな開発環境を用いた カンタン/安全なカスタマイズ開発 / kintone devCamp Boost! 2023
tasshi
0
770
Node Streamでメモリ性能改善、そしてWeb Streams APIへ / Improving memory performance of the CLI tool using Node Stream
tasshi
1
3.3k
グローバルチームことはじめ / Bootstrapping a global team
tasshi
1
3.6k
2年目サイボウズ社員とOSS / OSS development of junior engineer in Cybozu
tasshi
0
750
クレートを作ってcrates.ioに公開するまで / How to publishing to crates.io
tasshi
0
700
Other Decks in Programming
See All in Programming
Bedrock Agentsレスポンス解析によるAgentのOps
licux
3
830
AIの力でお手軽Chrome拡張機能作り
taiseiue
0
170
Honoとフロントエンドの 型安全性について
yodaka
5
600
XStateを用いた堅牢なReact Components設計~複雑なClient Stateをシンプルに~ @React Tokyo ミートアップ #2
kfurusho
1
890
バックエンドのためのアプリ内課金入門 (サブスク編)
qnighy
8
1.8k
昭和の職場からアジャイルの世界へ
kumagoro95
1
370
Honoをフロントエンドで使う 3つのやり方
yusukebe
7
3.2k
AWS Lambda functions with C# 用の Dev Container Template を作ってみた件
mappie_kochi
0
240
AWSマネコンに複数のアカウントで入れるようになりました
yuhta28
2
160
Flutter × Firebase Genkit で加速する生成 AI アプリ開発
coborinai
0
150
Writing documentation can be fun with plugin system
okuramasafumi
0
120
ファインディの テックブログ爆誕までの軌跡
starfish719
2
1.1k
Featured
See All Featured
Learning to Love Humans: Emotional Interface Design
aarron
273
40k
What's in a price? How to price your products and services
michaelherold
244
12k
4 Signs Your Business is Dying
shpigford
182
22k
Distributed Sagas: A Protocol for Coordinating Microservices
caitiem20
330
21k
Building Your Own Lightsaber
phodgson
104
6.2k
Designing on Purpose - Digital PM Summit 2013
jponch
117
7.1k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
40
2k
Code Review Best Practice
trishagee
66
17k
Rebuilding a faster, lazier Slack
samanthasiow
80
8.8k
Fight the Zombie Pattern Library - RWD Summit 2016
marcelosomers
233
17k
How GitHub (no longer) Works
holman
313
140k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
21
2.5k
Transcript
Streams APIと TCPフロー制御 2024.10.31 Nihonbashi.js #9 @tasshi_me 1
Stream APIとTCPフロー制御 - Nihonbashi.js #9 はじめに ⽥代雅治 (@tasshi_me) • 仕事
• サイボウズ株式会社 • kintone拡張基盤🧩チーム EM • 好きなもの • ⾃転⾞ 🚲 • ゲーム 🎮 • ⽝ 🐶 • 2024年9⽉に結婚しました💍 ⾃⼰紹介 2
Stream APIとTCPフロー制御 - Nihonbashi.js #9 はじめに • kintoneの拡張機能(プラグイン・連携サービス)向けの 開発基盤を開発・保守するチーム •
API開発 • SDK/CLIなどのOSS提供 など kintone拡張基盤🧩チーム 3
Streams API 4 WHATWGのStreams API (https://streams.spec.whatwg.org/)
Stream APIとTCPフロー制御 - Nihonbashi.js #9 Streams API • データ⼊出⼒を逐次的に、効率よく扱うためのAPI •
データを分割された断⽚(chunk)の連続した流れとして扱う • 概念⾃体はかなり古くからあり、多くの開発⾔語でストリーム操作はあります • 3種類のストリームオブジェクト(ReadableStream, WritableStream, TransformStream) • Stream同⼠をパイプ接続すると、chunkは流れるように終端まで処理される(パイプチェーン) Streams APIの概要 5 Transform Stream Readable Stream Writable Stream chunk データの読み込み データの変換 (読み込み+書き込み) データの書き込み chunk Source Sink
Stream APIとTCPフロー制御 - Nihonbashi.js #9 Streams API • ストリーム(オブジェクト)は未処理チャンクを保持する内部キューを持っている •
パイプチェーン上のストリーム間で処理速度に差がある場合、 内部キューにチャンクが溜まっていくことになる • => メモリを圧迫する︖ ストリームオブジェクト間の処理速度の違いとメモリ使⽤量 6 Transform Stream Readable Stream Writable Stream chunk chunk チャンクが溜まっていく︖ ここの処理が遅い場合 (DBアクセスなど)
Stream APIとTCPフロー制御 - Nihonbashi.js #9 Streams API • チャンクを受け⼊れられない場合、ストリームは上流に停⽌信号を出す •
停⽌信号を受けた上流のストリームはデータの送信を停⽌する • 下流のストリームが送信を指⽰(pull)すると再び処理が再開する => チャンクの流速が調整されてメモリを圧迫せずに処理できる 背圧 (Backpressure) 7 Transform Stream Readable Stream Writable Stream chunk chunk 内部キューが満杯 STOP! 内部キューが満杯 STOP!
Stream APIとTCPフロー制御 - Nihonbashi.js #9 Streams API • 背圧は内部キューの状態から、キューイング戦略に基づいて通知される •
現在は2種類のキューイング戦略が利⽤可能 キューイング戦略と最⾼⽔準点(highWaterMark) 8 キューに格納されたチャンク数で判定 キューに格納されたバイト数で判定 chunk chunk chunk chunk chunk chunk chunk chunk 3 最⾼⽔準点 (highWaterMark) 10KB CountQueuingStrategy ByteLengthQueuingStrategy
メモリリークと調査 9 ※このページ以降のデータ・グラフは再現リポジトリでの情報です。 (https://github.com/tasshi-playground/demo-stream-api-backpressure)
Stream APIとTCPフロー制御 - Nihonbashi.js #9 メモリリークと調査 • Webアプリケーションから全レコードを取得し、加⼯してDBに書き込んでいく • アプリケーションは全レコードをJSONL形式で返すAPIを持つ
• ストリームを使えばメモリに全レコードを載せずに効率よく処理できるはずだが、、、 全レコードへのバッチ処理 10 Text Decoder Stream Response JSON Parser Stream Line Splitter Stream ⾏ごとに 再分割 UTF-8 デコード DBに 書き込み レスポンスの 読み込み Writable Stream DBに 書き込み fetch DB
Stream APIとTCPフロー制御 - Nihonbashi.js #9 メモリリークと調査 • メモリを急速に圧迫した、なぜ︖ メモリリークの発⽣ 11
Stream APIとTCPフロー制御 - Nihonbashi.js #9 メモリリークと調査 • 終端ストリームを置き換え(DB => fs,
setTimeout)=> 効果なし • 中間ストリームを置き換え => 効果なし • 始端ストリームを置き換え(fetch => fs)=> 効果あり 原因箇所の特定 12 Response fetch Text Decoder Stream ここより前に原因がありそう
Stream APIとTCPフロー制御 - Nihonbashi.js #9 メモリリークと調査 • APIレスポンスはJSONL形式 (Content-Type: application/jsonl;
charset=utf-8) • HTTP/1.1 • Transfer-Encoding: chunked • ↑レスポンスはチャンク単位で送られてくる • => この通信に背圧が反映されないとResponse(ReadableStream)にチャンクが溜まるのでは︖ 通信の詳細 13 chunk Server chunk chunk Response STOP! STOP!…できてる︖
BackpressureとTCP通信 14 ※挙動ベースでの推察です (追ってNode.jsのコードを読んで裏付けしようと思います)
Stream APIとTCPフロー制御 - Nihonbashi.js #9 BackpressureとTCP通信 • Server: JSONLを70MB返却するAPI •
Batch Runner: • APIを呼び出し、レスポンスをストリーム処理する • 終端のWritableStreamはチャンクを受け取ると⼀定時間待機する(背圧を発⽣させる) 検証環境 15 chunk Server (Express) Batch Runner chunk chunk HTTP WireShark キャプチャ
Stream APIとTCPフロー制御 - Nihonbashi.js #9 BackpressureとTCP通信 • ウィンドウサイズはある程度⼤きい値を維持 • 受信側のチャンクサイズも⼩さい(受信速度より終端の処理速度が速い)
終端ストリームの遅延時間なしの場合 16 受信側のチャンクサイズ
Stream APIとTCPフロー制御 - Nihonbashi.js #9 BackpressureとTCP通信 • ⼀定期間ごとにウィンドウサイズが急激に⼩さくなっていることが確認できた • チャンクサイズは上限値に張り付いていた
• => 背圧の発⽣がウィンドウサイズに反映されていると考えられる 終端ストリームの遅延時間100msの場合 17
Stream APIとTCPフロー制御 - Nihonbashi.js #9 BackpressureとTCP通信 • Zero Windowが送信されていた •
ZeroWindow: ウィンドウサイズが0(通信⼀時停⽌) • ZeroWindowProveAck: 通信⼀時停⽌中の接続維持のためのACK • TCP Window Update: ウィンドウサイズが更新されて通信再開 • => よりキューの処理に時間がかかっていると考えられる 終端ストリームの遅延時間1000msの場合 18
Stream APIとTCPフロー制御 - Nihonbashi.js #9 BackpressureとTCP通信 • Zero Window送信後、⻑時間に渡って通信再開せず •
=> 内部キューが処理できていないと考えられる 終端ストリームの遅延時間10000msの場合 19
Stream APIとTCPフロー制御 - Nihonbashi.js #9 BackpressureとTCP通信 • Streams APIの背圧はネットワークリクエストにも反映されると考えられる •
ウィンドウサイズが⼩さくなり、受信可能データ量を調整する • それでも処理しきれず受信を⽌める場合は、 サーバーにzero windowパケットが送信される • (HTTP/2での挙動も⾒てみたい) まとめ 20 chunk Server chunk chunk Response STOP! STOP! (zero window)
余談 21 メモリリークの原因はなんだったのか
Stream APIとTCPフロー制御 - Nihonbashi.js #9 22 メモリリークの原因
Stream APIとTCPフロー制御 - Nihonbashi.js #9 メモリリークの原因 • Node.js v22.7.0 (undici
v6.19.7)で修正された (nodejs/undici#3445) • v22ではメモリ使⽤量が300MB付近で横ばい • LTSへのbackport状況 • v20.x => v20.18.0にbackport済み、だが計測結果は× • v18.x => backportの予定は⾒つけられず undiciがメモリリークしていた 23
おわり 24 ご清聴ありがとうございました