【问题标题】:mongoose.connection.collections.collection.drop() throws error every other timemongoose.connection.collections.collection.drop() 每隔一段时间就会抛出错误
【发布时间】:2023-04-03 22:32:01
【问题描述】:

我正在使用 Jest 为 Node/Express/Mongo 项目设置测试。我尝试编写一个函数来清除集合,因此每个测试都从一个干净的状态开始:

const clearCollection = (collectionName, done) => {
  const collection = mongoose.connection.collections[collectionName]
  collection.drop(err => {
    if (err) throw new Error(err)
    else done()
  )
}

beforeEach(done => {
  clearCollection('users', done)
})

又一次尝试,承诺:

const clearCollection = collectionName => {
  const collection = mongoose.connection.collections[collectionName]
  return collection.drop()
}

beforeEach(async () => {
  await clearCollection('users')
})

问题是它们都在工作和抛出错误之间交替。每次我保存文件时,它要么完美运行,要么抛出错误,每次交替。错误总是以下之一:

MongoError: cannot perform operation: a background operation is currently running for collection auth_test.users

MongoError: ns not found

通过让clearCollection()catch() 中调用自身,我可以让它在 100% 的时间内工作(无论如何都会受到堆栈的限制),但这感觉很不对:

const clearCollection = collectionName => {
  const collection = mongoose.connection.collections[collectionName]
  return collection.drop()
    .catch(() => clearCollection(collectionName))
}

【问题讨论】:

  • FWIW,大多数(如果不是全部)异步 MongoDB 方法返回承诺,所以 return collection.drop() 应该足够了。
  • 你是对的,但它仍然会每隔一段时间抛出相同的错误。我会更新我的问题以反映您的建议。
  • 它闻起来有点像在删除操作完成之前解决的承诺(AFAICS,删除集合会锁定数据库,这可以解释您遇到的第一个错误)。我怀疑它会解决任何问题,但是您是否尝试过让beforeEach() 返回一个承诺而不是使用async/await
  • @robertklep 我有,事实上这是我开始使用 async/await 之前的第一次迭代。那时它也没有工作。据我所知,async/await 只是用于执行此操作的语法糖。
  • 你是对的,这就是为什么我怀疑它会解决任何问题;)

标签: node.js mongodb mongoose promise async-await


【解决方案1】:

我不知道为什么mongoose.connection.collections.<collection>.drop() 会随机抛出错误,但是有一种简单的方法可以删除 Mongoose 中的所有文档,这对于在测试之前重置集合非常有效:

beforeAll(async () => {
  await User.remove({})
})

每次都有效。

【讨论】:

  • 哇,好简单!我相信这会对某人有所帮助。
  • 我在开始时尝试清理数据库时遇到了同样的问题,每次其他运行都会生成“数据库正在被删除”。尽管使用了承诺。我尝试使用 .drop() .remove({}) deleteMany({}).. 总是同样的错误
  • 我花了几个小时试图解决这个问题,最后找到了一个替代方案。如果有人知道为什么 drop() 方法会随机抛出错误,请在此处发布。
【解决方案2】:

我在测试开始时尝试删除数据库并在之后立即填充数据库时遇到了类似的问题。在第一次运行中,将创建集合;在下一个中,我会得到一个错误“数据库正在被删除。”; ......它是这样交替的。

我还在使用“内存中”Mongodb,在运行我的 Mocha 测试之前,在单独的终端窗口中运行 $ run-rs -v 4.0.0 -s (https://www.npmjs.com/package/run-rs)。另外,我这里有 Mongoose 5.2.0 和 Mocha 5.1.1。

我发现 Mongoose 不一定会立即执行 drop 命令。相反,它将在连接启动时安排它们。

因此,可能存在一种竞争条件,其中 drop 命令的承诺已解决,并且您继续执行代码,直到到达创建集合的指令......但 drop 命令尚未完成运行,你会得到创建新集合的错误。删除完成运行,下次运行测试时,您的数据库(或集合)已被删除,因此您将能够再次插入新的集合。

所以,我就是这样解决的……

在 before 钩子中运行:

test.dropDb = function(done) {
    this.timeout(0)

    let database = your_MongoDB_URI
    mongoose.connect(database, function (err) {
        if (err) throw err;
        mongoose.connection.db.dropDatabase(function(err, result){
            console.log('database dropping was scheduled');
        });
    })
    mongoose.connection.on('connected', function() {
        setTimeout(function(){ done() }, 3000);
    });
}

在嵌套的 before 钩子中运行

test.createDb = function(done) {
    this.timeout(0)

    let createDb = require('./create-db.js')
    createDb() // here I call all my MyCollections.insertMany(myData)...
    .then( function() {
        done()
    })
    .catch( function(err) {
        assert( null == err)
    });
}

在后钩子中运行

test.afterAll = function(done) {    
    mongoose.connection.close()
    .then( function() {
        done()
    })
}

我不知道为什么我必须在 after 挂钩中明确关闭连接。我想这与run-rs 的编程方式有关,但我没有调查。我只知道,就我而言,在测试后关闭连接是强制性的以获得预期的行为。

【讨论】:

    猜你喜欢
    • 2012-03-02
    • 1970-01-01
    • 2010-10-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多