Skip to content

Cache GitHub installation tokens in memory#4912

Merged
chronark merged 16 commits intounkeyed:mainfrom
vansh-commits:fix/github-installation-token-cache
Feb 5, 2026
Merged

Cache GitHub installation tokens in memory#4912
chronark merged 16 commits intounkeyed:mainfrom
vansh-commits:fix/github-installation-token-cache

Conversation

@vansh-commits
Copy link
Contributor

What does this PR do?

This PR introduces an in-memory cache for GitHub App installation tokens to avoid
fetching a new token on every deployment-related call.

GitHub installation tokens are valid for ~1 hour, but were previously fetched
on each request. This change reuses tokens until expiry, reducing unnecessary
GitHub API calls while preserving existing behavior.

Fixes #4901

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • go test ./svc/ctrl/worker/github
  • go test ./svc/ctrl/worker/...

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build (not applicable)
  • Ran pnpm fmt (not applicable)
  • Ran make fmt on /go directory
  • Checked for warnings, there are none
  • Removed all console.logs (not applicable)
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Copilot AI review requested due to automatic review settings February 3, 2026 18:29
@vercel
Copy link

vercel bot commented Feb 3, 2026

@vansh-commits is attempting to deploy a commit to the Unkey Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 3, 2026

Caution

Review failed

The pull request is closed.

📝 Walkthrough

Walkthrough

Adds an in-process per-installation cache for GitHub installation tokens, wires it into Client initialization, and changes GetInstallationToken to use a cache-first SWR loader that generates a JWT and requests an installation token from GitHub on cache miss. Also removes DownloadRepoTarball and adjusts return types.

Changes

Cohort / File(s) Summary
GitHub client & cache
svc/ctrl/worker/github/client.go
Adds tokenCache cache.Cache[int64, InstallationToken] to Client; NewClient initializes a default cache when nil (Fresh 55m, Stale 5m, MaxSize 10k, logger, clock); GetInstallationToken now uses SWR cache loader that generates a JWT and POSTs to GitHub on miss; returns token by value to avoid aliasing.
Interface changes
svc/ctrl/worker/github/interface.go, svc/ctrl/worker/github/noop.go
GetInstallationToken signature changed to return InstallationToken (value) instead of *InstallationToken; DownloadRepoTarball removed from interface and noop implementation.
Build deps
svc/ctrl/worker/github/BUILD.bazel
Adds public dependencies //pkg/cache and //pkg/clock to the github go_library.
Run init formatting
svc/ctrl/worker/run.go
Reformatted GitHub client construction literal; no functional change (fields same, indentation adjusted).

Sequence Diagram

sequenceDiagram
    participant Caller as Caller
    participant Client as GitHubClient
    participant Cache as InstallationTokenCache
    participant GitHub as GitHub API

    Caller->>Client: GetInstallationToken(installationID)
    Client->>Cache: SWR Lookup/Load(installationID)
    alt Cache Hit (fresh)
        Cache-->>Client: InstallationToken
        Client-->>Caller: InstallationToken
    else Cache Miss or Stale
        Client->>Client: Generate JWT
        Client->>GitHub: POST /app/installations/:id/access_tokens (Bearer <JWT>)
        GitHub-->>Client: 201 Created + token payload
        Client->>Cache: Store(installationID, InstallationToken)
        Client-->>Caller: InstallationToken
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: adding in-memory caching for GitHub installation tokens.
Description check ✅ Passed The description covers what the PR does, references the linked issue #4901, specifies the change type, includes testing instructions, and completes most required checklist items.
Linked Issues check ✅ Passed The code changes implement the requirement from issue #4901 by adding per-client installation token caching, reducing GitHub API calls while preserving existing behavior [#4901].
Out of Scope Changes check ✅ Passed All changes are within scope: github/client.go adds cache implementation, BUILD.bazel updates dependencies, and run.go initializes the cache in ClientConfig.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 golangci-lint (2.5.0)

Command failed


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces in-memory caching for GitHub App installation tokens to reduce unnecessary GitHub API calls. Installation tokens are valid for approximately one hour but were previously fetched on every request. The implementation adds a global cache that reuses tokens until expiry, reducing API calls while maintaining functional correctness.

