Skip to content

Commit fa3e386

Browse files
committed
fix: use retry and throttle octokit plugins
Refactors the client to use [@octokit/plugin-retry](https://www.npmjs.com/package/@octokit/plugin-retry) and [@octokit/plugin-throttling](https://www.npmjs.com/package/@octokit/plugin-throttling) as GitHub occasionally changes it's API and these plugins can abstract those changes away from this module. - Removes `lib/definitions/rate-limit.js` - Adds `lib/definitions/retry.js` and `lib/definitions/throttle.js` to handle retry/throttle settings - Updates tests to be more like GitHub (returing the correct rate limit response headers, etc) Fixes semantic-release#299 and semantic-release/semantic-release#2204 See also semantic-release#480 and semantic-release#378
1 parent c993e60 commit fa3e386

17 files changed

+302
-365
lines changed

lib/definitions/rate-limit.js

Lines changed: 0 additions & 27 deletions
This file was deleted.

lib/definitions/retry.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/**
2+
* Default exponential backoff configuration for retries.
3+
*/
4+
const RETRY_CONF = {
5+
retries: 3,
6+
retryAfterBaseValue: 1000,
7+
doNotRetry: [400, 401, 403],
8+
};
9+
10+
module.exports = {RETRY_CONF};

lib/definitions/throttle.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
/**
2+
* Default exponential backoff configuration for retries.
3+
*/
4+
const THROTTLE_CONF = {retryAfterBaseValue: 1000};
5+
6+
module.exports = {THROTTLE_CONF};

lib/get-client.js

Lines changed: 20 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,40 @@
1-
const {memoize, get} = require('lodash');
21
const {Octokit} = require('@octokit/rest');
3-
const pRetry = require('p-retry');
4-
const Bottleneck = require('bottleneck');
52
const urljoin = require('url-join');
63
const HttpProxyAgent = require('http-proxy-agent');
74
const HttpsProxyAgent = require('https-proxy-agent');
5+
const {throttling} = require('@octokit/plugin-throttling');
6+
const {RETRY_CONF} = require('./definitions/retry');
7+
const {THROTTLE_CONF} = require('./definitions/throttle');
8+
const {retry} = require('@octokit/plugin-retry');
89

9-
const {RETRY_CONF, RATE_LIMITS, GLOBAL_RATE_LIMIT} = require('./definitions/rate-limit');
10-
11-
/**
12-
* Http error status for which to not retry.
13-
*/
14-
const SKIP_RETRY_CODES = new Set([400, 401, 403]);
10+
module.exports = ({githubToken, githubUrl, githubApiPathPrefix, proxy}) => {
11+
const onRetry = (retryAfter, options) => {
12+
github.log.warn(`Request quota exhausted for request ${options.method} ${options.url}`);
1513

16-
/**
17-
* Create or retrieve the throttler function for a given rate limit group.
18-
*
19-
* @param {Array} rate The rate limit group.
20-
* @param {String} limit The rate limits per API endpoints.
21-
* @param {Bottleneck} globalThrottler The global throttler.
22-
*
23-
* @return {Bottleneck} The throller function for the given rate limit group.
24-
*/
25-
const getThrottler = memoize((rate, globalThrottler) =>
26-
new Bottleneck({minTime: get(RATE_LIMITS, rate)}).chain(globalThrottler)
27-
);
14+
if (options.request.retryCount <= RETRY_CONF.retries) {
15+
github.log.debug(`Will retry after ${retryAfter}.`);
16+
return true;
17+
}
18+
};
2819

29-
module.exports = ({githubToken, githubUrl, githubApiPathPrefix, proxy}) => {
3020
const baseUrl = githubUrl && urljoin(githubUrl, githubApiPathPrefix);
31-
const globalThrottler = new Bottleneck({minTime: GLOBAL_RATE_LIMIT});
32-
const github = new Octokit({
21+
const OctokitWithThrottlingAndRetry = Octokit.plugin(throttling, retry);
22+
const github = new OctokitWithThrottlingAndRetry({
3323
auth: `token ${githubToken}`,
3424
baseUrl,
25+
retry: RETRY_CONF,
3526
request: {
3627
agent: proxy
3728
? baseUrl && new URL(baseUrl).protocol.replace(':', '') === 'http'
3829
? new HttpProxyAgent(proxy)
3930
: new HttpsProxyAgent(proxy)
4031
: undefined,
4132
},
42-
});
43-
44-
github.hook.wrap('request', (request, options) => {
45-
const access = options.method === 'GET' ? 'read' : 'write';
46-
const rateCategory = options.url.startsWith('/search') ? 'search' : 'core';
47-
const limitKey = [rateCategory, RATE_LIMITS[rateCategory][access] && access].filter(Boolean).join('.');
48-
49-
return pRetry(async () => {
50-
try {
51-
return await getThrottler(limitKey, globalThrottler).wrap(request)(options);
52-
} catch (error) {
53-
if (SKIP_RETRY_CODES.has(error.status)) {
54-
throw new pRetry.AbortError(error);
55-
}
56-
57-
throw error;
58-
}
59-
}, RETRY_CONF);
33+
throttle: {
34+
...THROTTLE_CONF,
35+
onSecondaryRateLimit: onRetry,
36+
onRateLimit: onRetry,
37+
},
6038
});
6139

6240
return github;

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy