我正在使用anorm / postgres在scala / play中编写代码,以便根据用户配置文件生成匹配项。以下代码有效,但我已经注释掉导致问题的部分while
循环。我注意到在运行它时前三个Futures似乎同步工作但问题出现在我在第四步中检索表中的行数时。
第四步在上述插入实际发生之前返回计数。据我所知,步骤1-3正在为postgres同步排队,但是检索计数的调用似乎在完成前3个步骤之前返回,这对我没有意义。如果前3个步骤以正确的顺序排队,为什么第四步不会等到插入发生后才返回计数?
当我取消注释while循环时,将调用匹配生成和插入函数,直到内存耗尽,因为返回的计数持续低于所需的阈值。
我知道格式本身是低于标准的,但我的问题不是如何编写最优雅的scala代码,而只是如何让它现在正常工作。
def matchGeneration(email:String,itNum:Int) = {
var currentIterationNumber = itNum
var numberOfMatches = MatchData.numberOfCurrentMatches(email)
while(numberOfMatches < 150){
Thread.sleep(25000)//delay while loop execution time
generateUsers(email) onComplete {
case(s) => {
print(s">>>>>>>>>>>>>>>>>>>>>>>>>>>STEP 1")
Thread.sleep(5000)//Time for initial user generation to take place
genDemoMatches(email, currentIterationNumber) onComplete {
case (s) => {
print(s">>>>>>>>>>>>>>>>>>>>>>>>>>>STEP 2")
genIntMatches(email,currentIterationNumber) onComplete {
case(s) => {
print(s">>>>>>>>>>>>>>>>>>>>>>>>>>>STEP 3")
genSchoolWorkMatches(email,currentIterationNumber) onComplete {
case(s) => {
Thread.sleep(10000)
print(s">>>>>>>>>>>>>>>>>>>>>>>>>>>STEP 4")
incrementNumberOfMatches(email) onComplete {
case(s) => {
currentIterationNumber+=1
println(s"current number of matches: $numberOfMatches")
println(s"current Iteration: $currentIterationNumber")
}
}
}
}
}
}
}
}
}
}
//}
}
匹配函数定义为期货,例如:
def genSchoolWorkMatches(email:String,currentIterationNumber:Int):Future[Unit]=
Future(genUsersFromSchoolWorkData(email, currentIterationNumber))
genUsersFromSchoolWorkData(email:String)
遵循与其他两个相同的形式。这是一个函数,它最初获取用户在其配置文件中填写的所有学校/工作字段(SELECT major FROM school_work where email='$email'
),并生成一个dummyUser,其中包含与{{1}的此用户共同的字段之一}。打印此功能需要大约30-40行代码,以便我可以根据需要进一步解释。
我已经编辑了我的代码,到目前为止我发现的唯一方法是使用email:String
进行黑客攻击。我认为问题可能在于吵闹
因为我的Future逻辑结构确实像我预期的那样工作,但问题在于写入发生时与读取返回的不一致。 Thread.sleep()
函数返回匹配数,因为它是一个简单的numberOfCurrentMatches(email:String)
。问题是有时在插入23个匹配后,count返回0,然后在第二次迭代后返回46.我假设onComplete()将绑定到用SELECT count(email) from table where email='$email'
定义的基础anorm函数,但显然它可能太远了,无法完成这一任务。我现在还不确定要研究什么或进一步研究以试图解决这个问题,而不是编写一个单独的监督函数来返回接近150的值。
更新
感谢用户的建议,并尝试通过此链接了解Scala的文档:Scala Futures and Promises
我已经将我的代码更新为更具可读性和可扩展性:
DB.withConnection()
我仍然在很大程度上依赖 def genMatchOfTypes(email:String,iterationNumber:Int) = {
genDemoMatches(email,iterationNumber)
genIntMatches(email,iterationNumber)
genSchoolWorkMatches(email,iterationNumber)
}
def matchGeneration(email:String) = {
var currentIterationNumber = 0
var numberOfMatches = MatchData.numberOfCurrentMatches(email)
while (numberOfMatches < 150) {
println(s"current number of matches: $numberOfMatches")
Thread.sleep(30000)
generateUsers(email)
.flatMap(users => genMatchOfTypes(email,currentIterationNumber))
.flatMap(matches => incrementNumberOfMatches(email))
.map{
result =>
currentIterationNumber += 1
println(s"current Iteration2: $currentIterationNumber")
numberOfMatches = MatchData.numberOfCurrentMatches(email)
println(s"current number of matches2: $numberOfMatches")
}
}
}
来提供足够的时间来尝试再次循环之前运行while循环。它仍然是一个笨拙的黑客。当我取消注释Thread.sleep(30000)
我在bash中的输出如下所示:
Thread.sleep()
这当然是截断输出。它一遍又一遍地运行,直到我收到太多打开文件的错误,并且JVM / play服务器完全崩溃。
答案 0 :(得分:2)
一种解决方案是将Future.traverse
用于已知的迭代计数
暗示
object MatchData {
def numberOfCurrentMatches(email: String) = ???
}
def generateUsers(email: String): Future[Unit] = ???
def incrementNumberOfMatches(email: String): Future[Int] = ???
def genDemoMatches(email: String, it: Int): Future[Unit] = ???
def genIntMatches(email: String, it: Int): Future[Unit] = ???
def genSchoolWorkMatches(email: String, it: Int): Future[Unit] = ???
您可以编写类似
的代码def matchGeneration(email: String, itNum: Int) = {
val numberOfMatches = MatchData.numberOfCurrentMatches(email)
Future.traverse(Stream.range(itNum, 150 - numberOfMatches + itNum)) { currentIterationNumber => for {
_ <- generateUsers(email)
_ = print(s">>>>>>>>>>>>>>>>>>>>>>>>>>>STEP 1")
_ <- genDemoMatches(email, currentIterationNumber)
_ = print(s">>>>>>>>>>>>>>>>>>>>>>>>>>>STEP 2")
_ <- genIntMatches(email, currentIterationNumber)
_ = print(s">>>>>>>>>>>>>>>>>>>>>>>>>>>STEP 3")
_ <- genSchoolWorkMatches(email, currentIterationNumber)
_ = Thread.sleep(15000)
_ = print(s">>>>>>>>>>>>>>>>>>>>>>>>>>>STEP 4")
numberOfMatches <- incrementNumberOfMatches(email)
_ = println(s"current number of matches: $numberOfMatches")
_ = println(s"current Iteration: $currentIterationNumber")
} yield ()
}
如果你每次都要求检查一些条件,一种方法是使用来自scalaz library的酷酷的monadic事物。它为scala.Future
定义了monad,因此当我们想要
例如StreamT.unfoldM
可以创建条件monadic(异步)循环,即使我们不需要生成集合的元素,我们仍然可以将它用于迭代。
让我们定义你的
def generateAll(email: String, iterationNumber: Int): Future[Unit] = for {
_ <- generateUsers(email)
_ <- genDemoMatches(email, iterationNumber)
_ <- genIntMatches(email, iterationNumber)
_ <- genSchoolWorkMatches(email, iterationNumber)
} yield ()
然后迭代步骤
def generateStep(email: String, limit: Int)(iterationNumber: Int): Future[Option[(Unit, Int)]] =
if (MatchData.numberOfCurrentMatches(email) >= limit) Future(None)
else for {
_ <- generateAll(email, iterationNumber)
_ <- incrementNumberOfMatches(email)
next = iterationNumber + 1
} yield Some((), next)
现在我们的结果函数简化为
import scalaz._
import scalaz.std.scalaFuture._
def matchGeneration(email: String, itNum: Int): Future[Unit] =
StreamT.unfoldM(0)(generateStep(email, 150) _).toStream.map(_.force: Unit)
看起来同步方法MatchData.numberOfCurrentMatches
正在对incrementNumberOfMatches
内的异步修改做出反应。请注意,通常它可能会导致灾难性的结果,您可能需要将该状态移到某些actor或类似的内容