Changes:

  • Added global cache initialization in NewClient with 55-minute freshness/staleness windows
  • Modified GetInstallationToken to check cache before making GitHub API calls
  • Added cache write operation after successful token retrieval from GitHub

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@svc/ctrl/worker/github/client.go`:
- Around line 23-25: The global installationTokenCache is initialized with a
nil-check in NewClient which races when NewClient is called concurrently; add a
package-level sync.Once (e.g., installationTokenCacheOnce) and move the cache
creation into installationTokenCacheOnce.Do(...) so installationTokenCache is
created exactly once; update NewClient to call that Once.Do before using
installationTokenCache (and apply the same pattern to the related initialization
at lines 57-70), referencing the installationTokenCache and NewClient symbols.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 4 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@svc/ctrl/worker/github/client.go`:
- Around line 124-137: The cached-token branch for installationTokenCache
currently treats any non-Miss hit as valid; update the logic in the block that
calls installationTokenCache.Get(…) to verify the cached.ExpiresAt is still in
the future before returning it: after retrieving cached for installationID,
check that cached.ExpiresAt.After(time.Now()) (or equivalent) and only then log
via c.logger.Debug and return a copy of token; if expired, treat it as a cache
miss (do not return) so the code will fallthrough to refresh the token. Ensure
you still return a copy of the token (token := cached; return &token, nil) when
valid.
🧹 Nitpick comments (1)
svc/ctrl/worker/github/client.go (1)

29-29: Rename to errInstallationTokenCache per Go conventions.

Static analysis flags this: sentinel error names should follow errXxx format.

Proposed fix
 var (
 	installationTokenCache     cache.Cache[int64, InstallationToken]
 	installationTokenCacheOnce sync.Once
-	installationTokenCacheErr  error
+	errInstallationTokenCache  error
 )

Update usage at lines 63 and 75:

-		installationTokenCache, installationTokenCacheErr = cache.New(
+		installationTokenCache, errInstallationTokenCache = cache.New(

-	if installationTokenCacheErr != nil {
-		return nil, installationTokenCacheErr
+	if errInstallationTokenCache != nil {
+		return nil, errInstallationTokenCache

Copy link
Member

@Flo4604 Flo4604 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See comments

@vansh-commits
Copy link
Contributor Author

Thanks for the review! I’ve addressed the requested changes:

  1. Moved the installation token cache to be injected via ClientConfig (removed global state).
  2. Removed unnecessary nil checks, as the cache is now guaranteed to be initialized in NewClient.
  3. Refactored token retrieval to use cache.SWR instead of manual Get/Set calls.

Let me know if you’d like any further adjustments.

@Flo4604 Flo4604 self-requested a review February 4, 2026 09:23
Copy link
Member

@Flo4604 Flo4604 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

small nit otherwise lgtm

@Flo4604
Copy link
Member

Flo4604 commented Feb 4, 2026

also please run make bazel so the CI is happy

@vansh-commits
Copy link
Contributor Author

Done — ran make bazel and pushed the changes

@Flo4604
Copy link
Member

Flo4604 commented Feb 4, 2026

"svc/ctrl/worker/run.go:105:42: github.ClientConfig is missing field InstallationTokenCache (exhaustruct)"

The linter isn't happy please run make build locally it should show you any errors from bazel linting

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@svc/ctrl/worker/github/client.go`:
- Around line 110-173: GetInstallationToken currently returns a cached
InstallationToken from tokenCache.SWR without validating
InstallationToken.ExpiresAt, which can return expired tokens; update
GetInstallationToken to check the returned value's ExpiresAt against time.Now()
plus a small buffer (e.g., 5 minutes) and if the token is expired or within the
buffer, invalidate or bypass the cache and fetch a fresh token (e.g., call
tokenCache.Delete/Invalidate for installationID or re-run the SWR fetcher to
force refresh) rather than returning the stale value; reference the
GetInstallationToken function, tokenCache.SWR call, InstallationToken.ExpiresAt
field, and generateJWT flow when implementing the validation and refresh logic.

@Flo4604 Flo4604 self-requested a review February 4, 2026 21:12
Copy link
Member

@Flo4604 Flo4604 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks!

@vansh-commits
Copy link
Contributor Author

Welcome! Always happy to Contribute...

@chronark chronark merged commit 6eb8e28 into unkeyed:main Feb 5, 2026
1 of 4 checks passed
@vansh-commits vansh-commits deleted the fix/github-installation-token-cache branch February 5, 2026 08:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cache GitHub installation tokens to reduce API calls

3 participants