2016-08-02 8 views
1

は、次の点を考慮してバグ:これらは同じノードjsのプロセスから呼び出されFirebase取引

function useCredits(userId, amount){ 
    var userRef = firebase.database().ref().child('users').child(userId); 
    userRef.transaction(function(user) { 

     if (!user){ 
      return user; 
     } 
     user.credits -= amount; 
     return user; 


    }, NOOP, false); 
} 

function notifyUser(userId, message){ 

    var notificationId = Math.random(); 
    var userNotificationRef = firebase.database().ref().child('users').child(userId).child('notifications').child(notificationId); 
    userNotificationRef.transaction(function(notification) { 

     return message; 

    }, NOOP, false); 
} 

ユーザーは次のようになります。

{ 
    "name": 'Alex', 
    "age": 22, 
    "credits": 100, 
    "notifications": { 
    "1": "notification 1", 
    "2": "notification 2" 
    } 
} 

私は私のストレステストを実行すると、私はuserRefトランザクション更新関数に渡さ時々ユーザーオブジェクトはそれだけで、次のされて完全なユーザーではないことに注意してください:

{ 

    "notifications": { 
    "1": "notification 1", 
    "2": "notification 2" 
    } 
} 

これは明らかにuser.creditsが存在しないためにエラーが発生します。

userRefトランザクションの更新機能に渡されたユーザーオブジェクトが、userNotificationRefトランザクションの更新機能によって返されたデータと同じであることが疑わしいです。

これはなぜですか?この問題は、ユーザーの親の場所で両方のトランザクションを実行すると消えますが、効率的にロックしてユーザーオブジェクト全体を読み取るので、これはあまり最適ではありません。

答えて

3

私の経験では、トランザクション更新関数に渡される初期値に頼ることはできません。データがデータストアに移入されたとしても、関数はnull、部分的な値、または古くなった古い値(飛行中のローカル更新の場合)で呼び出されることがあります。これは、偽の更新が拒否され、トランザクションが再試行されるため、関数を書くときに防御的なアプローチをとっている限り、通常は問題ではありません。

データが意味をなさないためトランザクションを終了すると(undefinedを返す)、それはではなく、がサーバーに対してチェックされ、再試行されません。このため、決してトランザクションを中止することはお勧めしません。私はmonkey patchを作成してこの修正プログラム(およびその他のもの)を透過的に適用しました。それはブラウザのみですが、ノードに簡単に適応させることができます。

もう少し手助けをすることができるもう一つのことは、トランザクションの直前に同じrefにon('value')コールを挿入し、トランザクションが完了するまでそれを生かしておくことです。これはとなります。は、トランザクションが最初の試行で正しいデータで実行され、帯域幅にあまり影響を与えません(現在の値は送信される必要があるため)。applyLocallyが設定されていれば、またはデフォルト値はtrueです。私のNodeFireライブラリのI do this、他の多くの最適化と調整があります。

上記のうち、SDKのバグはまだありません。再始動するまで間違った基本値が「スタック」し、トランザクションが継続的に再試行されます(maxretryで失敗します)プロセス。

幸運を祈る!私はまだサーバでトランザクションを使用しています。障害が再試行されやすく、複数のプロセスが実行されていますが、クライアント上でそれらを使用することをあきらめてしまっています。私の意見では、トランザクションが不要になるようにデータ構造を再設計する方が良い場合がよくあります。

+0

トランザクションにこのような問題があったことはわかりませんでした... +1。トランザクションを必要とせずにこの "値を増やす"操作をどのように再設計することを提案しますか? – qxz

+0

詳細な対応をいただき、ありがとうございます。トランザクションは多少壊れているようです - 私の意見では、クライアントはこれらのすべての考慮事項について心配する必要はありません。あなたは、銀行口座の残高がファイアーベースでの取引を使わずにモデル化されていることをどのように示唆しますか?トランザクションを使用してこれを処理する堅牢な方法がない場合は、これは深刻な制限だと感じています。別の問題は、ある値をトランザクション的に更新し、同時に他の場所を原子的に更新できることです。 – user3391835

+0

これはすべて難しい経験であり、* hard *を重視しています。上記の注意点を念頭に置いている限り、トランザクションは単純に増分しても問題ありません。もう1つの方法は、ログに "+ N"エントリを押し込んで、サーバが定期的にトランザクションをロールアップさせる方法です。クライアントは、最後に集計された値とログを読み取り、その場で現在の値を計算することができます。ディープ・アトミック・アップデートは可能ですが、トランザクショナルにすることはできません。ログベースのデザインは、あなたの友人です。 – Piotr