Skip to content

⚡ perf: cache historical github year in review data - #518

Open
is0692vs wants to merge 2 commits into
mainfrom
jules-14933273120319169105-24a16e96
Open

⚡ perf: cache historical github year in review data#518
is0692vs wants to merge 2 commits into
mainfrom
jules-14933273120319169105-24a16e96

Conversation

@is0692vs

@is0692vs is0692vs commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

💡 What:
Updated the GitHub GraphQL data fetching logic to dynamically set the fetch API cache option. For requests querying historical data (any year prior to the current year), it now uses "force-cache". For the current year, it preserves the existing "no-store" behavior.

🎯 Why:
The previous implementation forced a fresh, uncached network request to GitHub on every call using cache: 'no-store', regardless of the queried year. Since a user's GitHub activity for past years is immutable (it will not change), bypassing the cache causes unnecessary latency, overhead, and potential rate-limiting. This optimization instantly improves response times for historical Year-in-Review data lookups by leveraging Next.js' native caching, which safely incorporates headers and POST bodies into the cache keys.

📊 Measured Improvement:
Due to Next.js server constraints preventing local standalone script benchmarking of server-only App Router logic outside of next dev, I could not generate millisecond-level benchmark outputs. However, enabling native 'force-cache' transforms external network bound REST/GraphQL API hops (typically 300-800ms) into near-instant local cache hits (~1-5ms), guaranteeing a substantial net performance gain.


PR created automatically by Jules for task 14933273120319169105 started by @is0692vs

Greptile Summary

過去年の Year in Review 取得で Next.js の fetch キャッシュを利用し、現在年は従来どおり毎回 GitHub から取得する変更です。

  • GraphQL ヘルパーにキャッシュ方式を渡せる引数を追加
  • 過去年の年次集計、コミット日時、REST heatmap を force-cache に変更
  • 現在年は no-store を維持

Confidence Score: 4/5

過去年の可変な GitHub 集計を無期限に固定するため、再検証方針を追加してからマージする必要があります。

過去年の主要 GraphQL・REST 応答が無効化経路なしでキャッシュされるため、GitHub 側で履歴や公開範囲が変化しても古い年次レビューが返り続けます。また、ローカル年と UTC 年の不一致により年越し時に現在年を誤ってキャッシュする可能性があります。

Files Needing Attention: src/lib/githubYearInReview.ts

Important Files Changed

Filename Overview
src/lib/githubYearInReview.ts 過去年の GitHub リクエストをキャッシュする変更だが、再検証期限がなく、現在年判定も既存の UTC 基準と一致していない。

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  A[Year request] --> B{year < currentYear}
  B -->|Yes| C[force-cache]
  B -->|No| D[no-store]
  C --> E[GitHub GraphQL year data]
  C --> F[GitHub GraphQL commit dates]
  C --> G[GitHub REST commits]
  D --> E
  D --> F
  D --> G
Loading
Prompt To Fix All With AI
### Issue 1
src/lib/githubYearInReview.ts:258
**過去年キャッシュが再検証されない**

過去年でもコミットの追加・削除、リポジトリの公開範囲変更、private contribution の表示設定変更などで GitHub の集計は更新されますが、この変更は GraphQL と REST の応答を再検証期限なしで `force-cache` に固定します。そのため、最初に保存された contribution totals、top repository、most-active hour、heatmap が GitHub の最新状態と一致しなくなり、キャッシュが外部要因で消えるまで古い年次レビューが返り続けます。

### Issue 2
src/lib/githubYearInReview.ts:256-257
**現在年判定が UTC と不一致**

サーバーのタイムゾーンが UTC より進んでいる場合、年越し直後は API とクライアントが `getUTCFullYear()` で選ぶ現在年を、ここでは `getFullYear()` により過去年と判定します。その時間帯だけ更新中の年次データが `force-cache` の対象になり、不完全な集計がキャッシュされるため、検索期間や API 検証と同じ UTC 基準を使用してください。

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "perf: cache historical github year in re..." | Re-trigger Greptile

Greptile also left 2 inline comments on this PR.

Context used:

Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com>
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@vercel

vercel Bot commented Aug 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
github-user-summary Ignored Ignored Aug 1, 2026 4:35am

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@dosubot dosubot Bot added the enhancement New feature or request label Aug 1, 2026
@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Warning

