为什么在等待删除调用完成后需要 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"
我再次寻求你与我分享你的智慧,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"