15

I'm trying to write a tool for batch data upload using Akka HTTP 2.0-M2. But I'm facing akka.stream.OverflowStrategy$Fail$BufferOverflowException: Exceeded configured max-open-requests value of [32] error.

I tried to isolate a problem and here is the sample code which also fails:

public class TestMaxRequests {
    private static final class Router extends HttpApp {
        @Override
        public Route createRoute() {
            return route(
                    path("test").route(
                            get(handleWith(ctx -> ctx.complete("OK")))
                    )
            );
        }
    }


    public static void main(String[] args) {
        ActorSystem actorSystem = ActorSystem.create();
        Materializer materializer = ActorMaterializer.create(actorSystem);

        Router router = new Router();
        router.bindRoute("127.0.0.1", 8082, actorSystem);

        LoggingAdapter log = Logging.getLogger(actorSystem, new Object());

        for (int i = 0; i < 100; i++) {
            final int reqNum = i;
            Http.get(actorSystem).singleRequest(HttpRequest.create().withUri("http://127.0.0.1:8082/test"), materializer)
                    .onComplete(new OnComplete<HttpResponse>() {
                        @Override
                        public void onComplete(Throwable failure, HttpResponse response) throws Throwable {
                            if (failure != null) {
                                log.error(failure, "Failed: {}", reqNum);
                            } else {
                                log.info("Success: {}, consuming stream...", reqNum);
                                response.entity().getDataBytes().runWith(Sink.ignore(), materializer);
                                log.info("Success: {}, consumed stream", reqNum);
                            }
                        }
                    }, actorSystem.dispatcher());
        }
    }
}

It fails with:

