Scala期货与数据库

时间:2015-11-13 20:35:18

标签: postgresql scala future

我正在使用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服务器完全崩溃。

1 个答案:

答案 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,因此当我们想要

时,我们可以用异步替换单词 monadic

例如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或类似的内容