2

MongoDB + Mongoose で NodeJS を使用する。

まず、非同期ノンブロッキング コードの利点を知っています。だから私はコールバックを扱います。しかし、最終的に次の問題に直面しました。

ユーザーがいつでも呼び出すことができる関数があるとしましょう。また、超「電光石火」のユーザーがほぼ同時に 2 回呼び出すことも可能です。

function do_something_with_user(user_id){
    User.findOne({_id:user_id}).exec(function(err,user){ // FIND QUERY
        // Do a lot of different stuff with user
        // I just cannot update user with a single query
        // I might need here to execute any other MongoDB queries
        // So this code is a set of queries-callbacks
        user.save() // SAVE QUERY
    })
}

もちろん、次のように実行されます: FIND QUERY、FIND QUERY、SAVE QUERY、SAVE QUERY

これは、アプリのロジックを完全に壊します (FIND QUERY、SAVE QUERY、FIND QUERY、SAVE QUERY のはずです)。そこで、特定のユーザーに対して関数全体を「ロック」することで、非同期動作を防ぐことにしました (そのため、関数コード内はまだ非同期です)。

var lock_function_for_user = {}

function do_something_with_user(user_id){
    if(!lock_function_for_user[user_id]){
        lock_function_for_user[user_id] = true
        User.findOne({_id:user_id}).exec(function(err,user){
            // Same code as above
            user.save(function(){
                lock_function_for_user[user_id] = false
            })
        })
    } else {
        setTimeout(function(){
            do_something_with_user(user_id)
        },100) // assuming that average function execution time is 100ms in average
    }
}

それで、私の質問は次のとおりです。それは良い習慣ですか、良いハックですか、それとも悪いハックですか? それが悪いハックである場合は、他の解決策を提供してください。特に、複数の NodeJS プロセスをスケーリングして起動する場合、このソリューションが機能するとは思えません。

4

2 に答える 2

2

これは非常に悪い習慣です。タイマーを使用してコードのフローを制御しないでください。

ここでの問題は原子性と呼ばれます。検索保存、検索保存を行う必要がある場合は、これらの操作を何らかの方法でパックする必要があります (トランザクション)。使用するソフトウェアによって異なります。redis には multi および exec コマンドがあります。mongodb には findAndModify() があります。別の解決策は、インデックスを使用することです。同じフィールドを 2 回保存しようとすると、エラーが発生します。mongoose の schemaType で属性「index: true」と「unique: true」を使用します。

var schema = mongoose.Schema ({
    myField: { type: String, index: true, unique: true, required: true },
});

これはあなたが必要とするものです: Mongodb - 一連の操作を分離 - 2 フェーズ コミットを実行します。ただし、多くのトランザクションを実行する必要がある場合、mongodb は最良の選択ではないことを考慮してください。

于 2013-03-28T23:23:52.050 に答える
0

RAMを無駄にしたくないので、交換してください

lock_function_for_user[user_id] = false

delete lock_function_for_user[user_id]

それとは別に、競合が発生した場合は、楽観的で再試行できます。ロックを省略して、問題が発生したときにDBが気付くようにしてください(その場合は再試行してください)。もちろん、どちらの方法が良いかは、そのような競合が実際にどのくらいの頻度で発生するかによって異なります。

于 2013-03-28T23:22:41.737 に答える