🧊

napi-rsで大量データをstdoutに流してパイプすると途中で切れる

大体の人が何を言ってるの?ってなると思うけど、このissueに端を発するもの。

oxfmt: Large files truncated when using --stdin-filepath option · 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

まさに!という内容だった。

これによると、

というわけ。

どうする

もっとも簡単なワークアラウンドは、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