我正在尝试使用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()
}