如何在同时调用涉及依赖于读操作的写操作的函数时缓解竞争条件

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

我有一个名为Ledger的简单集合,具有以下模式

Ledger: {
  account: String,
  amount: Number // actually an integer
}

我有一个功能,根据该帐户剩余的余额借记帐户:

const debit = async (account_to_be_debited, amount_to_be_debited) => {
  let transactions = await Ledger.find({ account: account_to_be_debited })
  
  let balance = transactions.reduce((accumulated_balance, transaction) => {
    accumulated_balance += transaction.amount
    return accumulated_balance
  }, 0)
    
  if (balance < amount_to_be_debited) {
    throw new Error('insufficient funds')
  }
  
  let new_transaction = await Ledger.create({ account: account_to_be_debited, amount: -amount_to_be_debited })
  
  return new_transaction._id
}

现在我希望能够在异步环境中做这样的事情:

// the first debit
debit('account_1', 100).then(id => console.log(id))

// another debit
debit('account_1', 200).then(id => console.log(id))

我担心的是,如果两个借记操作同时发生,则存在双重花费的风险,因为两个借记操作都将检查相同的余额。如果我在执行下一个之前等待一个借记交易完成,这不会是一个问题

let debit_1 = await debit('account_1', 100)
let debit_2 = await debit('account_1', 200)

我知道随着mongodb 4.0的发布,我可以使用事务,但我不确定mongodb是否同步执行事务。如果是,那么我可以确定每个后续事务都会读取最近提交的事务所反映的更改,并且跨事务的读取不会同时发生,因为事务不能同时发生。

所以我的问题是:mongodb交易是否适合我的用例,专门用于减轻我上面描述的竞争条件?

如果没有,我可以采取哪些方式来解决这个问题。我正在节点js中编写这个应用程序,我目前正在使用mongoose来建模并与mongodb副本集进行交互。

任何帮助是极大的赞赏。谢谢。

javascript node.js mongodb transactions race-condition
1个回答
0
投票

Mongo事务通常用于确保跨多个集合(或单个集合!)的一组更新/插入全部成功或全部失败,这听起来不像您正在描述的用例。相反,您希望确保如果有两个更新,其中一个将失败,这是您应该尝试在数据库端处理的(而不是应用程序代码)。

如果更新操作不是有效更新,则可以编写更新操作:db.ledger.updateOne({account: 'account_1', amount: {$gte: 200}}, {$inc: { amount: 200 }})

$gte将确保此操作仅在符合条件的文档上进行,您可以查看更新是否成功。

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