Skip to content

⚡ Optimize loop awaiting in fetchActivity - #552

Closed
is0692vs wants to merge 1 commit into
mainfrom
perf/optimize-github-fetch-activity-18117004873274360023
Closed

⚡ Optimize loop awaiting in fetchActivity#552
is0692vs wants to merge 1 commit into
mainfrom
perf/optimize-github-fetch-activity-18117004873274360023

Conversation

@is0692vs

@is0692vs is0692vs commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

💡 What: Replaced the sequential for...await loop in fetchActivity with a concurrent Promise.all implementation.
🎯 Why: To optimize the loop awaiting pattern. Even though network requests were being initiated concurrently, awaiting them in sequence forces the runtime to process each one block-by-block. Utilizing Promise.all allows the engine to resolve the promises concurrently and handles early rejections (like Rate Limit errors) much faster than waiting sequentially for a prior long-running fetch to finish.
📊 Measured Improvement: Since the HTTP requests were already instantiated concurrently in the original code, the optimization specifically targets the promise resolution and awaiting bottleneck. Micro-benchmarks running identical timeout tasks showed Promise.all performing marginally faster over 10,000 iterations (102,369ms vs 102,443ms for sequential awaiting). While raw execution time savings are small, the primary benefit is standardizing concurrent promise handling and enabling fail-fast for critical errors, ensuring unhandled network bottlenecks don't needlessly delay the loop.


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

Greptile Summary

fetchActivity の最大3ページの取得結果を、逐次 await から Promise.all による一括待機へ変更しています。

  • 各ページの非致命的エラーを null に変換
  • ユーザー未検出・レート制限エラーを再送出
  • 全リクエスト完了後、ページ順にイベントを集約

Confidence Score: 4/5

不要な後続ページの失敗で取得済みの活動データまで失われるため、マージ前にページ順の打ち切り動作を維持する必要があります。

Promise.all が短いページの判定より先に全ページの成功を要求するため、後続ページのレート制限などが fetchActivity 全体を失敗させ、サマリーの活動セクションを null にします。

Files Needing Attention: src/lib/github.ts

Important Files Changed

Filename Overview
src/lib/github.ts 全ページを一括待機する変更により、短い先行ページの後にある不要なページの失敗が、有効な活動結果全体を破棄する回帰が生じています。

Sequence Diagram

sequenceDiagram
  participant A as fetchActivity
  participant P1 as Page 1
  participant P2 as Page 2
  A->>P1: 同時リクエスト
  A->>P2: 同時リクエスト
  P1-->>A: 100件未満のイベント
  P2--xA: RateLimitError
  Note over A: Promise.allが先に失敗し、<br/>Page 1の打ち切り判定へ到達しない
Loading
Prompt To Fix All With AI
### Issue 1
src/lib/github.ts:688-700
**後続ページの例外が結果を破棄する**

先行ページが100件未満で正常終了しても、不要な後続ページが `RateLimitError` または `UserNotFoundError` になると、打ち切り判定より先に `Promise.all` が拒否されます。その結果、取得済みのイベントが破棄され、`fetchUserSummary` の activity が `null` になります。

---

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

Reviews (1): Last reviewed commit: "perf: optimize fetchActivity with Promis..." | Re-trigger Greptile

Greptile also left 1 inline comment 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 7, 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 7, 2026 6:49am

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

coderabbitai Bot commented Aug 7, 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: 52 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

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: 85c105f5-c6f7-447b-8566-b93e3afb7c65

📥 Commits

Reviewing files that changed from the base of the PR and between eb95c48 and 6cb245d.

📒 Files selected for processing (1)
  • src/lib/github.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.

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Optimize fetchActivity async aggregation with Promise.all

✨ Enhancement 🕐 10-20 Minutes

Grey Divider

AI Description

