Testa como a tua aplicação lida com os limites de taxa da API do GitHub

Tip

É novo na limitação? Aprende o que é o throttling e como lidar com isso.

De relance
Objetivo: Testar como a sua aplicação lida com os limites de taxa da API REST do GitHub
Tempo: 15 minutos
Plugins:RateLimitingPlugin, GenericRandomErrorPlugin, RetryAfterPlugin
Pré-requisitos:Configurar o Proxy de Desenvolvimento

A sua aplicação faz chamadas à API do GitHub. Funciona na sua máquina, depois um trabalho de CI, uma grande organização ou um dia de muito movimento ultrapassa o limite de taxa e começa a falhar. Para testar contra a API real, terias de esgotar a tua quota de pedidos e depois esperar até uma hora antes de poderes tentar novamente. O Dev Proxy simula os limites de taxa do GitHub localmente, com um limite e uma janela de tempo que escolhes.

Saiba o que o GitHub devolve

O GitHub tem dois tipos de limites de frequência para a API REST.

Os limites de taxa primária limitam o número de pedidos que efetua por hora. Por exemplo, 60 para pedidos não autenticados e 5.000 para pedidos com um token de acesso pessoal. Cada resposta inclui cabeçalhos que mostram onde está:

Header Meaning
x-ratelimit-limit O número máximo de solicitações por hora
x-ratelimit-remaining O número de solicitações restantes na janela atual
x-ratelimit-reset O momento em que a janela é reinicializada, em segundos epoch UTC

Quando ultrapassa o limite primário, o GitHub retorna 403 ou 429 com x-ratelimit-remaining definido para 0. Não volte a tentar até à hora em x-ratelimit-reset.

Os limites de taxa secundários protegem o GitHub de picos, como demasiados pedidos em simultâneo ou criação de conteúdo demasiado rápida. Quando ultrapassa um, o GitHub devolve 403 ou 429, com uma mensagem sobre um limite secundário de taxa. Se a resposta tiver um retry-after cabeçalho, espere esse número de segundos. Caso contrário, espere pelo menos um minuto e aumente o tempo de espera se o pedido continuar a falhar.

O GitHub pode bloquear integrações que continuam a enviar pedidos enquanto têm o limite de pedidos atingido. Para mais informações, consulte Limites de taxa para a API REST na documentação do GitHub.

Simule o limite de taxa primária

Use o RateLimitingPlugin para contar pedidos HTTP e devolver os cabeçalhos do limite de taxa do GitHub. Para testar sem esperar uma hora, use um pequeno limite e uma janela curta.

Ficheiro: devproxyrc.json

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
  "plugins": [
    {
      "name": "RetryAfterPlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll"
    },
    {
      "name": "RateLimitingPlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
      "configSection": "githubRateLimit"
    }
  ],
  "urlsToWatch": [
    "https://api.github.com/*"
  ],
  "githubRateLimit": {
    "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/ratelimitingplugin.schema.json",
    "headerLimit": "x-ratelimit-limit",
    "headerRemaining": "x-ratelimit-remaining",
    "headerReset": "x-ratelimit-reset",
    "resetFormat": "UtcEpochSeconds",
    "costPerRequest": 1,
    "rateLimit": 5,
    "resetTimeWindowSeconds": 60,
    "warningThresholdPercent": 0,
    "whenLimitExceeded": "Custom",
    "customResponseFile": "github-rate-limit-exceeded.json"
  }
}

Caution

Adicione o RetryAfterPlugin antes do RateLimitingPlugin no seu arquivo de configuração. Se adicionares isso depois, o RateLimitingPlugin trata do pedido antes de o RetryAfterPlugin o poder verificar.

No ficheiro de resposta personalizado, defina a resposta que o GitHub devolve quando ultrapassa o limite de taxa principal.

