Будущее, возвращаемое методом Slick run, успешно завершается до вставки строк - PullRequest
2 голосов
/ 27 марта 2019

Я работаю в приложении Scala, используя Slick и PostgreSQL для хранения. У меня есть метод foo, который читает данные из файла CSV и вставляет около 10000 строк в базу данных. После завершения действия Future of the insert строки вызывается другой метод bar, который извлекает эти строки из базы данных и выполняет с ними некоторые действия. Вот в чем проблема: на самом деле из базы данных не извлекаются строки, так как к моменту завершения Future строк не было вставлено.

Из того, что я мог собрать, ища ответ и в официальной документации, Future не должен завершаться до успешного выполнения оператора вставки. Если я добавлю следующий код Thread.sleep(30000) к bar, позволяя сначала выполнить оператор вставки, методы обеспечат ожидаемый результат. Теперь по понятным причинам я бы предпочел не делать этого, поэтому я ищу альтернативы.

Следующая диаграмма иллюстрирует ход выполнения программы после вызова начального метода doStuff:

The program flow diagram

doStuff вызывает foo, который загружает данные и сохраняет их в базе данных перед возвратом в будущее. В doStuff это Будущее затем отображается и делается вызов bar. bar бар извлекает строки из базы данных и обрабатывает их. Однако, поскольку в точке, где вызывается bar, не было вставлено ни одной строки, данные не обрабатываются.

Метод doStuff:

def doStuff(csvFile: File): Future[Unit] = {
  fooService.foo(csvFile)
    .map(_ => {
      csvFile.delete()
      barService.bar()
    })
}

Метод foo:

def foo(file: File) Future[Unit] = {
  val reader = CSVReader.open(file)
  fooStorage.truncateFooData().map(_ => {
    val foos = for (line <- reader.iterator if
    line.head != "bad1" &&
      line.head !="bad2")
      yield parseFooData(line)
    fooStorage.saveFooDataBulk(foos.toSeq)
  })
}

Как я использую Slick для вставки строк:

override def saveFooDataBulk(fooSeq: Seq[Foo]): Future[Seq[Foo]] =
  db.run(DBIO.seq(fooQuery ++= fooSeq)).map(_ => fooSeq)

Я ожидаю, что bar будет вызван, как только все строки будут вставлены в базу данных, и не раньше, однако, в настоящее время Future from Slick завершает работу слишком рано. В случае, если это уместно: метод doStuff вызывается при отправке запроса в конечную точку HKK Akka. Приложение и база данных работают в двух разных контейнерах Docker. Что я делаю не так?

Также я не могу понять, насколько подходящим является имя пользователя, которое я выбрал полтора года назад.

1 Ответ

2 голосов
/ 27 марта 2019

Заменить map на flatMap в def foo.В противном случае он начнет Future[Seq[Foo]], а затем сразу вернет (), который затем будет сброшен вашим doStuff.Примерно так:

fooStorage
  .truncateFooData()
  .flatMap(_ => {
    /* stuff... */
    fooStorage.saveFooDataBulk(foo.toSeq)
  })
  .map(_ => ())

Я не проверял это, но в любом случае, начинать несколько Future с в середине другого Future.map и затем немедленно возвращать () нечувствую себя совершенно правильно.

...