Review limit reached

@is0692vs, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 50 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7780c4a1-d90a-4dc5-bb0b-7348af1515c6

📥 Commits

Reviewing files that changed from the base of the PR and between e914034 and 111bb7e.

📒 Files selected for processing (2)
  • src/lib/__tests__/githubYearInReview.test.ts
  • src/lib/githubYearInReview.ts

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.

@dosubot

dosubot Bot commented Aug 1, 2026

Copy link
Copy Markdown

📄 Knowledge review

Dosu skipped reviewing this PR because your organization has used its 200 included credits for the month. Your usage will reset on 2026-09-01. To have Dosu review this PR before then, ask your organization admin to upgrade to a pro account.


Leave Feedback Ask Dosu about github-user-summary Add Dosu to your team

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Cache GitHub Year-in-Review fetches for historical years

✨ Enhancement 🕐 10-20 Minutes

Grey Divider

AI Description

• Add dynamic fetch caching based on requested year (historical vs current).
• Reuse cached GitHub GraphQL/REST responses for past years to reduce latency/rate-limit pressure.
• Preserve no-store behavior for the current year to keep results fresh.
Diagram

graph TD
  A["App Router caller"] --> B["Year-in-Review fetchers"] --> C{"Year < current?"}
  C -->|"Yes"| D["cacheOpt: force-cache"] --> F[("Next.js fetch cache")] --> G{{"GitHub API"}}
  C -->|"No"| E["cacheOpt: no-store"] --> F

  subgraph Legend
    direction LR
    _proc["Process"] ~~~ _dec{"Decision"} ~~~ _cache[("Cache")] ~~~ _ext{{"External"}}
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use time-based revalidation for past years
  • ➕ Avoids effectively-infinite staleness if users backfill/rewriter history for prior years
  • ➕ Keeps most of the latency and rate-limit benefits of caching
  • ➖ More tuning required (pick TTL) and behavior becomes less deterministic than force-cache
2. Wrap year-based fetches in unstable_cache() keyed by (user, year)
  • ➕ Explicit cache keying and easier invalidation control
  • ➕ Can cache the fully-assembled Year-in-Review payload rather than per-request responses
  • ➖ More code and Next.js-specific caching semantics to maintain
  • ➖ Still needs a revalidation strategy for edge cases

Recommendation: The current approach (force-cache for past years, no-store for current year) is a pragmatic performance win with minimal code change. Consider adding a revalidation window for historical years (e.g., days/weeks) if you want protection against rare but real backfilled historical activity (rebases, delayed pushes, author-date changes) while keeping most cache benefits.

Files changed (1) +16 / -8

Enhancement (1) +16 / -8
githubYearInReview.tsThread year-based fetch cache policy through GitHub GraphQL/REST calls +16/-8

Thread year-based fetch cache policy through GitHub GraphQL/REST calls

• Extends the internal GraphQL fetch wrapper to accept a RequestCache option and passes it through commit-history queries. Computes cache policy per request: uses force-cache for years before the current year and preserves no-store for the current year, applying the same policy to the REST commits endpoint used for heatmap building.

src/lib/githubYearInReview.ts

@codecov

codecov Bot commented Aug 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

