Home > Back-end >  Cannot await combined future of multiple async requests, proc hangs when doing so
Cannot await combined future of multiple async requests, proc hangs when doing so

Time:01-19

I wanted to do some performance testing of my site. For that purpose I wanted to fire n requests asynchronously, combine the Futures that result of that into one future that completes when they all complete and then wait for that futures completion.

However, my code gets stuck awaiting the combined future and never completes.

My code looked like this:

import benchy
import std/[sugar, strformat, sequtils, httpclient, asyncfutures, asyncdispatch]

proc callSite(client: AsyncHttpClient, url: static string, callCount: int): Future[string] {.async.} =
  var futures : seq[Future[AsyncResponse]] = @[]
  for x in 1..callCount:
    futures.add client.get(url)
  
  echo "pre combo"
  let comboFuture = all(futures)
  let responses = await comboFuture

  echo "post awaited combo"
  result = await responses[0].body
  echo "post response"


var myClient = newAsyncHttpClient()
myClient.headers = newHttpHeaders({
  "Authorization": "Bearer " & token,
  "Accept": "application/json"
})
const url = <Some URL>

timeIt "campaign overview":
  let x = waitFor myClient.callSite(url, 3)
  keep(x) 

When I run this I never get past "pre combo", the request gets stuck, even though the server receives 3 requests and sends 3 responses (I checked that in server-side logs).

What is going wrong here?

CodePudding user response:

The reason for the issue is that clients from std/httpclient do not support a client dealing with more than one request at a time!

import benchy
import std/[sugar, strformat, sequtils, httpclient, asyncfutures, asyncdispatch]


proc callSite(clients: seq[AsyncHttpClient], url: static string): Future[string] {.async.} =
  var futures : seq[Future[AsyncResponse]] = @[]
  for client in clients:
    futures.add client.get(url)
  
  let comboFuture = all(futures)
  let responses = await comboFuture
  result = await responses[0].body

proc createMyClient(): AsyncHttpClient =
  result = newAsyncHttpClient()
  result.headers = newHttpHeaders({
    "Authorization": "Bearer " & token,
    "Accept": "application/json"
  })

let fiftyClients: seq[AsyncHttpClient] = (1..50).toSeq().mapIt(createMyClient())
const url = <Some Url>

timeIt "50 client":
  let x = waitFor fiftyClients.callSite(url)
  keep(x)
  • Related