javascript 我可以做些什么来防止mongoose版本错误

5uzkadbs  于 12个月前  发布在  Java
关注(0)|答案(2)|浏览(96)

如何防止此事件请求1:我获取文档A并更改某些内容请求2:我获取文档A并更改其他内容请求1:我保存文档A请求2:我保存文档A现在请求1的更改将被覆盖如何防止此事件
如果这个请求被频繁地调用,则更改不会被保存,这只是一个示例,我在应用程序中有更复杂的代码,

exports.readInfo = async (req, res, n) => {
  const user = req.user;
  const data = req.data;
  const doc = await Doc.findOne({ _id: new ObjectId(data._id) });
//changes from request 1 might be saved now
  doc.infos[data.key][data.skey][data.i].read.push(user._id.toString());
  doc.markModified("infos." + data.key + "." + data.skey + "." + data.i);
  await doc.save();
  return res.end("success");
};

字符串

7dl7o3gd

7dl7o3gd1#

这更像是一个系统设计问题,因为JavaScript并没有内置任何东西(我知道)来处理它。
您的问题基本上是一个竞态条件,如下所述:https://stackoverflow.com/a/34550/8346513
简而言之,两个进程试图同时修改同一段数据,哪一个进程获胜有点随意,很难确定。
有几种方法可以处理这个问题。

在资源上使用互斥或锁。

这是防止此类问题发生的一种简单方法。它看起来像这样:

const locks = {};

exports.readInfo = async (req, res, n) => {
    const key = // generate key somehow from your request
    // wait for there to be no lock. This may require a timeout or something of the like

    locks[key] = true; // acquire the lock, now we own the lock and nothing else can take actions on this resource

    // do our actions

    locks[key] = false; // release the lock when done
}

字符串
这是相对便宜的,因为JavaScript是单线程的,在这种情况下工作得很好。但是它也有一些缺点,即你必须在每个可能使用这个资源的地方都有相同的锁(这可能是很多样板),而且如果你的进程在工作过程中崩溃,它永远不会释放锁,这可能会导致你的应用程序死锁。

使用历史队列处理更改

这与上面的例子类似,在上面的例子中,一次只能修改一个东西。但是现在不是像这样把那个东西放在你的API中,你会有一个通过以下步骤运行的系统:

  • 将要进行的更改排入队列
  • 定期运行一个进程,从队列中提取更改并写入它们。

这可以确保只有一个地方写入数据,并且需要的样板文件要少得多。然而,这也有一些问题。即设置要复杂得多,并且所有操作都将使用过时的数据运行。
你也可以使用一个更简单的内存缓存层来保存数据的实时副本。如果你修改了它,然后将更改的内容排队,那么你就可以更新数据而不需要执行code-c,这意味着你的请求不再需要阻塞,而是定期将数据发送到数据库。这也很复杂,但确实解决了上面的一些问题,而且如果程序崩溃,也会冒着没有完全写入所有数据的风险。

结论

如你所见,这不是一件容易解决的事情。我推荐使用简单的互斥锁,因为对于一个不需要完美的应用程序来说,它可能是最容易启动和运行的东西。但是如果你正在构建一个企业级应用程序,那么研究一下其他方法或其他东西可能会很好(可能是现成的解决方案),以确保尽可能多的基地覆盖。

8e2ybdfx

8e2ybdfx2#

你可以使用这个解决方案

db.coll.findAndModify({ status : "unlocked" },{$set : { status : "locked"}});

字符串
但在状态为锁定时不返回任何内容
为了更好的解决方案,你需要在应用层实现锁功能(我认为Mongodb没有实现这一点,但Postgres做到了)
简单的例子

import { Types } from "mongoose";

 export class Locking {
  locked_documents = new Set<Types.ObjectId>();
  pending_documents = new Map<Types.ObjectId, NodeJS.Timeout[]>();
  removed_intervals: NodeJS.Timeout[] = [];

  INTERVAL_TIME = 1000;
  TIME_OUT_COUNT = 60;
  
  add(_id: Types.ObjectId) {
    this.locked_documents.add(_id);
  }
  
  remove(_id: Types.ObjectId) {
    this.locked_documents.delete(_id);
    const pending_document_list = this.pending_documents.get(_id);

    if(pending_document_list) {
      const pending_document = pending_document_list.shift();
      if(pending_document) this.removed_intervals.push(pending_document);
      this.locked_documents.add(_id);
    }
  }
  
  async delay(_id: Types.ObjectId) {
    const promise = new Promise<void>((resolve, reject) => {
      let count = 1;
      const timeout = setInterval(() => {
        if(count === this.TIME_OUT_COUNT) {
          reject("Time out");
          return;
        }
        console.log("Count", count);
        const removedIntervalIndex = this.removed_intervals.findIndex((t) => t === timeout);
        if(removedIntervalIndex > -1) {
          clearInterval(timeout);
          this.removed_intervals.splice(removedIntervalIndex, 1);
          console.log("Resolved. Count: ", count);
          resolve(undefined);
        }
        count++;
      }, this.INTERVAL_TIME);

      const pending_document_list = this.pending_documents.get(_id);
      if(pending_document_list) {
        pending_document_list.push(timeout);
      } else {
        this.pending_documents.set(_id, [timeout]);
      }
    });

    return promise;
  }
}
  
const locking = new Locking();
  
const startTime = Date.now();
  
const doc1 = {
  _id: new Types.ObjectId(),
  name: "Name1",
};
  
  
async function test() {
  locking.add(doc1._id);
    
  setTimeout(() => {
    doc1.name = "Updated1";
    locking.remove(doc1._id);
  }, 10000);

  await locking.delay(doc1._id);

  console.log("Waited", Date.now() - startTime, doc1.name);

  locking.add(doc1._id);

  setTimeout(() => {
    doc1.name = "Updated2";
    locking.remove(doc1._id);
  }, 1500);

  await locking.delay(doc1._id);

  console.log("Waited", Date.now() - startTime, doc1.name);
  
  locking.add(doc1._id);

  setTimeout(() => {
    doc1.name = "Updated3";
    locking.remove(doc1._id);
  }, 5000);

  await locking.delay(doc1._id);

  console.log("Waited", Date.now() - startTime, doc1.name);
}
  
test();


结果:

Count 1
Count 2
Count 3
Count 4
Count 5
Count 6
Count 7
Count 8
Count 9
Count 10
Resolved. Count:  10
Waited 10013 Updated1
Count 1
Count 2
Resolved. Count:  2
Waited 12016 Updated2
Count 1
Count 2
Count 3
Count 4
Count 5
Resolved. Count:  5
Waited 17023 Updated3

相关问题