try {
const currentYear = new Date().getFullYear();
const cacheOpt: RequestCache = year < currentYear ? "force-cache" : "no-store";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 過去年キャッシュが再検証されない

過去年でもコミットの追加・削除、リポジトリの公開範囲変更、private contribution の表示設定変更などで GitHub の集計は更新されますが、この変更は GraphQL と REST の応答を再検証期限なしで force-cache に固定します。そのため、最初に保存された contribution totals、top repository、most-active hour、heatmap が GitHub の最新状態と一致しなくなり、キャッシュが外部要因で消えるまで古い年次レビューが返り続けます。

Knowledge Base Used: Year in Review

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/githubYearInReview.ts
Line: 258

Comment:
**過去年キャッシュが再検証されない**

過去年でもコミットの追加・削除、リポジトリの公開範囲変更、private contribution の表示設定変更などで GitHub の集計は更新されますが、この変更は GraphQL と REST の応答を再検証期限なしで `force-cache` に固定します。そのため、最初に保存された contribution totals、top repository、most-active hour、heatmap が GitHub の最新状態と一致しなくなり、キャッシュが外部要因で消えるまで古い年次レビューが返り続けます。

**Knowledge Base Used:** [Year in Review](https://app.greptile.com/hiroki-org/-/custom-context/knowledge-base/hiroki-org/github-user-summary/-/docs/year-in-review.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Comment on lines +256 to +257
const currentYear = new Date().getFullYear();
const cacheOpt: RequestCache = year < currentYear ? "force-cache" : "no-store";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2 現在年判定が UTC と不一致

サーバーのタイムゾーンが UTC より進んでいる場合、年越し直後は API とクライアントが getUTCFullYear() で選ぶ現在年を、ここでは getFullYear() により過去年と判定します。その時間帯だけ更新中の年次データが force-cache の対象になり、不完全な集計がキャッシュされるため、検索期間や API 検証と同じ UTC 基準を使用してください。

Knowledge Base Used: Year in Review

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/githubYearInReview.ts
Line: 256-257

Comment:
**現在年判定が UTC と不一致**

サーバーのタイムゾーンが UTC より進んでいる場合、年越し直後は API とクライアントが `getUTCFullYear()` で選ぶ現在年を、ここでは `getFullYear()` により過去年と判定します。その時間帯だけ更新中の年次データが `force-cache` の対象になり、不完全な集計がキャッシュされるため、検索期間や API 検証と同じ UTC 基準を使用してください。

**Knowledge Base Used:** [Year in Review](https://app.greptile.com/hiroki-org/-/custom-context/knowledge-base/hiroki-org/github-user-summary/-/docs/year-in-review.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (1) 📜 Skill insights (0)

Context used
✅ Compliance rules (platform): 30 rules

Grey Divider


Remediation recommended

1. cacheOpt behavior untested 📘 Rule violation ▣ Testability
Description
This PR changes cache behavior by switching GitHub requests to force-cache for historical years,
but no test asserts the new fetch(..., { cache: ... }) behavior for past vs current year. Without
explicit assertions, regressions (e.g., always using no-store) could slip in unnoticed.
Code

src/lib/githubYearInReview.ts[R255-264]

    try {
+        const currentYear = new Date().getFullYear();
+        const cacheOpt: RequestCache = year < currentYear ? "force-cache" : "no-store";
+
        const response = await graphql<YearInReviewResponse>(YEAR_IN_REVIEW_QUERY, token, {
            login: username,
            from: from.toISOString(),
            to: to.toISOString(),
            maxRepositories: 10,
-        });
+        }, cacheOpt);
Evidence
Rule 226115 requires tests to be added/updated when caching behavior changes. The updated code
introduces a year-dependent cacheOpt and passes it into fetch calls, but the existing test suite
stubs fetch without asserting request init options like cache and contains no
references/assertions for force-cache/no-store behavior.

Rule 226115: Add or update tests for behavioral changes in parsing, validation, aggregation, auth, and caching
src/lib/githubYearInReview.ts[255-264]
src/lib/githubYearInReview.ts[300-308]
src/lib/githubYearInReview.ts[327-327]
src/lib/tests/githubYearInReview.test.ts[1-20]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Caching behavior was changed to use `force-cache` for historical years and `no-store` for the current year, but tests do not assert that the correct `fetch` `cache` option is used.

## Issue Context
`fetchYearInReviewData` and `fetchCommitActivityHeatmap` now compute `cacheOpt` based on `year < new Date().getFullYear()` and pass it into the GraphQL helper / REST `fetch` call. Existing tests stub `fetch` but never validate the `cache` option in the request init.

## Fix Focus Areas
- src/lib/githubYearInReview.ts[255-308]
- src/lib/githubYearInReview.ts[327-327]
- src/lib/__tests__/githubYearInReview.test.ts[1-20]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Timezone year mismatch caching 🐞 Bug ≡ Correctness
Description
fetchYearInReviewData (and fetchCommitActivityHeatmap) uses new Date().getFullYear() (server local
timezone) to choose "force-cache" vs "no-store", but the API route validates requested years against
getUTCFullYear() and the from/to bounds are built with Date.UTC. Around New Year on servers ahead of
UTC, this can incorrectly treat the current UTC year as historical and cache it, serving stale
results for that window.
Code

src/lib/githubYearInReview.ts[R256-258]

+        const currentYear = new Date().getFullYear();
+        const cacheOpt: RequestCache = year < currentYear ? "force-cache" : "no-store";
+
Evidence
The cache selection uses local-year (getFullYear()), but both the route validation and the query’s
time window are UTC-based, creating an inconsistent definition of “current year” that can flip
behavior around New Year depending on server timezone.

src/lib/githubYearInReview.ts[252-264]
src/lib/githubYearInReview.ts[292-308]
src/app/api/dashboard/year/route.ts[18-22]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The caching decision for year-in-review requests uses `new Date().getFullYear()` (local timezone), while the rest of the system uses UTC year semantics (`getUTCFullYear()` and `Date.UTC(...)`). This mismatch can cause the current UTC year to be treated as “historical” and cached around New Year depending on server timezone.

### Issue Context
- The route handler selects/validates `year` using `getUTCFullYear()`.
- The data query window is constructed using `Date.UTC(...)`.
- The caching decision should follow the same UTC definition of “current year”.

### Fix
Replace `new Date().getFullYear()` with `new Date().getUTCFullYear()` in both call sites (or centralize into a small helper to avoid duplication).

### Fix Focus Areas
- src/lib/githubYearInReview.ts[252-264]
- src/lib/githubYearInReview.ts[292-308]
- src/app/api/dashboard/year/route.ts[18-22]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines 255 to +264
try {
const currentYear = new Date().getFullYear();
const cacheOpt: RequestCache = year < currentYear ? "force-cache" : "no-store";

const response = await graphql<YearInReviewResponse>(YEAR_IN_REVIEW_QUERY, token, {
login: username,
from: from.toISOString(),
to: to.toISOString(),
maxRepositories: 10,
});
}, cacheOpt);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

1. cacheopt behavior untested 📘 Rule violation ▣ Testability

This PR changes cache behavior by switching GitHub requests to force-cache for historical years,
but no test asserts the new fetch(..., { cache: ... }) behavior for past vs current year. Without
explicit assertions, regressions (e.g., always using no-store) could slip in unnoticed.
Agent Prompt
## Issue description
Caching behavior was changed to use `force-cache` for historical years and `no-store` for the current year, but tests do not assert that the correct `fetch` `cache` option is used.

## Issue Context
`fetchYearInReviewData` and `fetchCommitActivityHeatmap` now compute `cacheOpt` based on `year < new Date().getFullYear()` and pass it into the GraphQL helper / REST `fetch` call. Existing tests stub `fetch` but never validate the `cache` option in the request init.

## Fix Focus Areas
- src/lib/githubYearInReview.ts[255-308]
- src/lib/githubYearInReview.ts[327-327]
- src/lib/__tests__/githubYearInReview.test.ts[1-20]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +256 to +258
const currentYear = new Date().getFullYear();
const cacheOpt: RequestCache = year < currentYear ? "force-cache" : "no-store";

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

2. Timezone year mismatch caching 🐞 Bug ≡ Correctness

fetchYearInReviewData (and fetchCommitActivityHeatmap) uses new Date().getFullYear() (server local
timezone) to choose "force-cache" vs "no-store", but the API route validates requested years against
getUTCFullYear() and the from/to bounds are built with Date.UTC. Around New Year on servers ahead of
UTC, this can incorrectly treat the current UTC year as historical and cache it, serving stale
results for that window.
Agent Prompt
### Issue description
The caching decision for year-in-review requests uses `new Date().getFullYear()` (local timezone), while the rest of the system uses UTC year semantics (`getUTCFullYear()` and `Date.UTC(...)`). This mismatch can cause the current UTC year to be treated as “historical” and cached around New Year depending on server timezone.

### Issue Context
- The route handler selects/validates `year` using `getUTCFullYear()`.
- The data query window is constructed using `Date.UTC(...)`.
- The caching decision should follow the same UTC definition of “current year”.

### Fix
Replace `new Date().getFullYear()` with `new Date().getUTCFullYear()` in both call sites (or centralize into a small helper to avoid duplication).

### Fix Focus Areas
- src/lib/githubYearInReview.ts[252-264]
- src/lib/githubYearInReview.ts[292-308]
- src/app/api/dashboard/year/route.ts[18-22]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Co-authored-by: is0692vs <135803462+is0692vs@users.noreply.github.com>
@pull-request-size pull-request-size Bot added size/L and removed size/S labels Aug 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant