У нас есть объекты, для которых мы хотим сохранить заданный пользователем порядок. Например:
class Book:
id: ObjectId
order: int
class ClubBookRanking:
books: list[Book]
Прежде чем вы предложите: полагаться на порядок списка здесь не работает. Это просто меняет проблему.
Мы храним эти объекты в монго. Здесь нам нужно выполнить два типа операций.
Обе эти ситуации уязвимы к условиям гонки. Я инстинктивно пытаюсь сделать это в сложных атомарных транзакциях. Я предполагаю, что mongo будет выполнять атомарные транзакции последовательно, и поэтому можно будет избежать условий гонки (ala redis).
Кто-нибудь знает лучший способ справиться с этим? Мы используем Atlas, поэтому можем использовать их функциональность.
Редактировать: В книгах есть uuid
🤔 А знаете ли вы, что...
MongoDB может использоваться для хранения данных схем свойственных JSON, что удобно для веб-разработки...
Чтобы уточнить то, что сказал Джо, вы действительно можете получить выгоду от гарантированного порядка элементов в массиве и атомарности обновлений на уровне документа.
ClubBookRanking — это единый документ, и все книги упорядочены внутри массива «books».
Все, что вам нужно, это обеспечить отсутствие одновременных обновлений этого документа, что легко исправить с помощью изменений, например. посмотрите, как mongoose реализовал это с помощью versionKey https://github.com/Automattic/mongoose/blob/e359b99e0d1a15669143363855207660aa508fb9/docs/guide.md#option-versionkey
По сути, вы добавляете в ClubBookRanking монотонно увеличивающийся номер версии, который увеличивается при каждом обновлении. В псевдокоде:
const cbr = db.ClubBookRanking.findOne(); //the document
const __v = cbr.__v; //the revision number
reorder(cbr.book); // reshuffle the books
const result = db.ClubBookRanking.updateOne(
{
_id: cbr._id, // filter by unique id
__v: __v // and only when revision didn't change since we read the document
}, {
$set: {books: cbr.books}, // the new order
$inc:{__v:1} // increment the revision
})
if (result.nModified < 1 ) {
throw new Error("Concurrent update")
}
);
Здесь мы пробуем оптимистичное обновление, предполагая, что одновременных обновлений нет, так что updateOne обновляет не более 1 документа. Если было одновременное обновление, __v не будет соответствовать фильтру, и обновлений не произойдет — result.nModified будет равен 0.
Теперь вам решать, как разрешить конфликт — вы можете сделать что-то умное, выбрав новую ревизию и попытавшись повторно применить переупорядочение к последней ревизии программно, или через нее обратно пользователю, сказав, что кто-то другой обновил порядок быстрее. чем они, и попросить просмотреть и изменить заказ.