1

Suppose I've got a simple blocking HTTP client like this:

def httpGet(url: URL): Future[String] = ??? 

Now I want to use httpGet to call a server with a request rate limit; e.g. 1000 requests/sec. Since the standard library does not provide a rate limiter I will use RateLimiter of Guava:

import com.google.common.util.concurrent.RateLimiter
import scala.concurrent.{ExecutionContext, Future, blocking}

def throttled[A](fut: => Future[A], rateLimiter: RateLimiter)
                (implicit ec: ExecutionContext): Future[A] = {
  Future(blocking(rateLimiter.acquire())).flatMap(_ => fut)
}

implicit val ec = ExecutionContext.global
val rateLimiter = RateLimiter.create(permitsPerSeconds = 1000.0)
val throttledFuture = throttled(httpGet(url), rateLimiter)

Does it make sense ?
Would you use another execution context to execute rateLimiter.acquire() ?

Michael
  • 37,415
  • 63
  • 167
  • 303

1 Answers1

3

Since you're using blocking around the acquire, it's okay, IMO.

Depending on how much work gets done in the thread which calls httpGet, if you're on Scala 2.13 it might be worth considering using the parasitic execution context.

Style nit, but it might be worth taking advantage of Scala's ability to use {'s around single argument lists:

def throttled[A](rateLimiter: RateLimiter)(fut: => Future[A])(implicit ec: ExecutionContext): Future[A]

val throttledFuture = throttled(rateLimiter) { httpGet(url) }
Levi Ramsey
  • 7,898
  • 11
  • 18
  • Thanks for the suggestions. I am on 2.12 so I cannot use `parasitic`. Like the style remark: I will use it. – Michael May 13 '21 at 12:39