Skip to content

Add retry logic and improve test timing for fetchLineNames#22

Merged
TinyKitten merged 3 commits intomainfrom
claude/refresh-route-data-KWWBj
Feb 11, 2026
Merged

Add retry logic and improve test timing for fetchLineNames#22
TinyKitten merged 3 commits intomainfrom
claude/refresh-route-data-KWWBj

Conversation

@TinyKitten
Copy link
Copy Markdown
Member

@TinyKitten TinyKitten commented Feb 11, 2026

Summary

Implemented exponential backoff retry logic for failed line name fetches and refactored tests to use fake timers for more reliable async testing.

Key Changes

Source Code (hooks/use-line-names.ts)

  • Retry mechanism: Added exponential backoff retry logic with up to 3 retries at 2s, 4s, and 8s intervals
  • Request deduplication: Introduced pendingIds Set to prevent duplicate requests for IDs currently being fetched
  • State tracking: Separated fetchedIds into resolvedIds (successful fetches) and pendingIds (in-flight requests)
  • Error handling: Modified error handling to treat missing lines data in responses as retriable errors
  • Cleanup: Properly remove IDs from pendingIds on both success and failure

Tests (lib/__tests__/use-line-names.test.ts)

  • Fake timers: Enabled vi.useFakeTimers() in beforeEach and vi.useRealTimers() in afterEach for deterministic async testing
  • Helper function: Added flushPromises() utility to advance microtask queue instead of using vi.waitFor()
  • New test cases:
    • Retry behavior on fetch failures
    • Max retry limit enforcement
    • Retry reset after all retries exhausted
    • Duplicate request prevention for in-flight requests
    • Retry behavior when API response lacks lines data
  • Test updates: Replaced all vi.waitFor() calls with flushPromises() for consistency and replaced setTimeout waits with timer advancement

Configuration

  • Added package-lock.json to .gitignore

Implementation Details

  • Retries use exponential backoff delays: 2s → 4s → 8s
  • IDs are only considered "resolved" after successful fetch, allowing retry if initial fetch fails
  • Pending requests are tracked separately to avoid concurrent requests for the same ID
  • Both network errors and missing response data trigger retry logic

https://claude.ai/code/session_01AeztrVJGigrhtYXRwbWx3r

Summary by CodeRabbit

  • 改善

    • ネットワークエラー時の再試行を強化しました(指数バックオフで最大3回まで自動再試行)。
    • 同一データの重複リクエストを抑制し、無駄な問い合わせを減らしました。
    • 一部の計算結果をメモ化して再レンダリング時の負荷を低減しました。
  • テスト

    • フェイクタイマー等を用いた再試行挙動のテストを拡充しました。
  • その他

    • 開発用設定(依存管理の無視設定)を更新しました。

fetchLineNamesで取得に失敗したIDが永続的にfetchedIdsに残り、
再取得されない問題を修正。resolvedIds(取得成功済み)と
pendingIds(取得中)に分離し、失敗時は指数バックオフで
最大3回リトライする。全リトライ失敗後も新しい路線追加時に
再取得が可能になる。

https://claude.ai/code/session_01AeztrVJGigrhtYXRwbWx3r
pnpm管理のリポジトリでnpm installにより生成された
package-lock.jsonを無視するように設定。

https://claude.ai/code/session_01AeztrVJGigrhtYXRwbWx3r
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Feb 11, 2026

📝 Walkthrough

Walkthrough

パッケージロックの無視設定追加と、ライン名取得フックに対する per-id ライフサイクル追跡(pendingIds/resolvedIds)と指数バックオフによるリトライ実装を追加。テストとテスト用アクセサも拡張。

Changes

Cohort / File(s) Summary
設定ファイル
\.gitignore
package-lock.json を無視リストに追加。
フック実装
hooks/use-line-names.ts
resolvedIds / pendingIds による per-id ライフサイクル追跡を導入。fetchLineNames(ids, _retryCount = 0) に署名を拡張し、API エラーや不完全レスポンス時に最大3回の指数バックオフ(2s,4s,8s)でリトライするロジックを追加。キャッシュ更新・通知処理とテスト用アクセサ _getCache, _getColorCache, _setGqlUrl を追加。GQL URL ガード維持。
テスト
lib/__tests__/use-line-names.test.ts
Vitest のフェイクタイマー導入と flushPromises ヘルパーで非同期挙動を制御。リトライ挙動、最大回数制限、同一並行リクエストの重複抑止、レスポンス欠如時の再試行等の包括的テストを追加・更新。
UI コンポーネント
components/location-card.tsx
useMemo を用いて lineIds の算出をメモ化し、不要な再計算を回避(動作は同等)。

Sequence Diagram

