Skip to content

Request Synchronization

Ollie Jennings edited this page Sep 10, 2020 · 2 revisions

NB: If you do not use this library with concurrency set to > 1, then you can ignore this

Problem

If we are making several asynchronous requests to the Riot API, we cannot guarantee that all will arrive and return at the same time and in the same order. This means that some of our requests could be sent during the rate limits of one window, and return in another window, meaning that our local rate limiters would be out of sync with Riots rate limits.

A naive solution would be to simply update our local rate limiters with the rate limits provided from Riot on every response, but this is flawed. This is because a request we sent earlier could potentially take say 2 seconds to respond (the round trip), whilst a later request could take 50ms. The response that comes last would have invalid rate-limits from Riot, whilst the request that only took 50ms would have the most up to date limits.

This issue can potentially lead to multiple 429 responses, and eventually having your API Token blacklisted.

Solution

There is a couple of things we need to consider when working with concurrency:

  1. How many requests are currently inflight or being executed ?
  2. What is the remaining rate limits from Riot?
  3. What are our local rate limits?

With this information we can synchronize our requests and rate limiters to make sure we do not hit 429 responses. The way to do this can be outlined below:

  1. For every response we recieve we calculate the following (using the response headers):
    • Appication rate limits available (App-Rate-Limit - App-Rate-Limit-Count)
    • Method rate limits available (Method-Rate-Limit - Method-Rate-Limit-Count)
  2. Get our local rate-limiters limits for both App and Method (otherwise known as our resevoir)
  3. Get the total number of requests currently being executed
  4. Perform a the calculation: Math.min(resevoir, rateLimit - requests in flight)
  5. Update our resevoir with the value returned from the calculation above

A psuedo-code representation of this is provided below:

const riotAppRateLimitsLeft = AppRateLimit - AppRateLimitCount;
const riotMethodRateLimitsLeft = MethodRateLimit - MethodRateLimitCount;

const localAppResevoir = appRateLimiter.resevoir()
const localMethodResevoir = methodRateLimiter.resevoir()

const requestsInFlight = methodRateLimiter.counts().EXECUTING + methodRateLimiter.counts().RUNNING;

const newAppResevoir = Math.min(localAppResevoir, (riotAppRateLimitsLeft - requestsInFlight))
const newMethodResevoir = Math.min(localMethodResevoir, (riotMethodRateLimitsLeft - requestsInFlight))

appRateLimiter.setResevoir(newAppResevoir)
methodRateLimiter.setResevoir(newMethodResevoir)
Clone this wiki locally