Ficheiro: github-rate-limit-exceeded.json

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/ratelimitingplugin.customresponsefile.schema.json",
  "statusCode": 429,
  "headers": [
    {
      "name": "content-type",
      "value": "application/json; charset=utf-8"
    }
  ],
  "body": {
    "message": "API rate limit exceeded for user ID 1.",
    "documentation_url": "https://docs.github.com/rest/overview/rate-limits-for-the-rest-api"
  }
}

Inicia o Dev Proxy e executa a tua aplicação.

devproxy --config-file devproxyrc.json

O Dev Proxy encaminha os primeiros 5 pedidos de cada minuto para o GitHub e define os x-ratelimit-* cabeçalhos nas respostas. A partir do 6.º pedido, o Proxy de Desenvolvimento devolve a resposta do limite de taxa com x-ratelimit-remaining definido para 0 e x-ratelimit-reset definido para o final da janela. Se a tua app chamar a API novamente antes da janela reiniciar, o RetryAfterPlugin comunica isso e limita a taxa do pedido.

Verifique se a sua aplicação:

  • Lê x-ratelimit-remaining e abranda antes de chegar a 0.
  • Deixa de chamar a API após uma resposta de limite de pedidos e espera até x-ratelimit-reset.
  • Informa o utilizador do que está a acontecer, por exemplo "Limite de taxa do GitHub atingido, a tentar novamente às 14:05", em vez de falhar silenciosamente.

Note

O GitHub devolve ou 403 ou 429 quando ultrapassar um limite de taxa. Para testar se a tua aplicação também lida com 403, muda statusCode para 403. O RetryAfterPlugin só regista respostas 429, por isso não reporta tentativas antecipadas após um 403.

Tip

O Dev Proxy encaminha os pedidos para o GitHub até que o limite simulado seja atingido. Estes pedidos também contam para o seu limite real de pedidos do GitHub.

Simular limites de taxa secundária

Os limites de taxa secundários aparecem em ráfagas e incluem um retry-after cabeçalho. Utilize o GenericRandomErrorPlugin para os devolver aleatoriamente.

Ficheiro: devproxyrc.json

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/rc.schema.json",
  "plugins": [
    {
      "name": "RetryAfterPlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll"
    },
    {
      "name": "GenericRandomErrorPlugin",
      "enabled": true,
      "pluginPath": "~appFolder/plugins/DevProxy.Plugins.dll",
      "configSection": "githubSecondaryRateLimit"
    }
  ],
  "urlsToWatch": [
    "https://api.github.com/*"
  ],
  "githubSecondaryRateLimit": {
    "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.schema.json",
    "errorsFile": "github-secondary-rate-limit.json",
    "rate": 50,
    "retryAfterInSeconds": 60
  }
}

Ficheiro: github-secondary-rate-limit.json

{
  "$schema": "https://raw.githubusercontent.com/dotnet/dev-proxy/main/schemas/v3.3.1/genericrandomerrorplugin.errorsfile.schema.json",
  "errors": [
    {
      "request": {
        "url": "https://api.github.com/*"
      },
      "responses": [
        {
          "statusCode": 429,
          "headers": [
            {
              "name": "content-type",
              "value": "application/json; charset=utf-8"
            },
            {
              "name": "retry-after",
              "value": "@dynamic"
            }
          ],
          "body": {
            "message": "You have exceeded a secondary rate limit. Please wait a few minutes before you try again.",
            "documentation_url": "https://docs.github.com/rest/overview/rate-limits-for-the-rest-api#about-secondary-rate-limits"
          }
        }
      ]
    }
  ]
}

Inicia o Dev Proxy e executa a tua aplicação. Verifica se a tua aplicação espera o número de segundos indicados no cabeçalho retry-after antes de voltar a chamar a API. Se não o fizer, o RetryAfterPlugin comunica isso.

Se usares o Octokit com o plugin de limitação, verifica se os teus handlers onRateLimit e onSecondaryRateLimit funcionam e se eles devolvem o resultado que esperas.

Passo seguinte

Saiba mais sobre o RateLimitingPlugin.

Ver também