sequenceDiagram
    participant Hook as Hook (use-line-names)
    participant State as State (pendingIds / resolvedIds)
    participant API as GraphQL API
    participant Cache as Cache (names / colors)
    Hook->>State: 新しいIDを検出して pendingIds に追加
    Hook->>API: fetchLineNames(ids, _retryCount)
    alt API 成功(lines あり)
        API-->>Hook: lines データ返却
        Hook->>State: pendingIds から削除し resolvedIds に追加
        Hook->>Cache: names/colors を更新
        Hook->>Hook: リスナーへ通知
    else API 失敗 または lines undefined
        API-->>Hook: エラー / 不完全レスポンス
        Hook->>State: 影響IDの pendingIds をクリア
        Hook->>Hook: リトライをスケジュール(遅延: 2000ms → 4000ms → 8000ms)
        alt retry 回数未達
            Hook->>API: 再試行(_retryCount 増加)
        else retry 上限到達
            Hook-->>Hook: 以降リトライ停止
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐇 ぴょんと跳ねてキャッシュ見るよ
pending が鳴って resolved が晴れる
2秒、4秒、8秒の鼓動で再挑戦
名前と色が揃ってリスナーが歌うよ
テストも増えて、安心のにんまり 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 33.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title "Add retry logic and improve test timing for fetchLineNames" accurately reflects the main changes: exponential backoff retry logic implementation and test timing improvements using fake timers.

✏️ 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
  • Commit unit tests in branch claude/refresh-route-data-KWWBj

No actionable comments were generated in the recent review. 🎉

🧹 Recent nitpick comments
hooks/use-line-names.ts (1)

95-100: lineIdsの順序差分で不要な再取得が起きる場合は正規化を検討。
順序や重複の影響を避けるため、重複除去+ソートした配列をkey/取得に使うと安定します。

♻️ 差分案(両フックで同様に適用)
-  const lineIdsKey = lineIds.join(",");
+  const normalizedLineIds = Array.from(new Set(lineIds)).sort();
+  const lineIdsKey = normalizedLineIds.join(",");
   useEffect(() => {
-    fetchLineNames(lineIds);
+    fetchLineNames(normalizedLineIds);
     // eslint-disable-next-line react-hooks/exhaustive-deps
   }, [lineIdsKey]);

Also applies to: 108-112


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

Copy link
Copy Markdown

@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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
hooks/use-line-names.ts (1)

92-100: ⚠️ Potential issue | 🟡 Minor

LocationCardコンポーネントで毎レンダー新しい配列が生成されることによるfetch呼び出しの増加

LocationCardコンポーネント(line 92)でconst lineIds = update.line_id ? [update.line_id] : [];として毎レンダー新しい配列を生成しており、useEffect依存配列の参照が毎回変わるため、fetchLineNamesが毎レンダーで呼び出されています。fetchLineNames内部のフィルタリング(resolvedIds/pendingIdsによる重複排除)により実際のネットワークリクエストは防がれていますが、不要な関数呼び出しと依存配列チェックが発生しています。useMemolineIdsをメモ化するか、JSON.stringify(lineIds)を依存配列に使用することで改善してください。

一方、map.tsxtimeline.tsxで使用されているstate.lineIdsはコンテキストから取得される値で、lineIds自体が変更されたときのみ配列参照が更新される設計となっているため、より効率的です。

🧹 Nitpick comments (2)
hooks/use-line-names.ts (2)

70-74: バッチ内の一部IDがレスポンスに含まれない場合の挙動について

現在の実装では、レスポンスにlines配列が存在すれば、リクエストした全てのnewIdsresolvedIdsに追加されます。しかし、APIが一部のIDのみを返した場合(例:存在しないID)、そのIDも「解決済み」として扱われ、再取得されなくなります。

これが意図した動作であれば問題ありませんが、存在しないIDを後で再取得する必要がある場合は、レスポンスに含まれるIDのみをresolvedIdsに追加することを検討してください。

♻️ レスポンスに含まれるIDのみを解決済みにする案
-      // 取得成功: resolvedIdsに追加してpendingIdsから削除
-      for (const id of newIds) {
-        resolvedIds.add(id);
-        pendingIds.delete(id);
-      }
+      // 取得成功: レスポンスに含まれるIDのみresolvedIdsに追加
+      const returnedIds = new Set(lines.map((line) => String(line.id)));
+      for (const id of newIds) {
+        if (returnedIds.has(id)) {
+          resolvedIds.add(id);
+        }
+        pendingIds.delete(id);
+      }

81-89: エラー情報のロギングを検討

現在のcatchブロックはエラー情報を無視しています。本番環境でのデバッグを容易にするため、エラーをログに記録することを検討してください。

♻️ エラーログを追加する案
-    .catch(() => {
+    .catch((error) => {
       // 取得失敗: pendingIdsから削除してリトライ
+      if (__DEV__) {
+        console.warn(`fetchLineNames failed (retry ${_retryCount}/${MAX_RETRIES}):`, error);
+      }
       for (const id of newIds) pendingIds.delete(id);

- useLineNames/useLineColorsのuseEffect依存配列をlineIds.join(",")に
  変更し、配列参照ではなく値ベースで比較するように改善
- LocationCardのlineIds配列をuseMemoでメモ化し、毎レンダーの
  新規配列生成を防止

https://claude.ai/code/session_01AeztrVJGigrhtYXRwbWx3r
@TinyKitten TinyKitten merged commit 7d5a775 into main Feb 11, 2026
1 check passed
@TinyKitten TinyKitten deleted the claude/refresh-route-data-KWWBj branch February 11, 2026 22:09
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.

2 participants