4

I'm using host-level API with a queue.

  private val (queueSource, connectionPool) = Source.queue[(HttpRequest, Promise[HttpResponse])](queueSize, OverflowStrategy.backpressure).async
    .viaMat(poolFlow)(Keep.both)
    .toMat(
      Sink.foreach({
        case ((Success(resp), p)) =>
          p.success(resp)
        case ((Failure(e), p)) => p.failure(e)
      })
    )(Keep.left)
    .run()

I have a lot of request racing for connections in the connection pool but I get the following error:

 java.lang.IllegalStateException: You have to wait for previous offer to be resolved to send another request
    at akka.stream.impl.QueueSource$$anon$1.akka$stream$impl$QueueSource$$anon$$bufferElem(QueueSource.scala:84)
    at akka.stream.impl.QueueSource$$anon$1$$anonfun$1.apply(QueueSource.scala:94)
    at akka.stream.impl.QueueSource$$anon$1$$anonfun$1.apply(QueueSource.scala:91)
    at akka.stream.impl.fusing.GraphInterpreter.runAsyncInput(GraphInterpreter.scala:447)
    at akka.stream.impl.fusing.GraphInterpreterShell$AsyncInput.execute(ActorGraphInterpreter.scala:464)
    at akka.stream.impl.fusing.GraphInterpreterShell.processEvent(ActorGraphInterpreter.scala:559)
    at akka.stream.impl.fusing.ActorGraphInterpreter.akka$stream$impl$fusing$ActorGraphInterpreter$$processEvent(ActorGraphInterpreter.scala:741)
    at akka.stream.impl.fusing.ActorGraphInterpreter$$anonfun$receive$1.applyOrElse(ActorGraphInterpreter.scala:756)
    at akka.actor.Actor$class.aroundReceive(Actor.scala:517)
    at akka.stream.impl.fusing.ActorGraphInterpreter.aroundReceive(ActorGraphInterpreter.scala:666)
    at akka.actor.ActorCell.receiveMessage(ActorCell.scala:527)
    at akka.actor.ActorCell.invoke(ActorCell.scala:496)
    at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:257)
    at akka.dispatch.Mailbox.run(Mailbox.scala:224)
    at akka.dispatch.Mailbox.exec(Mailbox.scala:234)
    at akka.dispatch.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260)
    at akka.dispatch.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339)
    at akka.dispatch.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979)
    at akka.dispatch.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107)

I tried adding .async but back pressure still does not kick in. What does the error above mean and how to go about investigating the problem?

Rabzu
  • 52
  • 5
  • 26

1 Answers1

1

You are already constructing a Source with the Source.queue object method so I don't think it is possible to directly apply back pressure to whatever functionality is calling queue.offer. However, your problem can likely be solved in a different way.

Different OverflowStrategy

You could change the strategy to something like OverflowStrategy.dropHead or OverflowStrategy.dropTail. If your queueSize is large enough compared to the rate of queue.offer invocations then this will probably suite your needs.

Community
  • 1
  • 1
Ramón J Romero y Vigil
  • 17,373
  • 7
  • 77
  • 125
  • 1
    I can't afford to lose messages. Whats the point of backpressure strategy if it doesn't work? – Rabzu Dec 09 '17 at 21:22
  • @Rabzu I don't know of an example using the back pressure strategy with Source.queue. If backpressure is that important to you then why use `Source.queue` in the first place? – Ramón J Romero y Vigil Dec 09 '17 at 21:24
  • whats the alternative? – Rabzu Dec 09 '17 at 21:40
  • I have a service for which I want to create a clients using akka-http. I'm creating a thread pool and which backpressures requests from materialised akka-http client streams. The docs clearly say that host-level API with a queue [guards itself a) by applying backpressure to all request streams connected to the cached pool](https://doc.akka.io/docs/akka-http/current/scala/http/client-side/host-level.html#using-the-host-level-api-with-a-queue) by applying backpressure to all request streams connected to the cached pool ). – Rabzu Dec 11 '17 at 18:05
  • Are you asking me to use [host-level API in a streaming fashion](https://doc.akka.io/docs/akka-http/current/scala/http/client-side/host-level.html#using-the-host-level-api-with-a-queue)? – Rabzu Dec 11 '17 at 18:06
  • @Rabzu Continue reading the example from your link, specifically the code that deals directly with the queue. (1) They don't use `OverflowStrategy.backpressure`, instead they use ` Source.queue[(HttpRequest, Promise[HttpResponse])](QueueSize, OverflowStrategy.dropNew)` . (2) They explicitly handle the case when the queue is full `Future.failed(new RuntimeException("Queue overflowed. Try again later."))` which is something you said you don't want to do: "I can't afford to lose messages." – Ramón J Romero y Vigil Dec 11 '17 at 18:49
  • ok, but as an alternative, should I use streamed method and if so, can the Source(List(request)) be replaced with ["anti pattern" Source.single(request)](https://doc.akka.io/docs/akka-http/current/scala/http/client-side/host-level.html#examples) ? – Rabzu Dec 11 '17 at 18:53
  • @Rabzu I recommend another stack question. I don't know exactly what you're looking for and I can't answer with sufficient detail in an answer comments... – Ramón J Romero y Vigil Dec 11 '17 at 18:56
  • I don't agree with you or your arguments. Documentation clearly states that backpressure is one of the strategies. When testing sometimes it works but in production it does not. So maybe it has something to do with threads. It cannot backpressure other threads – Rabzu Dec 12 '17 at 17:58
  • Thank you. I did post this: https://stackoverflow.com/questions/47761994/how-to-create-akka-http-client-with-backpressure-overflow-strategy – Rabzu Dec 12 '17 at 22:56