-
Notifications
You must be signed in to change notification settings - Fork 0
⚡ Optimize GitHub events fetch to fail fast on rate limit #292
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -685,9 +685,21 @@ export const fetchActivity = cache(async function fetchActivity( | |
| // Suppress unhandled promise rejections for subsequent pages if we break early or throw | ||
| promises.forEach((p) => p.catch((e) => logger.error("Event fetch promise rejected:", e))); | ||
|
|
||
| // A promise that rejects immediately if a critical error occurs | ||
| const failFast = Promise.all( | ||
| promises.map((p) => | ||
| p.catch((e) => { | ||
| if (e instanceof UserNotFoundError || e instanceof RateLimitError) { | ||
| throw e; | ||
| } | ||
| return null; | ||
| }) | ||
| ) | ||
| ).then(() => new Promise<never>(() => {})); // Never resolves to prevent Promise.race from resolving with nulls | ||
|
|
||
| for (const p of promises) { | ||
| try { | ||
| const events = await p; | ||
| const events = await Promise.race([p, failFast]); | ||
|
Comment on lines
+689
to
+702
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Prompt To Fix With AIThis is a comment left during a code review.
Path: src/lib/github.ts
Line: 689-702
Comment:
**failFast が「中断予定のイテレーション」でも早期エラーを発火させてしまう**
`failFast` は全 3 ページのいずれかが `RateLimitError` / `UserNotFoundError` を投げた時点で即座に reject します。しかし `Promise.race([p, failFast])` はループの **第 1 イテレーション(page 1 の待機中)** でも使われるため、たとえば「page 1 が 50 件を返してくれれば `break` できる」状況であっても、page 3 が先に `RateLimitError` を返すと `failFast` が先に reject して `throw` に至ります。元のコードでは page 1 が解決した後に `break` が実行され成功していたケースです。PR の説明には「成功ケースのロジックや動作は変えない」と記載されていますが、このタイミング依存の競合状態により、元コードでデータを返せていたシナリオで例外をスローする可能性があります。
How can I resolve this? If you propose a fix, please make it concise. |
||
| allEvents.push(...events); | ||
| if (events.length < 100) break; | ||
| } catch (error) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
failFastpromise implementation usingPromise.allon the mappedpromisesarray is clever, but it has a minor efficiency drawback:Promise.allwill hold onto the full result arrays (GitHubEvent[]) of all successful pages until every page has settled. While manageable for 3 pages, a more memory-efficient approach for a 'fail-fast' monitor is to usePromise.racewith a map that only returns rejections or never-resolving promises for successes. This ensures the monitor doesn't keep references to the data fetched by other concurrent requests. I have updated the suggestion to include explicit return types and to log suppressed errors for better maintainability and debugging.References