大体の人が何を言ってるの?ってなると思うけど、このissueに端を発するもの。
oxfmt: Large files truncated when using
--stdin-filepathoption · Issue #17939 · oxc-project/oxc https://github.com/oxc-project/oxc/issues/17939
いまだに完全に理解できてるかわからんので、とりあえず書き残しておく。
何が起きていたか
端的には、こういうケースで問題が起きてた。
cat large.js | oxfmt --stdin-filepath=a.js | wc -l
stdinでコードを読み込んで、フォーマットして、stdoutに書き出し、それを消費する後続のコマンドがある状況。
これ自体は想定されたユースケースで、問題はないはず。
しかし、ファイルがそこそこ大きい場合に、以下のエラーが起きてた。
`Err` value: Os { code: 35, kind: WouldBlock, message: "Resource temporarily unavailable" }
そしてどうやら、前ではなく後のパイプがある時だけ起きるらしく、その結果としてフォーマット済みコードが途中までしか出力されないらしい。
事態を複雑にしてるのは、このoxfmtはただのJS CLIでもRust CLIでもなく、Rustで書いたコードをJSからN-API経由で動かしてるってところ。
元issue
あれこれ試すうちに見つけたのがこのissue。
failed printing to stdout: Resource temporarily unavailable (os error 35)on MacOS · Issue #1630 · napi-rs/napi-rs https://github.com/napi-rs/napi-rs/issues/1630
まさに!という内容だった。
これによると、
- Node.jsは
stdoutとstderrをデフォルトでnon-blockingにしてる- これは歴史的な経緯らしい
isTTYの場合はblockingになったそう
- 一方で
napi-rs、つまりRustとしては、普通にstdoutに書こうとするとき、それはblockingだと仮定されてる - しかし実際に実行されるJSはnon-blockingなので、上流と下流で流量が調整できずエラー
というわけ。
どうする
もっとも簡単なワークアラウンドは、napi-rsのissueにもある通りこれ。
process.stdout._handle.setBlocking(true);
process.stderr._handle.setBlocking(true);
ただし、Node.jsのDocsにもあるように、
Warning: Synchronous writes block the event loop until the write has completed. https://nodejs.org/api/process.html#a-note-on-process-io
というわけで、パフォーマンスを損ねる場合もあるから注意せよとのこと。
!process.stdout.isTTYを見て、影響を最小限にするほかない・・・?
WouldBlockエラーが出たときに、手動でリトライするという手もあるかもしれんけど、根本解決ではないしな〜・・・。
追記: 20250115
ことはそう単純でもないらしい。
Stdio from child process with inherit is lost · Issue #1464 · napi-rs/napi-rs https://github.com/napi-rs/napi-rs/issues/1464#issuecomment-3435119771