软件版本:
我想在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()))
有没有更好/更实用的方法来达到理想的效果?
答案 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”之类的方法等。仍然需要处理整个源。从错误处理的角度来看,这很重要,而且很快就会失败。