fuel icon indicating copy to clipboard operation
fuel copied to clipboard

Method awaitResponse in fuel-coroutines blocks IO thread

Open rishikesh13 opened this issue 3 years ago • 2 comments

When I call awaitResponse method without passing coroutine scope (which means it will use Dispatcher.IO as default) from a coroutine, current coroutine is suspended and thread is released as expected and coroutine gets dispatched to IO pool. After this I can see one thread from Dispatcher.IO is blocked until http response is returned ( blocking behavior instead of suspend)

Code snippet -

` // Setting single thread for Dispatcher.IO to check if coroutine send httpRequest concurrently System.setProperty(IO_PARALLELISM_PROPERTY_NAME, "1")

//Using single thread to check if parent coroutine is suspended` Executors.newSingleThreadExecutor().asCoroutineDispatcher().launch{

logger.info("Calling external api") Fue.get("URL").body(b).awaitReponse() //suspend call. As per fuel code, withContext will switch dispatcher to IO pool logger.info("Received response") }`

URL points to my other service which sends response with delay.

When I send two requests simultaneously, both requests get processed cocurrently ( both requests are logged with logger) which confirms that even with single thread executor parent coroutine run concurrently. But HTTP request-response part (awaitResponse code) do not run concurrently in IO thread pool. This is confirmed by two facts - other server receives second request only after first request is served and based on thread profiler I can see one thread in Dispatcher.IO is in blocked state(waiting for httpResponse).

Based on my understanding, with single thread in Dispatcher.IO two requests should be sent concurrently instead of sequential run. Is this expected or am I misinterpreting something here ?

rishikesh13 avatar Oct 07 '20 17:10 rishikesh13

Where did you place your second Request-Call? In the same launch-Block below the other request? Seems right to me that they run sequential.

MaaxGr avatar Oct 09 '20 19:10 MaaxGr

@MaaxGr Two requests are fired in different coroutine and not in single launch block. Here is sample code :

Server 1 -

@RestController
@RequestMapping("/test")
class FuelTest{

    init {
        System.setProperty(IO_PARALLELISM_PROPERTY_NAME, "1"); // Setting Dispatcher.IO size to 1
    }

    private val logger: KLogger = KotlinLogging.logger {}

    val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() // Using single thread to test concurrency

    @GetMapping("/request")
    @ResponseStatus(HttpStatus.OK)
    fun makeHttpRequest(): String {

        val clients = listOf("client1","client2")

        clients.forEach{

            CoroutineScope(dispatcher).launch {

                logger.info { " Making http call to : $it" }
                val response = Fuel.get("http://localhost:8000/api/$it")
                        .timeout(60000)
                        .timeoutRead(60000)
                        .awaitStringResponse()
                logger.info { " Received response : $response" }
            }
        }

        return "Done"
    }

}

Server 2 -

@RestController
@RequestMapping("/api")
class ApiController{

    private val logger: KLogger = KotlinLogging.logger {}

    @GetMapping("/client1")
    @ResponseStatus(HttpStatus.OK)
    fun client1(): String {

        logger.info { "Received request for client1" }
        Thread.sleep(20000)
        logger.info { "Returning request for client1" }

        return "Return client1"
    }

    @GetMapping("/client2")
    @ResponseStatus(HttpStatus.OK)
    fun client2(): String {

        logger.info { "Received request for client2" }
        Thread.sleep(15000)
        logger.info { "Returning request for client2" }

        return "Return client2"
    }
}

Logs when I fire : http://localhost:8080/test/request

Server 1

Server1

Server 2

Server2

As per logs, coroutines runs concurrently on single thread pool executor (logger prints Making http request for both clients) but when context is switched to single thread IO pool ( where actual http request is made within fuel) I don't see concurrent behavior.. This is based on observation that server2 receives second request only after first request is served.

rishikesh13 avatar Oct 10 '20 17:10 rishikesh13