[2015-12-15 16:17:32,609] [   INFO] [] [] a.e.s.Slf4jLogger: Slf4jLogger started
[2015-12-15 16:17:32,628] [  DEBUG] [main] [EventStream(akka://default)] a.e.EventStream: logger log1-Slf4jLogger started
[2015-12-15 16:17:32,636] [  DEBUG] [main] [EventStream(akka://default)] a.e.EventStream: Default Loggers started
[2015-12-15 16:17:33,531] [  DEBUG] [spatcher-3] [akka://default/system/IO-TCP/selectors/$a/0] a.i.TcpListener: Successfully bound to /127.0.0.1:8082
[2015-12-15 16:17:33,624] [  DEBUG] [spatcher-7] [akka://default/user/PoolInterfaceActor-0] a.h.i.e.c.PoolInterfaceActor: (Re-)starting host connection pool to 127.0.0.1:8082
[2015-12-15 16:17:33,736] [  DEBUG] [spatcher-8] [akka://default/user/SlotProcessor-0] a.h.i.e.c.PoolSlot$SlotProcessor: become unconnected, from subscriber pending
[2015-12-15 16:17:33,748] [  DEBUG] [patcher-11] [akka://default/user/SlotProcessor-3] a.h.i.e.c.PoolSlot$SlotProcessor: become unconnected, from subscriber pending
[2015-12-15 16:17:33,758] [  DEBUG] [spatcher-9] [akka://default/user/SlotProcessor-2] a.h.i.e.c.PoolSlot$SlotProcessor: become unconnected, from subscriber pending
[2015-12-15 16:17:33,762] [  DEBUG] [spatcher-9] [akka://default/user/SlotProcessor-1] a.h.i.e.c.PoolSlot$SlotProcessor: become unconnected, from subscriber pending
[2015-12-15 16:17:33,779] [  ERROR] [patcher-11] [Object(akka://default)] j.l.Object: Failed: 36
akka.stream.OverflowStrategy$Fail$BufferOverflowException: Exceeded configured max-open-requests value of [32]
    at akka.http.impl.engine.client.PoolInterfaceActor$$anonfun$receive$1.applyOrElse(PoolInterfaceActor.scala:120) ~[akka-http-core-experimental_2.11-2.0-M2.jar:na]
    at akka.actor.Actor$class.aroundReceive(Actor.scala:480) ~[akka-actor_2.11-2.4.0.jar:na]
    at akka.http.impl.engine.client.PoolInterfaceActor.akka$stream$actor$ActorSubscriber$$super$aroundReceive(PoolInterfaceActor.scala:48) ~[akka-http-core-experimental_2.11-2.0-M2.jar:na]
    at akka.stream.actor.ActorSubscriber$class.aroundReceive(ActorSubscriber.scala:201) ~[akka-stream-experimental_2.11-2.0-M2.jar:na]
    at akka.http.impl.engine.client.PoolInterfaceActor.akka$stream$actor$ActorPublisher$$super$aroundReceive(PoolInterfaceActor.scala:48) ~[akka-http-core-experimental_2.11-2.0-M2.jar:na]
    at akka.stream.actor.ActorPublisher$class.aroundReceive(ActorPublisher.scala:309) ~[akka-stream-experimental_2.11-2.0-M2.jar:na]
    at akka.http.impl.engine.client.PoolInterfaceActor.aroundReceive(PoolInterfaceActor.scala:48) ~[akka-http-core-experimental_2.11-2.0-M2.jar:na]
    at akka.actor.ActorCell.receiveMessage(ActorCell.scala:525) [akka-actor_2.11-2.4.0.jar:na]
    at akka.actor.ActorCell.invoke(ActorCell.scala:494) [akka-actor_2.11-2.4.0.jar:na]
    at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:257) [akka-actor_2.11-2.4.0.jar:na]
    at akka.dispatch.Mailbox.run(Mailbox.scala:224) [akka-actor_2.11-2.4.0.jar:na]
    at akka.dispatch.Mailbox.exec(Mailbox.scala:234) [akka-actor_2.11-2.4.0.jar:na]
    at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:260) [scala-library-2.11.7.jar:na]
    at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1339) [scala-library-2.11.7.jar:na]
    at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1979) [scala-library-2.11.7.jar:na]
    at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:107) [scala-library-2.11.7.jar:na]
[2015-12-15 16:17:33,780] [  ERROR] [patcher-20] [Object(akka://default)] j.l.Object: Failed: 48

I guess that's because I'm trying to create a lot of Futures and execute them all at once. But isn't Akka supposed to enable backpressure? I guess I'm using it wrong. I tried superPool methods but nothing changed because, as I understand, Http.singleRequest has same pool inside. I also tried to reuse Http instance instead of calling Http.get() in the loop but it also did not help.

What is the correct way of firing a batch of requests? I am planning to execute batches of 10 000 - 100 000 requests.

Ramón J Romero y Vigil
  • 17,373
  • 7
  • 77
  • 125
relgames
  • 1,356
  • 1
  • 16
  • 34

1 Answers1

18

Akka absolutely enables backpressure, you're just not taking advantage of it. Instead of dispatching multiple single requests, you can use a single Flow to send all of your requests through. From the documentation:

final Flow<HttpRequest, HttpResponse, Future<OutgoingConnection>> connectionFlow = 
  Http.get(actorSystem).outgoingConnection("127.0.0.1", 8082);

You can then use this Flow to process your HttpRequest objects:

HttpRequest httpRequest = HttpRequest.GET("/test")

//imitates your for-loop example of 100 requests
Source.from(() -> Collections.nCopies(100, httpRequest).iterator()) 
      .via(connectionFlow)
      .runForeach(...)
Cowbolt
  • 158
  • 1
  • 10
Ramón J Romero y Vigil
  • 17,373
  • 7
  • 77
  • 125
  • I missed the part of using multiple requests in the Source.from()! Thanks!! – relgames Dec 15 '15 at 19:23
  • 1
    @RamonJRomeroyVigil How can I use flow with backpressure if I can't create bunch of requests in advance ? For example let's say I'm requesting something based upon IDs returned in some paginated API. Thus I want to use flow as I process responses from previous requests. – expert May 05 '16 at 11:37
  • @expert A combination of my answer above and my answer to the following question should do the trick. http://stackoverflow.com/questions/30964824/how-to-create-a-source-that-can-receive-elements-later-via-a-method-call – Ramón J Romero y Vigil May 05 '16 at 21:04
  • @RamonJRomeroyVigil Thank you, Ramon. Unfortunately it doesn't work for me :( Could you please take a look at http://stackoverflow.com/q/37061585/226895 ? – expert May 05 '16 at 22:54