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

时间:2017-07-19 03:41:12

标签: scala reactivemongo

我再次邀请你与我分享你的智慧,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)
    }
  }
}

使用代码初始化连接到db(构造函数的一部分)的读取和删除方法:

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。知道这是邪恶的,通常受到许多鞭打的惩罚,我想学习并在这里找到适当的解决方案。

在尝试其他东西时,我发现而不是等待,完全关闭与数据库的连接也在做伎俩......

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

请注意,当我删除删除功能中的读取调用时,一切正常。同样通过关闭连接,我的意思是从我的测试中调用MongoDriver上的close,并停止并重新开始嵌入我正在后台使用的Mongo。

感谢帮助人们。

1 个答案:

答案 0 :(得分:2)

警告:这是一个盲目的猜测,我对Scala上的MongoDB没有经验。

您可能忘记了flatMap

看看这一点:

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

由于您的代码的收集时间为Future[BSONCollection],而remove会返回Future[WriteResult] per doc,因此此表达式的实际类型为Future[Future[WriteResult]]

现在,您已将函数注释为返回Future[Unit]。 Scala经常通过丢弃可能有意义的值来使Unit作为返回值,这在你的情况下会这样做:

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"