Skip to content

Commit 70a2ade

Browse files
authored
Make it very obvious when the rate limit has been hit (#4677)
1 parent 29d17c1 commit 70a2ade

File tree

3 files changed

+36
-3
lines changed

3 files changed

+36
-3
lines changed

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2327,6 +2327,7 @@
23272327
"@vscode/extension-telemetry": "0.7.5",
23282328
"apollo-boost": "^0.4.9",
23292329
"apollo-link-context": "1.0.20",
2330+
"cockatiel": "^3.1.1",
23302331
"cross-fetch": "3.1.5",
23312332
"dayjs": "1.10.4",
23322333
"events": "3.2.0",

src/github/loggingOctokit.ts

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
import { Octokit } from '@octokit/rest';
77
import { ApolloClient, ApolloQueryResult, FetchResult, MutationOptions, NormalizedCacheObject, OperationVariables, QueryOptions } from 'apollo-boost';
8+
import { bulkhead, BulkheadPolicy } from 'cockatiel';
9+
import * as vscode from 'vscode';
810
import Logger from '../common/logger';
911
import { ITelemetry } from '../common/telemetry';
1012
import { RateLimit } from './graphql';
@@ -17,11 +19,27 @@ interface RestResponse {
1719
}
1820

1921
export class RateLogger {
22+
private bulkhead: BulkheadPolicy = bulkhead(140);
2023
private static ID = 'RateLimit';
2124
private hasLoggedLowRateLimit: boolean = false;
2225

2326
constructor(private readonly telemetry: ITelemetry) { }
2427

28+
public logAndLimit(apiRequest: () => Promise<any>): Promise<any> | undefined {
29+
if (this.bulkhead.executionSlots === 0) {
30+
Logger.error('API call count has exceeded 140 concurrent calls.', RateLogger.ID);
31+
// We have hit more than 140 concurrent API requests.
32+
/* __GDPR__
33+
"pr.highApiCallRate" : {}
34+
*/
35+
this.telemetry.sendTelemetryErrorEvent('pr.highApiCallRate');
36+
vscode.window.showErrorMessage(vscode.l10n.t('The GitHub Pull Requests extension is making too many requests to GitHub. This indicates a bug in the extension. Please file an issue on GitHub and include the output from "GitHub Pull Request".'));
37+
return undefined;
38+
}
39+
Logger.debug(`Extension rate limit remaining: ${this.bulkhead.executionSlots}`, RateLogger.ID);
40+
return this.bulkhead.execute(() => apiRequest());
41+
}
42+
2543
public async logRateLimit(info: string | undefined, result: Promise<{ data: { rateLimit: RateLimit | undefined } | undefined } | undefined>, isRest: boolean = false) {
2644
let rateLimitInfo;
2745
try {
@@ -70,13 +88,19 @@ export class LoggingApolloClient {
7088
constructor(private readonly _graphql: ApolloClient<NormalizedCacheObject>, private _rateLogger: RateLogger) { };
7189

7290
query<T = any, TVariables = OperationVariables>(options: QueryOptions<TVariables>): Promise<ApolloQueryResult<T>> {
73-
const result = this._graphql.query(options);
91+
const result = this._rateLogger.logAndLimit(() => this._graphql.query(options));
92+
if (result === undefined) {
93+
throw new Error('API call count has exceeded a rate limit.');
94+
}
7495
this._rateLogger.logRateLimit((options.query.definitions[0] as { name: { value: string } | undefined }).name?.value, result as any);
7596
return result;
7697
}
7798

7899
mutate<T = any, TVariables = OperationVariables>(options: MutationOptions<T, TVariables>): Promise<FetchResult<T>> {
79-
const result = this._graphql.mutate(options);
100+
const result = this._rateLogger.logAndLimit(() => this._graphql.mutate(options));
101+
if (result === undefined) {
102+
throw new Error('API call count has exceeded a rate limit.');
103+
}
80104
this._rateLogger.logRateLimit(options.context, result as any);
81105
return result;
82106
}
@@ -86,7 +110,10 @@ export class LoggingOctokit {
86110
constructor(public readonly api: Octokit, private _rateLogger: RateLogger) { };
87111

88112
async call<T, U>(api: (T) => Promise<U>, args: T): Promise<U> {
89-
const result = api(args);
113+
const result = this._rateLogger.logAndLimit(() => api(args));
114+
if (result === undefined) {
115+
throw new Error('API call count has exceeded a rate limit.');
116+
}
90117
this._rateLogger.logRestRateLimit((api as unknown as { endpoint: { DEFAULTS: { url: string } | undefined } | undefined }).endpoint?.DEFAULTS?.url, result as Promise<unknown> as Promise<RestResponse>);
91118
return result;
92119
}

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1693,6 +1693,11 @@ co@^4.6.0:
16931693
resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184"
16941694
integrity sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=
16951695

1696+
cockatiel@^3.1.1:
1697+
version "3.1.1"
1698+
resolved "https://registry.yarnpkg.com/cockatiel/-/cockatiel-3.1.1.tgz#82c95dcad673649c43c0a35c424c5d2ad59d4e6b"
1699+
integrity sha512-zHMqBGvkZLfMKkBMD+0U8X1nW8zYwMtymgJ8CTknWOmTDpvjEwygtFN4QR9A1iFQDwCbg8g8+B/zVBoxvj1feQ==
1700+
16961701
color-convert@^1.9.0:
16971702
version "1.9.3"
16981703
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"

0 commit comments

Comments
 (0)