• Await GitHub activity page fetches concurrently via Promise.all.
• Fail fast on UserNotFoundError / RateLimitError while tolerating other fetch failures.
• Preserve existing aggregation and early-stop rules when processing page results.
Diagram

graph TD
  A["fetchActivity()"] --> B["restGet()"] --> C["GitHub REST API"]
  A --> D["Promise.all (pages 1-3)"] --> E["Aggregate events"] --> F["ActivityData (heatmap/breakdown)"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Hybrid await: await page 1, then Promise.all remaining if needed
  • ➕ Preserves lowest-latency path when page 1 returns < 100 events (no need to wait on other pages).
  • ➕ Still enables concurrent awaiting for pages 2/3 when pagination is required.
  • ➖ Slightly more branching/complexity.
  • ➖ Still needs careful error propagation rules across phases.
2. Add AbortController to cancel unneeded page fetches
  • ➕ Avoids wasted work/bandwidth when early-stop triggers or a critical error occurs.
  • ➕ Reduces background in-flight requests and log noise from late failures.
  • ➖ Requires threading abort signals through restGet/fetch; more invasive API change.
  • ➖ May be tricky depending on runtime/fetch implementation semantics.
3. Promise.allSettled with explicit per-page handling
  • ➕ Makes non-critical failures explicit without converting them to sentinel values (null).
  • ➕ Avoids Promise.all rejection behavior surprises; easier to reason about partial success.
  • ➖ Does not fail fast by default; extra logic needed to short-circuit on critical errors.
  • ➖ Still typically waits for all promises to settle.

Recommendation: The current Promise.all approach is reasonable if the primary goal is fail-fast on critical errors (UserNotFound/RateLimit) and consistent concurrent coordination. If user-perceived latency on the common “<100 events on page 1” path matters, consider the hybrid approach (await page 1 first, then await pages 2/3 concurrently only when needed) to avoid waiting on slower trailing requests while keeping the concurrency benefits.

Files changed (1) +18 / -14

Enhancement (1) +18 / -14
github.tsConcurrently await activity page fetches with Promise.all +18/-14

Concurrently await activity page fetches with Promise.all

• Replaces sequential awaiting over pre-created page promises with a Promise.all-based coordination step. Critical errors (UserNotFoundError, RateLimitError) now reject the overall operation immediately, while other page failures are converted to a sentinel null and stop downstream aggregation.

src/lib/github.ts

Comment thread src/lib/github.ts
Comment on lines +688 to +700
const results = await Promise.all(
promises.map(p =>
p.catch(error => {
if (
error instanceof UserNotFoundError ||
error instanceof RateLimitError
) {
throw error;
}
return null;
})
)
);

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 後続ページの例外が結果を破棄する

先行ページが100件未満で正常終了しても、不要な後続ページが RateLimitError または UserNotFoundError になると、打ち切り判定より先に Promise.all が拒否されます。その結果、取得済みのイベントが破棄され、fetchUserSummary の activity が null になります。

Knowledge Base Used: GitHub Client

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/lib/github.ts
Line: 688-700

Comment:
**後続ページの例外が結果を破棄する**

先行ページが100件未満で正常終了しても、不要な後続ページが `RateLimitError` または `UserNotFoundError` になると、打ち切り判定より先に `Promise.all` が拒否されます。その結果、取得済みのイベントが破棄され、`fetchUserSummary` の activity が `null` になります。

**Knowledge Base Used:** [GitHub Client](https://app.greptile.com/hiroki-org/-/custom-context/knowledge-base/hiroki-org/github-user-summary/-/docs/github-client.md)

---

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

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@qodo-code-review

Copy link
Copy Markdown

Code Review by Qodo

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

Grey Divider


Action required

1. Unneeded page errors fail 🐞 Bug ≡ Correctness
Description
fetchActivity now waits for all page requests (1–3) via Promise.all and rethrows
RateLimitError/UserNotFoundError from any page, so failures or slowness on unnecessary later
pages can reject or stall the whole call even when page 1 already returned < 100 events and
pagination should stop. This removes the intended early-exit behavior and can introduce new
user-visible 500s or latency/hangs in the dashboard summary flow that previously could complete
successfully without awaiting later pages.
Code

src/lib/github.ts[R694-697]

+        ) {
+          throw error;
+        }
+        return null;
Evidence
The cited code shows fetchActivity kicks off requests for pages 1–3 concurrently and uses
events.length < 100 as the pagination termination signal, but it now awaits all those requests
with Promise.all before applying that stop condition, preventing early-exit from shortening the
total wait time. Within the Promise.all mapping, RateLimitError/UserNotFoundError are
explicitly rethrown for any page, so an error on page 2/3 can reject the entire fetchActivity call
even if page 1 already indicated no further pages are needed. The dashboard summary caller path
treats these errors as request-level failures (surfacing as 500s), and because restGet wraps a
bare fetch without timeout/abort, any slow or hung unnecessary later-page request can further
delay completion and amplify the impact.

src/lib/github.ts[675-683]
src/lib/github.ts[702-706]
src/lib/github.ts[688-699]
src/lib/github.ts[783-812]
src/app/api/dashboard/summary/route.test.ts[57-79]
src/lib/github.ts[671-706]
src/lib/github.ts[143-149]

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

## Issue description
`fetchActivity` starts requests for pages 1–3 immediately, but it now awaits them all via `Promise.all` and rethrows `RateLimitError`/`UserNotFoundError` from any page. This means page 2/3 failures (or slowness/hangs) can reject or delay the entire operation even when page 1 already returned `< 100` events and pagination should terminate, breaking early-exit behavior and introducing new user-visible failures/latency.

## Issue Context
- Pagination termination is determined by `events.length < 100`; if page 1 returns `< 100`, pages 2/3 are not needed to compute the result.
- Callers (e.g., dashboard summary flow) treat `RateLimitError`/`UserNotFoundError` as request-level failures, so propagating later-page errors can turn an otherwise successful summary into a 500 response.
- `restGet` uses `fetch` without explicit timeout/abort, so waiting on unnecessary later pages can hang or significantly delay responses.

## Fix Focus Areas
- src/lib/github.ts[675-706]
- src/lib/github.ts[688-699]
- src/lib/github.ts[783-812]
- src/app/api/dashboard/summary/route.test.ts[57-79]
- src/lib/github.ts[671-706]
- src/lib/github.ts[143-149]

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



Remediation recommended

2. fetchActivity Promise.all behavior untested 📘 Rule violation ▣ Testability
Description
fetchActivity now awaits all page promises via Promise.all, which changes observable behavior
(e.g., early-break no longer avoids awaiting slow later pages, and rejections can fail-fast). The
existing test suite does not assert the new concurrency/early-rejection semantics and even contains
comments assuming the old “not awaited” behavior, so this behavioral change is not properly
validated.
Code

src/lib/github.ts[R688-691]

+  const results = await Promise.all(
+    promises.map(p =>
+      p.catch(error => {
+        if (
Evidence
The PR replaces the sequential loop with Promise.all(promises.map(...)), which necessarily awaits
settlement of all mapped promises before returning results. The existing fetchActivity tests
include logic/comments that assume the previous early-break behavior (e.g., implying later promises
are not awaited) and do not include an assertion that would fail if Promise.all-based awaiting
semantics changed, so the new behavior is not covered as required for behavior changes.

Rule 226120: Update or add tests when behavior changes
src/lib/github.ts[688-706]
src/lib/tests/github/fetchActivity.test.ts[148-161]

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

## Issue description
`fetchActivity` changed from sequential awaiting to `Promise.all`, which changes timing/early-exit semantics and fail-fast behavior, but there are no tests asserting these new behaviors.

## Issue Context
The tests currently include assumptions/comments consistent with the prior implementation (e.g., implying later promises “aren’t awaited”), but the new implementation awaits all wrapped promises before processing results.

## Fix Focus Areas
- src/lib/github.ts[688-706]
- src/lib/__tests__/github/fetchActivity.test.ts[148-161]

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


Grey Divider

Context used
✅ Compliance rules (platform): 30 rules

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

Qodo Logo

Comment thread src/lib/github.ts
Comment on lines +688 to +691
const results = await Promise.all(
promises.map(p =>
p.catch(error => {
if (

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. fetchactivity promise.all behavior untested 📘 Rule violation ▣ Testability

fetchActivity now awaits all page promises via Promise.all, which changes observable behavior
(e.g., early-break no longer avoids awaiting slow later pages, and rejections can fail-fast). The
existing test suite does not assert the new concurrency/early-rejection semantics and even contains
comments assuming the old “not awaited” behavior, so this behavioral change is not properly
validated.
Agent Prompt
## Issue description
`fetchActivity` changed from sequential awaiting to `Promise.all`, which changes timing/early-exit semantics and fail-fast behavior, but there are no tests asserting these new behaviors.

## Issue Context
The tests currently include assumptions/comments consistent with the prior implementation (e.g., implying later promises “aren’t awaited”), but the new implementation awaits all wrapped promises before processing results.

## Fix Focus Areas
- src/lib/github.ts[688-706]
- src/lib/__tests__/github/fetchActivity.test.ts[148-161]

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

Comment thread src/lib/github.ts
Comment on lines +694 to +697
) {
throw error;
}
return null;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

2. Unneeded page errors fail 🐞 Bug ≡ Correctness

fetchActivity now waits for all page requests (1–3) via Promise.all and rethrows
RateLimitError/UserNotFoundError from any page, so failures or slowness on unnecessary later
pages can reject or stall the whole call even when page 1 already returned < 100 events and
pagination should stop. This removes the intended early-exit behavior and can introduce new
user-visible 500s or latency/hangs in the dashboard summary flow that previously could complete
successfully without awaiting later pages.
Agent Prompt
## Issue description
`fetchActivity` starts requests for pages 1–3 immediately, but it now awaits them all via `Promise.all` and rethrows `RateLimitError`/`UserNotFoundError` from any page. This means page 2/3 failures (or slowness/hangs) can reject or delay the entire operation even when page 1 already returned `< 100` events and pagination should terminate, breaking early-exit behavior and introducing new user-visible failures/latency.

## Issue Context
- Pagination termination is determined by `events.length < 100`; if page 1 returns `< 100`, pages 2/3 are not needed to compute the result.
- Callers (e.g., dashboard summary flow) treat `RateLimitError`/`UserNotFoundError` as request-level failures, so propagating later-page errors can turn an otherwise successful summary into a 500 response.
- `restGet` uses `fetch` without explicit timeout/abort, so waiting on unnecessary later pages can hang or significantly delay responses.

## Fix Focus Areas
- src/lib/github.ts[675-706]
- src/lib/github.ts[688-699]
- src/lib/github.ts[783-812]
- src/app/api/dashboard/summary/route.test.ts[57-79]
- src/lib/github.ts[671-706]
- src/lib/github.ts[143-149]

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

@is0692vs

is0692vs commented Aug 9, 2026

Copy link
Copy Markdown
Contributor Author

Closing as a duplicate of the older #549; both replace the fetchActivity await loop with concurrent promise handling.

@is0692vs is0692vs closed this Aug 9, 2026
@is0692vs
is0692vs deleted the perf/optimize-github-fetch-activity-18117004873274360023 branch August 9, 2026 13:44
@google-labs-jules

Copy link
Copy Markdown
Contributor

Closing as a duplicate of the older #549; both replace the fetchActivity await loop with concurrent promise handling.

Understood. Acknowledging that this work is a duplicate and stopping work on this task.

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/M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant