逃したvarは私たちの打ち上げを引き裂いた

このメモの翻訳は、読者をNode.jsの使用から遠ざけることを意図したものではなく、老女は穴だらけですが、注意するよう呼びかけるだけで、おそらく、アプリケーションの同様の動作に突然遭遇した人に解決策を伝えるでしょう。 著者の語彙は、大きな変更と検閲なしで残されます。



簡単に言えば、おとぎ話は影響しますが、長い間、 MelonCardTechCrunchで他社と一緒に今日発表されました。突然すべてが壊れました。 それぞれ。 小さなもの。 つまらない。 jQueryテンプレートとKnockoutJSで最もクールな動的フロントエンドを使用して、ロングポーリングNodeJSを使用して、応答性を高めるためにサイトを更新しました。 彼らは最善を尽くし、 Vowsを使用して手動およびユニットテストを実施しました。 すべてのシステムは準備ができており、全速力で進んでいますか? そこにあった。



NodeJSシステムはユーザーの状態を使用します。たとえば、「これらの2つのレコードの更新を期待しています」、サーバー(時間セリフのチェックから開始)は「あなたのレコードは現在」または「xxxレコードはyyyに変更されました」のいずれかを返します(実際、すべてが少し複雑です) Rails、MySQL、Redis、およびNodeを相互接続するための共有Redis変数、セッション、およびその他のセキュリティチェックを使用します)。 すべてが非常に明確ですが、何かがうまくいかない場合、単純なNodeJSコードでさえ地獄に変わる可能性があります。 今日それが起こった。



私たちについての記事が公開された後、幸せなユーザーの流れが私たちに流れました(たとえば、1時間あたり50〜100人の新しいユーザー)。 そして突然、すべてがバラバラになりました。 ページは機能しなくなりました。 メールボックスが不満を抱いたユーザーからのメッセージでいっぱいになり始めました。 私はコーヒーを注ぎ、戦いの準備をしました。



私が最初に考えたのは、NodeJSが負荷を非常によく保持しており、これが彼を有名にしていることです。 50人または100人のユーザーはシステムを捨てることができませんでした。 そして判明したように、それはNodeJSのせいではありませんでした。 サーバーは、ユーザーが「レコードa、b、cを持っている」と言ったように、完全に予期しない回答を返し始め、サーバーは「あなたはバカです、レコードx、y、zを消去しますが、ここではレコードa、b、cがあります」と答えました。 Nodeのひどいエラー処理とデバッグ機能を考えると、問題に集中して再現することは不可能でした。 常に次のコマンドを使用する必要がありました(はい、実稼働サイトで)。



NODE_ENV = 'production'ノード/ privacy.js | grep「返された結果」



この山を解体するのがどれほど大変だったか想像できます。 テストサーバーですべてが正常に機能し続け、すべてのユニットテストが素晴らしく合格し、他に何も開始できなかったことは注目に値します。 さらに、システムはセッションを慎重にチェックし(セキュリティのため)、ユーザーがさまざまなブラウザータブでログインおよびログアウトし、認証されていないという大量の警告を受け取りました(さらに、実際のエラーを遮断することができませんでした)。 このようなエラーが発生しました:



トレース:EventEmitterで。 (/—/Node/privacy.js:118:11)at EventEmitter.emit(events.js:81:20)



ここに記載されている行(ノードによって報告される唯一の行):



process.on( 'uncaughtException'、function(err){console.log(['Caught exception'、err]); console.trace();});



まあ、少なくともアプリケーションはクラッシュしませんでしたが、とにかく-開始するものは何もありませんでした。 私たちがやったこと(インターフェースの手動テスト、単体テスト、エラー処理など)では、この行に関連するエラーは明らかになりませんでした。 はい、負荷テストを使用する必要がありますが、この不運を明らかにするという確実性はありません。

4時間のデバッグ(およびホームページを503-Temporarily Unavailable)に変換した後、共同リーダーが各フラストレーションと好奇心をそそるユーザーに謝罪で答えたが、ユーザーが他のユーザーの要求パラメーターと自分の要求パラメーターを混同していることに気付いた。 率直に言って、サーバーはあなたのリクエストであなたの情報だけを返すように設計され動作しましたが、あなたのリクエストを混乱させました。 つまり、あなたは「私はリンゴとメロンが大好きです」と尋ね、彼は「ナンセンス、あなたはマンゴーが大好きだ」と答えました。 つまり、すべては安全でしたが、それでもなお間違っています。 ExpressJSサーバーが、私が彼に尋ねたものを突然混乱させるのはなぜですか。 私は掘り始め、これを見つけました:



app.all('/apps/:user_id/status', function(req, res, next) {

// …

initial = extractVariables(req.body);

});







悪く見えますか? はい、それは単なる失敗です。 私はJavaScriptの専門家ではありませんが、できることを説明しようと思います。 JavaScriptでは、変数の宣言は関数のコンテキストまたはグローバルコンテキストのいずれかで発生します(現在のコンテキストからグローバルへのコンテキストのネストを通過する際にいくつかの問題が発生します)。 「var」なしで「initial」を作成すると、現在のコンテキストからパッセージが作成され、すぐにグローバルコンテキストに移行し、そこでグローバル変数「initial」を作成しました。 次のリクエストが到着すると、同じパスが再度作成され、同じ変数(前のリクエストがまだ使用しようとしていたものと同じ変数)にデータが記録されました。 そして、その後の各リクエストでそれが起こりました。 サーバーが何らかの処理後にリクエストに応答すると、この絶えず更新される変数から読み取って、妄想的な結果を返しました。 たわごとを完了します。 このようなものを書くことだけが必要でした:



var initial = extractVariables(req.body);







このようなコードは、匿名関数のコンテキストで変数を作成し、別の要求で変数を上書きする方法はありません。 これはアマチュアの間違いでしたが、私だけが適用できるすべてのデバッグとテストに気付かずに合格しました。



そのため、「 CoffeeScriptを使用する必要がありました」と言う必要があります。 そして、あなたは正しいでしょう。 他の状況では、さらに悪化した可能性があります(セッション変数のコンテキストを間違えたらどうなりますか?)。 それに加えて、通常のエラー処理の欠如(Railsではスタックトレースでエラーをキャッチし、電子メールでチームに一意のスタックレースを送信します)と、デバッグの通常の手段(grep以下を除く)がプログラミングが快適ではなかった年に私を連れて行きました事。 または、もっと注意する必要があっただけかもしれません。



サービスを4時間中断し、数百人のユーザーが不満を抱いた後、問題を発見し、生産的なサーバーですぐに修正しました。 雲が散り、鳥がおしゃべりし、太陽が出ました。 私たちは謝罪してユーザーに答え始め、損失を計算し、先へ進みました。 しかし、1つの欠落しているキーワードがもたらした損害から、それはまだ容易ではありません。 1つのvarが見つからなかったために私が不備になる可能性はありますか?



All Articles