在Slick事务中处理Akka流

时间:2016-05-06 09:22:25

标签: akka slick akka-stream slick-3.0

软件版本:

  • Akka 2.4.4
  • Slick 3.1.0

我想在Slick事务中处理来自Akka流的元素。 下面是一些简化的代码来说明一种可能的方法:

def insert(d: AnimalFields): DBIO[Long] =
  animals returning animals.map(_.id) += d

val source: Source[AnimalFields, _]
val sourceAsTraversable = ???

db.run((for {
  ids <- DBIO.sequence(sourceAsTraversable.map(insert))
} yield { ids }).transactionally)

到目前为止,我能想出的一个解决方案是阻止每个未来遍历元素:

class TraversableQueue[T](sinkQueue: SinkQueue[T]) extends Traversable[T] {

  @tailrec private def next[U](f: T => U): Unit = {
    val nextElem = Await.result(sinkQueue.pull(), Duration.Inf)
    if (nextElem.isDefined) {
      f(nextElem.get)
      next(f)
    }
  }

  def foreach[U](f: T => U): Unit = next(f)
}

val sinkQueue = source.runWith(Sink.queue())
val queue = new TraversableQueue(sinkQueue)

现在我可以将可遍历队列传递给DBIO.sequence()。但这无法实现流处理的目的。

我找到的另一种方法是:

def toDbioAction[T](queue: SinkQueue[DBIOAction[S, NoStream, Effect.All]]):
        DBIOAction[Queue[T], NoStream, Effect.All] =
  DBIO.from(queue.pull() map { tOption =>
    tOption match {
      case Some(action) =>
          action.flatMap(t => toDbioAction(queue).map(_ :+ t))
      case None => DBIO.successful(Queue())
    }
  }).flatMap(r => r)

使用此方法,可以生成一系列DBIOActions而不会阻塞:

toDbioAction(source.runWith(Sink.queue()))

有没有更好/更实用的方法来达到理想的效果?

1 个答案:

答案 0 :(得分:0)

这是我对sourceAsTraversable的实现:

import scala.collection.JavaConverters._
def sourceAsTraversable[A](source: Source[A, _])(implicit mat: Materializer): Traversable[A] =
    source.runWith(StreamConverters.asJavaStream()).iterator().asScala.toIterable

TraversableQueue的问题在于forEach必须完全完成对流的处理-它不支持“ break”概念,因此诸如“ drop” /“ take”之类的方法等。仍然需要处理整个源。从错误处理的角度来看,这很重要,而且很快就会失败。