Skip to content

perf(problems): cache anonymous /problems responses at the CDN edge#3667

Merged
KATO-Hiro merged 4 commits into
stagingfrom
#3666
Jun 15, 2026
Merged

perf(problems): cache anonymous /problems responses at the CDN edge#3667
KATO-Hiro merged 4 commits into
stagingfrom
#3666

Conversation

@KATO-Hiro

@KATO-Hiro KATO-Hiro commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

close #3666

Summary by CodeRabbit

リリースノート

  • 新機能
    • 問題一覧ページで、匿名ユーザー向けのCDNキャッシュ制御(Cache-Control)を追加し、読み込みを高速化しました。
  • 改善
    • 投票統計の取得に失敗してもページ表示は継続し、キャッシュ対象の条件を見直して適切に制御するよう改善しました。
  • テスト
    • CDNキャッシュ制御(公開/共有キャッシュ有無、Set-Cookie有無など)をE2Eとユニットテストで検証するテストを追加しました。
  • ドキュメント
    • SvelteKit向けテスト手順・命名ルールを追記しました。

KATO-Hiro and others added 3 commits June 14, 2026 13:42
Anonymous responses contain no per-user answer state and are identical
for all visitors, so set Cache-Control: public, max-age=0, s-maxage=300,
stale-while-revalidate=600 for anonymous requests. Logged-in and
degraded (vote stats failure) responses are deliberately excluded.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…oute load() unit tests

- E2E: add logged-in test asserting s-maxage is absent (personalized responses must not be shared-cached)
- Unit: add tagIds branch coverage and fix header key to Cache-Control (canonical case)
- .claude/rules/testing.md: document route load() unit test pattern with setHeaders spy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

load 関数に setHeaders を追加。匿名かつ getVoteGradeStatistics 成功時のみ Cache-Control: public, s-maxage=300 を付与。分岐ロジックはユニットテスト、ワイヤー契約は E2E で検証。テストガイドに命名規則とモック例を統合。

Changes

匿名アクセス CDN キャッシュ制御

Layer / File(s) Summary
load 関数へのキャッシュ制御追加
src/routes/problems/+page.server.ts
load 引数に setHeaders を追加。getVoteGradeStatistics 失敗を voteStatsOk フラグで管理し、匿名かつ成功時のみ Cache-Control: public, max-age=0, s-maxage=300, stale-while-revalidate=600 を設定。ログイン済み・stats 失敗時は何も設定しない。
ユニットテスト基盤と検証
src/routes/problems/page_server.test.ts
getVoteGradeStatistics / getTaskResults / getTasksWithTagIds をモック化し createMockEvent ヘルパで load に渡す。(1) 匿名+成功、(2) tagIds 付き匿名+成功、(3) ログイン済み、(4) stats 失敗 の 4 ケースで setHeaders の呼び出し有無を検証。
E2E テスト: ワイヤー上のレスポンスヘッダー検証
e2e/problems_cache.spec.ts
Playwright で /problems に GET し、匿名時の cache-controlpublics-maxage=300 が含まれ set-cookie が無いこと、ログイン済み時に s-maxage が含まれないことをレスポンスヘッダーで検証。
テストガイドライン更新
.claude/rules/testing.md
ルート配下ユニットテストの命名規則(+ プレフィックス禁止、page_server.test.ts 推奨)と、setHeadersvi.fn() でスパイする手順、E2E との役割分担、モックイベント例を追記。

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

