Skip to content

Commit 0fb6f4a

Browse files
authored
fix: cache GitHub App ID to reduce SSM calls (#4994)
Cache the GitHub App ID at module level to avoid repeated SSM parameter fetches on every rate limit metric update. The value is now fetched once per Lambda execution context and reused across invocations. Especially when multiple events are badged, repeatedly calling the SSM Parameter API feels wasteful and adds a non-negligible amount of latency to several functions in the scale-up Lambda.
1 parent 6685cb6 commit 0fb6f4a

File tree

2 files changed

+35
-1
lines changed

2 files changed

+35
-1
lines changed

lambdas/functions/control-plane/src/github/rate-limit.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { createSingleMetric } from '@aws-github-runner/aws-powertools-util';
33
import { MetricUnit } from '@aws-lambda-powertools/metrics';
44
import { metricGitHubAppRateLimit } from './rate-limit';
55
import { describe, it, expect, beforeEach, vi } from 'vitest';
6+
import { getParameter } from '@aws-github-runner/aws-ssm-util';
67

78
process.env.PARAMETER_GITHUB_APP_ID_NAME = 'test';
89
vi.mock('@aws-github-runner/aws-ssm-util', async () => {
@@ -78,4 +79,27 @@ describe('metricGitHubAppRateLimit', () => {
7879

7980
expect(createSingleMetric).not.toHaveBeenCalled();
8081
});
82+
83+
it('should cache GitHub App ID and only call getParameter once', async () => {
84+
// Reset modules to clear the appIdPromise cache
85+
vi.resetModules();
86+
const { metricGitHubAppRateLimit: freshMetricFunction } = await import('./rate-limit');
87+
88+
process.env.ENABLE_METRIC_GITHUB_APP_RATE_LIMIT = 'true';
89+
const headers: ResponseHeaders = {
90+
'x-ratelimit-remaining': '10',
91+
'x-ratelimit-limit': '60',
92+
};
93+
94+
const mockGetParameter = vi.mocked(getParameter);
95+
mockGetParameter.mockClear();
96+
97+
await freshMetricFunction(headers);
98+
await freshMetricFunction(headers);
99+
await freshMetricFunction(headers);
100+
101+
// getParameter should only be called once due to caching
102+
expect(mockGetParameter).toHaveBeenCalledTimes(1);
103+
expect(mockGetParameter).toHaveBeenCalledWith(process.env.PARAMETER_GITHUB_APP_ID_NAME);
104+
});
81105
});

lambdas/functions/control-plane/src/github/rate-limit.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,16 @@ import { MetricUnit } from '@aws-lambda-powertools/metrics';
44
import yn from 'yn';
55
import { getParameter } from '@aws-github-runner/aws-ssm-util';
66

7+
// Cache the app ID to avoid repeated SSM calls across Lambda invocations
8+
let appIdPromise: Promise<string> | null = null;
9+
10+
async function getAppId(): Promise<string> {
11+
if (!appIdPromise) {
12+
appIdPromise = getParameter(process.env.PARAMETER_GITHUB_APP_ID_NAME);
13+
}
14+
return appIdPromise;
15+
}
16+
717
export async function metricGitHubAppRateLimit(headers: ResponseHeaders): Promise<void> {
818
try {
919
const remaining = parseInt(headers['x-ratelimit-remaining'] as string);
@@ -13,7 +23,7 @@ export async function metricGitHubAppRateLimit(headers: ResponseHeaders): Promis
1323

1424
const updateMetric = yn(process.env.ENABLE_METRIC_GITHUB_APP_RATE_LIMIT);
1525
if (updateMetric) {
16-
const appId = await getParameter(process.env.PARAMETER_GITHUB_APP_ID_NAME);
26+
const appId = await getAppId();
1727
const metric = createSingleMetric('GitHubAppRateLimitRemaining', MetricUnit.Count, remaining, {
1828
AppId: appId,
1929
});

0 commit comments

Comments
 (0)