为什么在等待删除调用完成后需要 Thread.sleep 或关闭连接?

Why a Thread.sleep or closing the connection is required after waiting for a remove call to complete?

我再次寻求你与我分享你的智慧,scala padawan!

我在 scala 中使用反应式 mongo,当我使用 scalatest 编写测试时,我遇到了以下问题。

首先是代码:

"delete" when {
  "passing an existent id" should {
    "succeed" in {
      val testRecord = TestRecord(someString)
      Await.result(persistenceService.persist(testRecord), Duration.Inf)

      Await.result(persistenceService.delete(testRecord.id), Duration.Inf)
      Thread.sleep(1000) // Why do I need that to make the test succeeds?

      val thrownException = intercept[RecordNotFoundException] {
        Await.result(persistenceService.read(testRecord.id), Duration.Inf)
      }
      thrownException.getMessage should include(testRecord._id.toString)
    }
  }
}

读取和删除方法以及初始化数据库连接的代码(构造函数的一部分):

class MongoPersistenceService[R](url: String, port: String, databaseName: String, collectionName: String) {
  val driver = MongoDriver()
  val parsedUri: Try[MongoConnection.ParsedURI] = MongoConnection.parseURI("%s:%s".format(url, port))
  val connection: Try[MongoConnection] = parsedUri.map(driver.connection)
  val mongoConnection = Future.fromTry(connection)
  def db: Future[DefaultDB] = mongoConnection.flatMap(_.database(databaseName))

  def collection: Future[BSONCollection] = db.map(_.collection(collectionName))
  def read(id: BSONObjectID): Future[R] = {
      val query = BSONDocument("_id" -> id)

      val readResult: Future[R] = for {
        coll <- collection
        record <- coll.find(query).requireOne[R]
      } yield record

      readResult.recover {
        case NoSuchResultException => throw RecordNotFoundException(id)
      }
  }
  def delete(id: BSONObjectID): Future[Unit] = {
    val query = BSONDocument("_id" -> id)

    // first read then call remove. Read will throw if not present
    read(id).flatMap { (_) => collection.map(coll => coll.remove(query)) }
  }
}

所以为了让我的测试通过,我必须在等待删除完成后立即进行 Thread.sleep。知道这是邪恶的,通常会受到许多鞭打的惩罚,我想在这里学习并找到合适的解决方法。

在尝试其他东西时,我发现与其等待,不如完全关闭与数据库的连接也能解决问题...

我在这里误解了什么?是否应该为每次调用打开和关闭与数据库的连接?并没有通过一个连接进行许多操作,例如添加、删除、更新记录?

请注意,当我删除删除函数中的读取调用时,一切正常。另外通过关闭连接,我的意思是从我的测试中调用关闭 Mongo 驱动程序,并停止并重新开始嵌入我在后台使用的 Mongo。

感谢大家的帮助。

警告:这是盲目的猜测,我没有在 Scala 上使用 MongoDB 的经验。

您可能忘记了 flatMap

看看这个位:

collection.map(coll => coll.remove(query))

由于集合是 Future[BSONCollection] 根据您的代码和 remove returns Future[WriteResult] per doc,所以此表达式的实际类型是 Future[Future[WriteResult]].

现在,您已将函数注释为 returning Future[Unit]。 Scala 通常通过丢弃可能有意义的值来使 Unit 成为 return 值,在您的情况下它就是这样做的:

read(id).flatMap { (_) =>
  collection.map(coll => {
    coll.remove(query) // we didn't wait for removal
    ()                 // before returning unit
  })
}

所以你的代码应该是

read(id).flatMap(_ => collection.flatMap(_.remove(query).map(_ => ())))

或者一个for-理解:

for {
  _    <- read(id)
  coll <- collection
  _    <- coll.remove(query)
} yield ()

您可以通过添加编译器标志(假设 SBT)让 Scala 警告您丢弃的值:

scalacOptions += "-Ywarn-value-discard"