📦 匿名の旅人、CDN に宿を取り
s-maxage=300 の秒、静かに眠る
ログイン済みには鍵をかけて
setHeaders 一撃、漏洩ゼロへ
キャッシュと守護、ともに走る 🚀

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed PR タイトルは「perf(problems): cache anonymous /problems responses at the CDN edge」で、変更の中心である匿名アクセスの /problems エンドポイントに対する CDN キャッシュ実装を明確に説明している。
Linked Issues check ✅ Passed 変更内容が Issue #3666 の全コーディング目標をカバーしている: (1)setHeaders で匿名時のみ Cache-Control ヘッダを設定 (2)ログイン時はキャッシュ非対象(セキュリティ分離) (3)vote stats 失敗時は setHeaders を呼ばず degraded 応答をキャッシュから除外 (4)タグ指定フィルタと非指定両対応 (5)ユニット+E2E テストで分岐ロジックと実ワイヤープロトコルを検証。
Out of Scope Changes check ✅ Passed 全変更が Issue #3666 の CDN キャッシュ実装スコープ内である: コア実装ファイル 1 つ、テストファイル 2 つ(unit + E2E)、ドキュメント 1 つ(testing.md)で、スコープ外の変更なし。
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
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch #3666

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.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/routes/problems/`+page.server.ts:
- Around line 40-42: The code currently sets Cache-Control headers only when
session is null and voteStatsOk is true, but does not explicitly set headers for
the complementary case (when session is not null or voteStatsOk is false). To
meet the requirement of forbidding shared caching for logged-in or degraded
states, add an else branch after the existing if statement that explicitly sets
Cache-Control headers with a directive that prevents shared caching (such as
'private' or 'private, max-age=0') rather than relying on runtime or middleware
defaults. This ensures the cache guarantee is explicitly codified in the code
for both branches.

In `@src/routes/problems/page_server.test.ts`:
- Around line 71-73: The Cache-Control header tests are using partial matching
which misses regressions of important directives. At
src/routes/problems/page_server.test.ts lines 71-73 (anchor), replace the
partial toContain assertions with a complete expectation that validates all
required directives: public, s-maxage=300, max-age=0, and
stale-while-revalidate=600 together (consider using a fixed expected string or
toMatch with a regex that ensures all directives are present). At
src/routes/problems/page_server.test.ts lines 82-84 (sibling), apply the same
complete contract validation for the tagIds case. At e2e/problems_cache.spec.ts
lines 16-17 (sibling), enhance the wire-level validation to also check for
max-age=0 and stale-while-revalidate=600 in the Cache-Control response header.
At e2e/problems_cache.spec.ts line 39 (sibling), when validating the logged-in
case, confirm not only that s-maxage is absent but also that the public
directive is not present in the Cache-Control header.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 604abd5c-8355-41e1-8577-68ae22b13274

📥 Commits

Reviewing files that changed from the base of the PR and between 8fe32e5 and 2086b34.

📒 Files selected for processing (4)
  • .claude/rules/testing.md
  • e2e/problems_cache.spec.ts
  • src/routes/problems/+page.server.ts
  • src/routes/problems/page_server.test.ts

Comment thread src/routes/problems/+page.server.ts
Comment thread src/routes/problems/page_server.test.ts Outdated
Unit: replace loose toContain() with exact toBe() for the full directive
string — partial matches let malformed headers silently pass.
E2E: add max-age=0, stale-while-revalidate=600, and public assertions to
cover the complete contract; logged-in test also asserts public is absent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

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.

♻️ Duplicate comments (1)
e2e/problems_cache.spec.ts (1)

16-19: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

匿名レスポンスの Cache-Control 検証は完全一致にしてください。

現状の toContain 連鎖だと、余計な/矛盾するディレクティブ混入を見逃せます。
ユニットテストと同様に、E2E でもヘッダー契約を1本で固定するのが安全です。

差分例
-    expect(headers['cache-control']).toContain('public');
-    expect(headers['cache-control']).toContain('max-age=0');
-    expect(headers['cache-control']).toContain('s-maxage=300');
-    expect(headers['cache-control']).toContain('stale-while-revalidate=600');
+    expect(headers['cache-control']).toBe(
+      'public, max-age=0, s-maxage=300, stale-while-revalidate=600',
+    );
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@e2e/problems_cache.spec.ts` around lines 16 - 19, Replace the four separate
`expect(...).toContain()` assertions in the anonymous response cache validation
(lines checking for 'public', 'max-age=0', 's-maxage=300', and
'stale-while-revalidate=600') with a single exact match assertion that verifies
the complete Cache-Control header value equals the full directive string. This
ensures no extraneous or conflicting directives can slip through undetected,
matching the same validation pattern used in unit tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@e2e/problems_cache.spec.ts`:
- Around line 16-19: Replace the four separate `expect(...).toContain()`
assertions in the anonymous response cache validation (lines checking for
'public', 'max-age=0', 's-maxage=300', and 'stale-while-revalidate=600') with a
single exact match assertion that verifies the complete Cache-Control header
value equals the full directive string. This ensures no extraneous or
conflicting directives can slip through undetected, matching the same validation
pattern used in unit tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 56c9a4f0-64de-45a8-b599-0d2dd8ce1028

📥 Commits

Reviewing files that changed from the base of the PR and between 2086b34 and be64273.

📒 Files selected for processing (2)
  • e2e/problems_cache.spec.ts
  • src/routes/problems/page_server.test.ts

@KATO-Hiro KATO-Hiro left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

LGTM

@KATO-Hiro KATO-Hiro merged commit e4abfbd into staging Jun 15, 2026
3 checks passed
@KATO-Hiro KATO-Hiro deleted the #3666 branch June 15, 2026 13:50
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.

[perf] 一覧表で匿名アクセスのレスポンスのみ CDN キャッシュを有効にしましょう

1 participant