Skip to content

Commit 58456d6

Browse files
add p-limit to GitHub API calls to avoid overwhelming the node process (or the API rate limits) (#591)
1 parent fd17871 commit 58456d6

File tree

4 files changed

+30
-6
lines changed

4 files changed

+30
-6
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515
- Fixed issue with GitHub app token tracking and refreshing. [#583](https://github.com/sourcebot-dev/sourcebot/pull/583)
1616
- Fixed "The account is already associated with another user" errors with GitLab oauth provider. [#584](https://github.com/sourcebot-dev/sourcebot/pull/584)
1717
- Fixed error when viewing a generic git connection in `/settings/connections`. [#588](https://github.com/sourcebot-dev/sourcebot/pull/588)
18+
- Fixed issue with an unbounded `Promise.allSettled(...)` when retrieving details from the GitHub API about a large number of repositories (or orgs or users). [#591](https://github.com/sourcebot-dev/sourcebot/pull/591)
1819

1920
## Removed
2021
- Removed built-in secret manager. [#592](https://github.com/sourcebot-dev/sourcebot/pull/592)

packages/backend/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
"ioredis": "^5.4.2",
5252
"lowdb": "^7.0.1",
5353
"micromatch": "^4.0.8",
54+
"p-limit": "^7.2.0",
5455
"posthog-node": "^4.2.1",
5556
"prom-client": "^15.1.3",
5657
"simple-git": "^3.27.0",

packages/backend/src/github.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@ import { createLogger } from "@sourcebot/logger";
44
import { GithubConnectionConfig } from "@sourcebot/schemas/v3/github.type";
55
import { hasEntitlement } from "@sourcebot/shared";
66
import micromatch from "micromatch";
7+
import pLimit from "p-limit";
78
import { processPromiseResults, throwIfAnyFailed } from "./connectionUtils.js";
89
import { GithubAppManager } from "./ee/githubAppManager.js";
910
import { env } from "./env.js";
1011
import { fetchWithRetry, measure } from "./utils.js";
1112
import { getTokenFromConfig } from "@sourcebot/crypto";
1213

1314
export const GITHUB_CLOUD_HOSTNAME = "github.com";
15+
16+
// Limit concurrent GitHub requests to avoid hitting rate limits and overwhelming installations.
17+
const MAX_CONCURRENT_GITHUB_QUERIES = 5;
18+
const githubQueryLimit = pLimit(MAX_CONCURRENT_GITHUB_QUERIES);
1419
const logger = createLogger('github');
1520

1621
export type OctokitRepository = {
@@ -194,7 +199,7 @@ export const getReposForAuthenticatedUser = async (visibility: 'all' | 'private'
194199
}
195200

196201
const getReposOwnedByUsers = async (users: string[], octokit: Octokit, signal: AbortSignal, url?: string) => {
197-
const results = await Promise.allSettled(users.map(async (user) => {
202+
const results = await Promise.allSettled(users.map((user) => githubQueryLimit(async () => {
198203
try {
199204
logger.debug(`Fetching repository info for user ${user}...`);
200205

@@ -243,7 +248,7 @@ const getReposOwnedByUsers = async (users: string[], octokit: Octokit, signal: A
243248
}
244249
throw error;
245250
}
246-
}));
251+
})));
247252

248253
throwIfAnyFailed(results);
249254
const { validItems: repos, warnings } = processPromiseResults<OctokitRepository>(results);
@@ -255,7 +260,7 @@ const getReposOwnedByUsers = async (users: string[], octokit: Octokit, signal: A
255260
}
256261

257262
const getReposForOrgs = async (orgs: string[], octokit: Octokit, signal: AbortSignal, url?: string) => {
258-
const results = await Promise.allSettled(orgs.map(async (org) => {
263+
const results = await Promise.allSettled(orgs.map((org) => githubQueryLimit(async () => {
259264
try {
260265
logger.debug(`Fetching repository info for org ${org}...`);
261266

@@ -291,7 +296,7 @@ const getReposForOrgs = async (orgs: string[], octokit: Octokit, signal: AbortSi
291296
}
292297
throw error;
293298
}
294-
}));
299+
})));
295300

296301
throwIfAnyFailed(results);
297302
const { validItems: repos, warnings } = processPromiseResults<OctokitRepository>(results);
@@ -303,7 +308,7 @@ const getReposForOrgs = async (orgs: string[], octokit: Octokit, signal: AbortSi
303308
}
304309

305310
const getRepos = async (repoList: string[], octokit: Octokit, signal: AbortSignal, url?: string) => {
306-
const results = await Promise.allSettled(repoList.map(async (repo) => {
311+
const results = await Promise.allSettled(repoList.map((repo) => githubQueryLimit(async () => {
307312
try {
308313
const [owner, repoName] = repo.split('/');
309314
logger.debug(`Fetching repository info for ${repo}...`);
@@ -341,7 +346,7 @@ const getRepos = async (repoList: string[], octokit: Octokit, signal: AbortSigna
341346
}
342347
throw error;
343348
}
344-
}));
349+
})));
345350

346351
throwIfAnyFailed(results);
347352
const { validItems: repos, warnings } = processPromiseResults<OctokitRepository>(results);

yarn.lock

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7807,6 +7807,7 @@ __metadata:
78077807
json-schema-to-typescript: "npm:^15.0.4"
78087808
lowdb: "npm:^7.0.1"
78097809
micromatch: "npm:^4.0.8"
7810+
p-limit: "npm:^7.2.0"
78107811
posthog-node: "npm:^4.2.1"
78117812
prom-client: "npm:^15.1.3"
78127813
simple-git: "npm:^3.27.0"
@@ -16463,6 +16464,15 @@ __metadata:
1646316464
languageName: node
1646416465
linkType: hard
1646516466

16467+
"p-limit@npm:^7.2.0":
16468+
version: 7.2.0
16469+
resolution: "p-limit@npm:7.2.0"
16470+
dependencies:
16471+
yocto-queue: "npm:^1.2.1"
16472+
checksum: 10c0/18e5ea305c31fdc0cf5260da7b63adfd46748cd72d4b40a31a13bd23eeece729c9308047fb5d848d7fa006d1b2fc2f36bb0d9dd173a300c38cd6dc1ed3355382
16473+
languageName: node
16474+
linkType: hard
16475+
1646616476
"p-locate@npm:^5.0.0":
1646716477
version: 5.0.0
1646816478
resolution: "p-locate@npm:5.0.0"
@@ -20836,6 +20846,13 @@ __metadata:
2083620846
languageName: node
2083720847
linkType: hard
2083820848

20849+
"yocto-queue@npm:^1.2.1":
20850+
version: 1.2.1
20851+
resolution: "yocto-queue@npm:1.2.1"
20852+
checksum: 10c0/5762caa3d0b421f4bdb7a1926b2ae2189fc6e4a14469258f183600028eb16db3e9e0306f46e8ebf5a52ff4b81a881f22637afefbef5399d6ad440824e9b27f9f
20853+
languageName: node
20854+
linkType: hard
20855+
2083920856
"zod-to-json-schema@npm:^3.24.1, zod-to-json-schema@npm:^3.24.5":
2084020857
version: 3.24.5
2084120858
resolution: "zod-to-json-schema@npm:3.24.5"

0 commit comments

Comments
 (0)