Akka Streams:无法推动端口两次,或者在拉动之前

时间:2018-04-13 09:19:43

标签: scala akka akka-stream

我正在尝试使用Akka Streams TestKit测试我的滑动窗口阶段,我看到了这个异常。

Exception in thread "main" java.lang.AssertionError: assertion failed: expected OnNext(Stream(2, ?)), found OnError(java.lang.IllegalArgumentException: Cannot push port (Sliding.out(2043106095)) twice, or before it being pulled

Akka,Akka Streams,Akka Streams TestKit版本:2.5.9
Scala版本:2.12.4

case class Sliding[T](duration: Duration, step: Duration, f: T => Long) extends GraphStage[FlowShape[T, immutable.Seq[T]]] {

  val in = Inlet[T]("Sliding.in")
  val out = Outlet[immutable.Seq[T]]("Sliding.out")
  override val shape: FlowShape[T, immutable.Seq[T]] = FlowShape(in, out)

  override protected val initialAttributes: Attributes = Attributes.name("sliding")

  override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = new GraphStageLogic(shape) with InHandler with OutHandler {
    private var buf = Vector.empty[T]

    var watermark = 0L
    var dropUntilDuration = step.toMillis

    private def isWindowDone(current: T) = {
      if (buf.nonEmpty) {
        val hts = f(buf.head)
        val cts = f(current)

        cts >= hts + duration.toMillis
      } else false
    }

    override def onPush(): Unit = {
      val data = grab(in)

      val timeStamp = f(data)

      if (timeStamp > watermark) {
        watermark = timeStamp

        if (isWindowDone(data)) {
          push(out, buf)

          buf = buf.dropWhile { x =>
            val ts = f(x)
            ts < dropUntilDuration
          }

          dropUntilDuration = dropUntilDuration + step.toMillis
        }

        buf :+= data
        pull(in)
      } else {
        pull(in)
      }
    }

    override def onPull(): Unit = {
      pull(in)
    }

    override def onUpstreamFinish(): Unit = {
      if (buf.nonEmpty) {
        push(out, buf)
      }
      completeStage()
    }

    this.setHandlers(in, out, this)
  }
}

测试代码:

object WindowTest extends App {

  implicit val as = ActorSystem("WindowTest")
  implicit val m = ActorMaterializer()

  val expectedResultIterator = Stream.from(1).map(_.toLong) 

  val infinite = Iterator.from(1)

  Source
    .fromIterator(() => infinite)
    .map(_.toLong)
    .via(Sliding(10 millis, 2 millis, identity))
    .runWith(TestSink.probe[Seq[Long]])
    .request(1)
    .expectNext(expectedResultIterator.take(10).toSeq)
    .request(1)
    .expectNext(expectedResultIterator.take(11).drop(1).toSeq)
    .expectComplete()
}

0 个答案:

没有答案