Mongodb 中的动态原子更改,避免竞争条件

问题描述 投票:0回答:1

我们希望保留这些对象的用户定义顺序。例如:

class Book:
    id: ObjectId
    order: int


class ClubBookRanking:
    books: list[Book]

在您提出建议之前,依赖列表顺序在这里不起作用。它只是转移问题。

我们将这些对象存储在 mongo 中。这里我们需要执行两种操作。

  1. 添加新书:这应该附加到列表的末尾(包含新项目时顺序应等于列表长度)
  2. 用户重新订购:书籍必须更改其订单值,并且其之后的所有书籍也必须更新其订单。

这些情况都容易受到竞争条件的影响。我的本能是尝试在复杂的原子事务中完成这些事情。我假设 mongo 将顺序执行原子事务,因此将避免竞争条件(ala redis)。

有人知道处理这个问题的最佳方法吗?我们确实使用 Atlas,所以我们可以使用它们的功能。

编辑: 这些书有一个 uuid

mongodb atomic
1个回答
0
投票

为了详细说明乔所说的内容,您确实可以从数组中元素的保证顺序和文档级别更新的原子性中受益。

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。

现在由您决定如何解决冲突 - 您可以通过获取新修订版并尝试以编程方式将重新排序重新应用到最新修订版来做一些聪明的事情,或者通过它返回给用户,告诉用户其他人更新了订单比他们快,并要求审核并重新订购。

© www.soinside.com 2019 - 2024. All rights reserved.