perf: reduce GitHub quota spent validating credentials

This commit is contained in:
Codex committed 2026-11-10 11:36:55 +00:00
1 parent 25fe9d2df7
commit 5267599254
3 files changed
+171 -2

No files matched your search

+16 -2
View File
@@ -1,4 +1,5 @@
import { isGitHubRateLimitError, OctokitRequestErrorLike } from "./github-rate-limit";
import { CredentialValidation } from "./credential-validation";
import { refreshLegacyToken } from "./legacy-token-refresh";
import { notifyOwnerAccessProblem } from "./owner-notifications";
import AnonymizedRepositoryModel from "./model/anonymizedRepositories/anonymizedRepositories.model";
@@ -277,13 +278,26 @@ export function octokit(token: string) {
export { waitForTokenGate };
const credentialValidation = new CredentialValidation();
export async function checkToken(token: string) {
const oct = octokit(token);
try {
const context = githubTokenContext(token);
if (context?.publicRepository) { await context.renew(); return true; }
if (token.startsWith("ghs_")) await oct.request("GET /installation/repositories");
else await oct.users.getAuthenticated();
// Managed credentials can renew or revalidate repository access. Never
// share their checks or cache validators under a potentially rotating handle.
if (context) {
if (token.startsWith("ghs_")) await oct.request("GET /installation/repositories");
else await oct.users.getAuthenticated();
} else {
await credentialValidation.check(token, async etag => {
const headers = etag ? { "if-none-match": etag } : {};
return token.startsWith("ghs_")
? oct.request("GET /installation/repositories", { headers })
: oct.users.getAuthenticated({ headers });
});
}
return true;
} catch (err) {
// Only a confirmed invalid credential permits fallback. Network failures,
+44
View File
@@ -0,0 +1,44 @@
import { createHash } from "crypto";
type ValidationRequest = (etag?: string) => Promise<{ headers: { etag?: string } }>;
/** Store validators, never cached authorization decisions. Every completed
* check requires a fresh authenticated response from GitHub. */
export class CredentialValidation {
private readonly validators = new Map<string, string>();
private readonly pending = new Map<string, Promise<void>>();
constructor(private readonly capacity = 2000) {}
check(token: string, request: ValidationRequest): Promise<void> {
// Quota keys can be shared by multiple credentials. Validation cannot.
const key = createHash("sha256").update(token).digest("hex");
const pending = this.pending.get(key);
if (pending) return pending;
const check = this.validate(key, request);
if (this.pending.size >= this.capacity) return check;
const tracked = check.finally(() => { this.pending.delete(key); });
this.pending.set(key, tracked);
return tracked;
}
private async validate(key: string, request: ValidationRequest): Promise<void> {
const etag = this.validators.get(key);
try {
const response = await request(etag);
this.validators.delete(key);
if (response.headers.etag && this.capacity > 0) {
if (this.validators.size >= this.capacity) {
this.validators.delete(this.validators.keys().next().value!);
}
this.validators.set(key, response.headers.etag);
}
} catch (error) {
// Octokit throws for 304. Accept it only when this exact credential
// previously succeeded and this request sent its validator.
if (etag && (error as { status?: number })?.status === 304) return;
this.validators.delete(key);
throw error;
}
}
}