taiyoh's memorandum

@ttaiyoh が、技術ネタで気づいたことを書き溜めておきます。

node-fiberでライフチェンジングとか煽ったことを若干後悔してる

 承前→ node.jsのvmとsynchronizeはライフチェンジング - taiyoh's memorandum
 @hokaccha先生の記事読んで、やっぱインフルエンサーが書くと内容の充実度も反応も全然違うなー、とか思ってたのですが、特にあの記事をぶくましてるけどfiber使ったことない人は、使いどころをちゃんと見極めた方がいい、という注意喚起。もしくは過去の自分の記事に対する懺悔。
 実は以前qiitaにこっそり投稿してたんだけど、以下の処理の時にエラーが発生する。

// sync_test.js
var sync = require('synchronize');

sync.fiber(function() {
  try {
    sync.await((function(defer) {
      console.log("await 1 start");
      setTimeout(function() {
        console.log("await 1 end");
        try {
          sync.await((function(defer2) {
            console.log("await 2 start");
            setTimeout(function() {
              console.log("await 2 end");
              defer2(null, 'ok');
            }, 1000);
          })(sync.defer()));
          console.log("await 2 over");
          defer(null, "ok");
        }
        catch(e) {
          console.log("await 2 error", e);
          defer(e, null);
        }
      }, 1000);
    })(sync.defer()));
  } catch(e) {
    console.log("await 1 error", e);
  }
  console.log("await 1 over");
});

 ずらずら長いが、端的に言えば、sync.await中にもう一回sync.awaitしたらどうなるか、ということを検証したもの。結果は以下のとおりになる。

% node sync_test.js
await 1 start
await 1 end
await 2 error [Error: no current Fiber, defer can'b be used without Fiber!]
await 1 error [Error: no current Fiber, defer can'b be used without Fiber!]
await 1 over

 これだけだとピンとこないかもしれない。じゃあ以下のパターンがあった時は?

  • ちょっと大きめのDBのselect文をsync.awaitで発行してる最中に、別のリクエストでsync.awaitを使ってファイルへの書き込みをする
  • OAuth認証の処理をsync.awaitで発行してる最中に別のリクエストでsync.awaitを使ったselect文を発行する

 他にも色々あると思うけど、ふつーにこういうウェブアプリケーション書いてしまうと、確実にエラーが発生し、「テストでは通ってるけど、本番に上げたらなんかちょくちょくエラーが起きる」という事態が発生する。シングルスレッドでこなすnodeならではの現象だと思う。PerlとかRubyみたいにpreforkしてリクエストを処理したらどうなるかはわからない(そもそもできるのか?このへんは自分はよく知らない)。でも、そういうアプリケーションを書く人は、よっぽどの変態か「自分はJS以外の言語を知らないから」という消極的な理由でアプリケーション実装を選択してるか以外にないだろうと思う。
 要は、プロセス中のシーケンスが単一であればいい。なので、バッチスクリプトを書くときなんかは、すごく効果的なんじゃないかと思う。バッチ実装で非同期とか考えないでいい筈なのにコールバック地獄とか、ただの面倒事でしかないので。