Работа с асинхронным поведением NodeJS

77
4

Использование NodeJS с MongoDB + Mongoose.

Прежде всего, я знаю преимущества асинхронного неблокирующего кода. Поэтому я занимаюсь обратными вызовами. Но, наконец, я столкнулся со следующей проблемой.

Допустим, у меня есть функция, которая может быть вызвана пользователем в любое время. И возможно, что супер "молниеносный" пользователь вызывает его дважды почти одновременно.

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
})
}

Конечно, он выполняется следующим образом: НАЙТИ ЗАПРОС, НАЙДИТЕ ВОПРОС, СОХРАНЯЙТЕ ВОПРОС, СОХРАНЯЙТЕ ВОПРОС

Это полностью нарушает логику приложения (если НАЙТИ ВОПРОС, СОХРАНЯЙТЕ ВОПРОС, НАЙДИТЕ ВОПРОС, СОХРАНЯЙТЕ ВОПРОС). Поэтому я решил предотвратить асинхронное поведение, "заблокировав" всю функцию для конкретного пользователя (поэтому внутри кода функции все равно async).

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.

спросил(а) 2021-01-19T18:05:14+03:00 6 месяцев, 1 неделя назад
1
Решение
79

Это очень плохая практика, вы никогда не должны использовать таймеры для управления потоком кода.

Проблема здесь называется атомарностью. Если вам нужно найти find-save, find-save, вам нужно как-то упаковать эти операции (транзакцию). Это зависит от используемого вами программного обеспечения. В redis у вас есть команды multi и exec. В mongodb вы найдетеAndModify(). Другое решение - использовать индекс. Когда вы пытаетесь сохранить одно и то же поле дважды, вы получаете сообщение об ошибке. Используйте атрибуты "index: true" и "unique: true" в schemaType в mongoose:

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

Это то, что вам нужно: Mongodb - Изолировать последовательность операций - выполнить двухфазные фиксации. Но учтите, что mongodb не может быть лучшим выбором, если вам нужно совершить много транзакций.

ответил(а) 2021-01-19T18:05:14+03:00 6 месяцев, 1 неделя назад
45

Вы не хотите тратить ОЗУ, поэтому замените

lock_function_for_user[user_id] = false

с


delete lock_function_for_user[user_id]

Кроме того: вы можете просто быть оптимистом и повторять попытку, если произойдет конфликт. Просто оставьте блокировку и убедитесь, что DB замечает, когда что-то идет не так (и повторите попытку в этом случае). Конечно, какой способ лучше зависит от того, насколько часто такие конфликты действительно происходят.

ответил(а) 2021-01-19T18:05:14+03:00 6 месяцев, 1 неделя назад
Ваш ответ
Введите минимум 50 символов
Чтобы , пожалуйста,
Выберите тему жалобы:

Другая проблема