Kity Cartman
Kity Cartman

Reputation: 896

Akka HTTP client server idle timeouts are not working

I have written an API that takes couple of minutes to create a response. This apparently results in connection resets:

curl: (56) Recv failure: Connection reset by peer

I have tried following timeout configurations from Akka HTTP but nothing seems to solve the above issue.

akka.http.server.idle-timeout = 600 s
akka.http.client.idle-timeout = 600 s

akka.http.server.request-timeout = 600 s

I suspect the issue is with the idle-timeout. But the above configuration doesn't seem to take any affect.

Could anyone please suggest what could be the issue?

I have tried a reverse scenario where i have set the idle-timeout to 5s which is suppose to reset the connection but even that doesn't work. Not sure what is really wrong here. Could anyone please explain this behaviour and how can I fix it?

I have implemented a fully working example for anyone to try here.

application.conf

akka.http.server.idle-timeout = 5 s
akka.http.client.idle-timeout = 5 s

akka.http.server.request-timeout = 600 s

helloworld {
  http {
    interface = "127.0.0.1"
    port = "8066"
  }
}

Routes.scala

package com.codingkapoor.helloworld.http

import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives._
import akka.http.scaladsl.server.Route
import akka.util.Timeout
import akka.stream.ActorMaterializer
import scala.concurrent.duration._

private[helloworld] trait Routes extends JsonSupport {

  implicit lazy val timeout: Timeout = Timeout(5.minute)

  implicit def materializer: ActorMaterializer

  lazy val routes: Route = pathSingleSlash {
    get {
      Thread.sleep(180000)
      complete(StatusCodes.OK, "Hello World!")
    }
  }
}

Upvotes: 2

Views: 4108

Answers (1)

Kity Cartman
Kity Cartman

Reputation: 896

There were two mistakes I found, fixing which solved the problem.

Mistake 1

I was forgetting to pass appConf as an argument while creating an instance of ActorSystem, without which ActorSystem was always getting created with default akka configs. Hence always make sure to pass appConf while creating an instance of ActorSystem otherwise your akka configurations will never take effect.

HelloWorld.Scala

val appConf = RuntimeEnvironment.appConf

implicit val system = ActorSystem("helloworld", appConf)

Mistake 2

In the question I was putting Thread.sleep in the Route thread. This was causing idle-timeout to reach the logs only after sleep got over.

What I am trying to say is that when I was creating ActorSystem without appConf, default idle-timeout of 1 min was supposed to come into play but because I was putting Thread.sleep of 3 min in the route thread, the idle-timeout of 1 min was getting logged only after 3 mins.

# Despite idle-timeout being 1 min, the following exception was getting 
# logged only after 3 mins

Caused by: akka.http.impl.engine.HttpIdleTimeoutException: 
  HTTP idle-timeout encountered, no bytes passed in the last 1 minute.
  This is configurable by akka.http.[server|client].idle-timeout.

The best way to mimic delayed response is to complete the request with a future as shown below. Here I have set an idle-timeout of 4 mins and request-timeout of 3 mins. This results in request being served always! Hope that clarifies.

application.conf

akka.http.server.idle-timeout = 240000 s
akka.http.client.idle-timeout = 240000 s

akka.http.server.request-timeout = 600 s

Routes.scala

lazy val routes: Route = pathSingleSlash {
    get {
        complete {
            Future.unit.map(_ => Thread.sleep(180000))
                       .map(_ => StatusCodes.OK -> "Hello World!")
        }
    }
}

The whole idea is that if idle-timeout is greater than request-timeout, your requests at least have chance to get served otherwise they would simply get dropped!

idle timeout (10mins)    ------>

request timeout (5 mins) --->


idle timeout (1 min)     --->

request timeout (5 mins) ------>

You can find the working solution here.

Upvotes: 3

Related Questions