From a7cfac6e937f033dff20f816e104006bb92a4ed2 Mon Sep 17 00:00:00 2001 From: Thaddee Tyl Date: Mon, 27 Jun 2016 20:04:57 +0200 Subject: [PATCH] GitHub auth: use token with the most remaining requests Part of #529. --- lib/github-auth.js | 44 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/github-auth.js b/lib/github-auth.js index c492fcd182474..359a95bfc427c 100644 --- a/lib/github-auth.js +++ b/lib/github-auth.js @@ -124,6 +124,7 @@ function sendTokenToAllServers(token) { // Track rate limit requests remaining. +// Ideally, we would want priority queues here. var reqRemaining = new Map(); // From token to requests remaining. var reqReset = new Map(); // From token to timestamp. @@ -146,26 +147,49 @@ function utcEpochSeconds() { var userTokenRateLimit = 12500; +// Return false if the token cannot reasonably be expected to perform +// a GitHub request. +function isTokenUsable(token, now) { + var reqs = reqRemaining.get(token); + var reset = reqReset.get(token); + // We don't want to empty more than 3/4 of a user's rate limit. + var hasRemainingReqs = reqs > (userTokenRateLimit / 4); + var isBeyondRateLimitReset = reset < now; + return hasRemainingReqs || isBeyondRateLimitReset; +} + +// Return a list of tokens (as strings) which can be used for a GitHub request, +// with a reasonable chance that the request will succeed. +function usableTokens() { + var now = utcEpochSeconds(); + return githubUserTokens.data.filter(function(token) { + return isTokenUsable(token, now); + }); +} + // Retrieve a user token if there is one for which we believe there are requests // remaining. Return undefined if we could not find one. function getReqRemainingToken() { // Go through the user tokens. - // Keep the first one which is usable or has reset. - var now = utcEpochSeconds(); - for (var token of reqReset.keys()) { + // Among usable ones, use the one with the highest number of remaining + // requests. + var tokens = usableTokens(); + var highestReq = -1; + var highestToken; + for (var i = 0; i < tokens.length; i++) { + var token = tokens[i]; var reqs = reqRemaining.get(token); - var reset = reqReset.get(token); - // We don't want to empty more than 3/4 of a user's rate limit. - var hasRemainingReqs = reqs > (userTokenRateLimit / 4); - var isBeyondRateLimitReset = reset < now; - if (hasRemainingReqs || isBeyondRateLimitReset) { - return token; + if (reqs > highestReq) { + highestReq = reqs; + highestToken = token; } } + return highestToken; } function addGithubToken(token) { - setReqRemaining(token, 0, 0); // A reset date of 0 has to be in the past. + // A reset date of 0 has to be in the past. + setReqRemaining(token, userTokenRateLimit, 0); // Insert it only if it is not registered yet. if (githubUserTokens.data.indexOf(token) === -1) { githubUserTokens.data.push(token);