Skip to content

Commit c2d6565

Browse files
vn7n24fzkqclaude
andauthored
perf(cache): double the default fresh window to 24h (#316)
Peak hours run close to the GitHub hourly quota ceiling (#308 follow-up); halving steady-state refresh traffic is the cheapest lever. Behind the 48h CDN window, data refreshed daily is indistinguishable from 12h. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent ecd8361 commit c2d6565

2 files changed

Lines changed: 9 additions & 6 deletions

File tree

src/utils/data-cache.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@
1818

1919
import {AsyncLocalStorage} from 'async_hooks';
2020

21-
// 12h: with the CDN already serving most viewers up-to-48h-old cards, a
21+
// 24h: with the CDN already serving most viewers up-to-48h-old cards, a
2222
// shorter Redis fresh window buys little visible freshness while doubling the
23-
// GitHub refresh traffic. Halving that traffic is a free-tier budget lever.
24-
const FRESH_SECONDS_DEFAULT = 12 * 60 * 60; // serve without re-fetching
23+
// GitHub refresh traffic. Halving that traffic is a free-tier budget lever —
24+
// raised from 12h on 2026-07-28 while peak hours ran close to the GitHub
25+
// hourly quota ceiling (#308 follow-up); card data changing at most daily is
26+
// indistinguishable to viewers behind the 48h CDN window anyway.
27+
const FRESH_SECONDS_DEFAULT = 24 * 60 * 60; // serve without re-fetching
2528
const RETENTION_SECONDS_DEFAULT = 7 * 24 * 60 * 60; // Redis EX — stale kept as a rate-limit fallback
2629
const KV_TIMEOUT_MS = 1500; // never let a slow Redis block a card render
2730

tests/utils/data-cache.test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ describe('withDataCache', () => {
6363
});
6464

6565
it('re-fetches when the cached value is stale', async () => {
66-
const staleAt = Date.now() - 13 * 60 * 60 * 1000; // older than the 12h fresh window
66+
const staleAt = Date.now() - 25 * 60 * 60 * 1000; // older than the 24h fresh window
6767
fetchSpy
6868
.mockResolvedValueOnce(envelopeResponse(staleAt, 'stale'))
6969
.mockResolvedValueOnce({ok: true, json: async () => ({})} as Response);
@@ -72,7 +72,7 @@ describe('withDataCache', () => {
7272
});
7373

7474
it('serves the stale value when the fetcher fails (rate limited)', async () => {
75-
const staleAt = Date.now() - 13 * 60 * 60 * 1000;
75+
const staleAt = Date.now() - 25 * 60 * 60 * 1000;
7676
fetchSpy.mockResolvedValueOnce(envelopeResponse(staleAt, 'stale'));
7777
const fetcher = jest.fn().mockRejectedValue(new Error('rate limited'));
7878
await expect(withDataCache('k', fetcher)).resolves.toBe('stale');
@@ -139,7 +139,7 @@ describe('runWithCacheStats', () => {
139139
});
140140

141141
it('reports stale when a fallback copy was served', async () => {
142-
const staleAt = Date.now() - 13 * 60 * 60 * 1000;
142+
const staleAt = Date.now() - 25 * 60 * 60 * 1000;
143143
fetchSpy.mockResolvedValue(envelopeResponse(staleAt, 'stale'));
144144
const {cacheStatus} = await runWithCacheStats(async () => {
145145
await withDataCache('a', jest.fn().mockRejectedValue(new Error('rate limited')));

0 commit comments

Comments
 (0)