如何在 MongoDB/Mongoose 中原子更新具有锁定条件的文档,并区分由于锁定而未找到与文档不存在?

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

我正在使用 Mongoose 来管理 MongoDB 集合,其中包含表示事务的嵌入式文档。我需要确保我可以根据特定条件自动更新文档的“锁定”字段。

要求是:

  1. 仅当文档在任何事务中尚未具有排他锁(“X”)时才应更新。
  2. 仅当交易时间戳早于 5 分钟时才应更新文档,以防止死锁。
  3. 如果文档满足这些条件,我想对其设置独占锁(“X”),以便我可以安全地操作目录中的文件。

问题是我需要区分文档没有找到是因为:

  1. 文档不满足锁定条件。
  2. 该文档根本不存在。

如何以原子方式实现这一点,确保一次只发生一个操作并正确处理锁定条件?对代码的任何指导或改进将不胜感激。

这是示例代码:

// Schemas
const mongoose = require('mongoose');
const { Schema, model } = mongoose;

const TransactionSchema = new Schema(
  {
    timestamp: { type: Number, required: true },
    lock: { type: String, trim: true, required: true },
  },
  { timestamps: true }
);

const DirectorySchema = new Schema(
  {
    owner: { type: String, trim: true, required: true },
    transactions: [TransactionSchema],
  },
  { timestamps: true }
);

const DirectoryModel = model('Directory', DirectorySchema);

// Example function
const updateLock = async (req) => {
  const initTransaction = {
    timestamp: Date.now(),
    lock: 'X', // exclusive lock
  };

  try {
    // Check the directory status, if the directory is not busy, then set the exclusive lock
    const doc = await DirectoryModel.findOneAndUpdate(
      {
        owner: req.uid,
        _id: req.params.id,
        'transactions.lock': { $ne: 'X' }, // check there are no Share Lock or Exclusive lock in the transactions
        'transactions.timestamp': { $lte: Date.now() - 300000 }, // prevent deadlock
      },
      {
        $push: { transactions: initTransaction }
      },
      {
        returnOriginal: false,
        upsert: false // ensure it doesn't insert a new document if not found
      }
    ).exec();

    if (!doc) {
      console.log('Document not found or lock condition not met');
    } else {
      // Manipulate the file in the directory
      // do something...
      console.log('Document updated:', doc);
    }
  } catch (error) {
    console.error('Error updating lock:', error);
  }
};

// Example request object
const req = {
  uid: 'user123',
  params: { id: 'documentId123' }
};

// Call the function
updateLock(req);

我需要确保操作是原子的,以防止竞争条件,并正确区分由于锁定条件而找不到文档与文档不存在。我怎样才能实现这个目标?

javascript mongodb mongoose
1个回答
0
投票

这里的问题是存在三种可能性:

  1. 目录中可能不存在该文档
  2. 该文档可能存在于目录中,但处于锁定状态
  3. 该文档可能存在于目录中,但处于解锁状态

更新运算符 findOneAndUpdate 只有两个原子操作:

  1. 如果文档不存在,可以查找并更新或插入

对于不使用事务的原子过程,操作员已经设置了限制。因此,需要在该限制内对数据进行建模。该过程还必须被视为一个整体 - 锁定和解锁。

因此可能有两组操作:

第 1 组:

  1. 使用 findOneAndUpdate
  2. 查找并插入,并将此插入建模为文档锁定
  3. 这里没有更新文档。
  4. 这将在访问文档时执行。

第二组:

  1. 使用 findOneAndDelete
  2. 查找并删除,并将此删除建模为文档已解锁。
  3. 这将在发布文件时执行。这将是另一个类似的过程,但性质相反。

通过这种方式,文档的锁定和解锁将是原子的,无需使用事务。

删除的数据需要记录以供将来参考,这可能需要在删除之前完成。

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