Skip to content

Commit

Permalink
GitHub auth: use token with the most remaining requests
Browse files Browse the repository at this point in the history
Part of #529.
  • Loading branch information
espadrine committed Jun 27, 2016
1 parent bfc6e7d commit a7cfac6
Showing 1 changed file with 34 additions and 10 deletions.
44 changes: 34 additions & 10 deletions lib/github-auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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);
Expand Down

0 comments on commit a7cfac6

Please sign in to comment.