Проблемы с параллелизмом, обновляющие статистику чужой коллекции

#node.js #mongodb #express #mongoose #concurrency

#node.js #mongodb #экспресс #мангуст #параллелизм

Вопрос:

Я пытаюсь создать систему обозначений для фильмов

Пользователь может отметить фильм в своем списке.

Всякий раз, когда пользователь нажимает на интерфейс, listId, movieId, note они отправляются на сервер для обновления заметки. Примечание может быть установлено в значение null, но это не удаляет запись из списка.

Но если пользователь нажимает слишком много раз, фильмы totalNote и nbNotes полностью ломаются. Похоже, что есть какие-то проблемы с параллелизмом?

Это правильный подход к этой проблеме или я обновляю неправильно?

Схемы мангуста, связанные :

 // Movie Schema
const movieSchema = new Schema({
    // ...
    note: { type: Number, default: 0 },
    totalNotes: { type: Number, default: 0 },
    nbNotes: { type: Number, default: 0 },
})
movieSchema.statics.updateTotalNote = function (movieId, oldNote, newNote) {
    if (!oldNote amp;amp; !newNote) return
    const nbNotes = !newNote ? -1 : (!oldNote ? 1 : 0) // If oldNote is null we  1, if newNote is null we -1
    return Movie.findOneAndUpdate({ _id: movieId }, { $inc: { nbNotes: nbNotes, totalNotes: (newNote - oldNote) } }, { new: true }).catch(err => console.error("Couldn't update note from movie", err))
}

// List Schema
const movieEntry = new Schema({
    _id: false, // movie makes an already unique attribute, which is populated on GET
    movie: { type: Schema.Types.ObjectId, ref: 'Movies', required: true },
    note: { type: Number, default: null, max: 21 },
})

const listSchema = new Schema({
    user: { type: Schema.Types.ObjectId, ref: 'Users', required: true },
    movies: [movieEntry]
})
 

API обновления сервера (добавление / удаление movieEntry аналогично $push и $pull вместо $set )

 exports.updateEntry = (req, res) => {
    const { listId, movieId } = req.params
    const movieEntry = { movieId: movieId, note: req.body.note }
    List.findOneAndUpdate({ _id: listId, 'movies.movie': movieId }, { $set: { 'movies.$[elem]': movieEntry } }, { arrayFilters: [{ 'elem.movie': movieId }] })
        .exec()
        .then(list => {
            if (!list) return res.sendStatus(404)
            const oldNote = list.getMovieEntryById(movieId).note // getMovieEntryById(movieId) = return this.movies.find(movieEntry => movieEntry.movie == movieId)
            Movie.updateTotalNote(movieId, oldNote, movieEntry.note)
            let newList = list.movies.find(movieEntry => movieEntry.movie == movieId) // Because I needed the oldNote and findOneAndUpdate returns the list prior to modification, I change it to return it
            newList.note = movieEntry.note
            newList.status = movieEntry.status
            newList.completedDate = movieEntry.completedDate
            return res.status(200).json(list)
        })
        .catch(err => {
            console.error(err)
            return res.sendStatus(400)
        })
}
 

Ответ №1:

Записи, которые мне нужно было обновить, были массивами, которые могли расти бесконечно, поэтому мне пришлось сначала изменить свои модели и использовать виртуальные и другую модель для записей списка.

Это упростило работу, и я смог создавать, обновлять и удалять записи проще и без каких-либо проблем с параллелизмом.

Это также могло быть не проблемой параллелизма, а проблемой транзакции.