diff --git a/.github/workflows/deploy-homepage-metrics.yml b/.github/workflows/deploy-homepage-metrics.yml index 003fb7c..44addbd 100644 --- a/.github/workflows/deploy-homepage-metrics.yml +++ b/.github/workflows/deploy-homepage-metrics.yml @@ -106,3 +106,9 @@ jobs: env: CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + + - name: Warm homepage metrics cache + run: >- + curl --fail --silent --show-error --retry 5 --retry-all-errors + --retry-delay 2 --max-time 45 --output /dev/null + https://rustfs.com/api/homepage-metrics diff --git a/workers/homepage-metrics/src/index.test.ts b/workers/homepage-metrics/src/index.test.ts index 0038efb..c2fd528 100644 --- a/workers/homepage-metrics/src/index.test.ts +++ b/workers/homepage-metrics/src/index.test.ts @@ -1,7 +1,7 @@ import assert from "node:assert/strict"; import test from "node:test"; import type { HomepageMetrics } from "../../../lib/homepage-metrics.ts"; -import { mergeHomepageMetrics } from "./index.ts"; +import { loadOrRefreshHomepageMetrics, mergeHomepageMetrics } from "./index.ts"; const current: HomepageMetrics = { schemaVersion: 1, @@ -67,3 +67,43 @@ test("updates Docker without overwriting failed GitHub values", () => { assert.equal(result.githubRefreshed, false); assert.equal(result.dockerRefreshed, true); }); + +test("returns valid cached metrics without refreshing", async () => { + let refreshCalls = 0; + const result = await loadOrRefreshHomepageMetrics( + async () => current, + async () => { + refreshCalls += 1; + return { + metrics: { ...current, schemaVersion: 1 }, + githubRefreshed: true, + dockerRefreshed: true, + }; + }, + ); + + assert.deepEqual(result, current); + assert.equal(refreshCalls, 0); +}); + +test("refreshes metrics when the cache is empty", async () => { + let refreshCalls = 0; + const refreshed = { + ...current, + github: { ...current.github, stars: 120, updatedAt: refreshedAt }, + } satisfies HomepageMetrics; + const result = await loadOrRefreshHomepageMetrics( + async () => null, + async () => { + refreshCalls += 1; + return { + metrics: refreshed, + githubRefreshed: true, + dockerRefreshed: false, + }; + }, + ); + + assert.deepEqual(result, refreshed); + assert.equal(refreshCalls, 1); +}); diff --git a/workers/homepage-metrics/src/index.ts b/workers/homepage-metrics/src/index.ts index 8d30c26..dc5120e 100644 --- a/workers/homepage-metrics/src/index.ts +++ b/workers/homepage-metrics/src/index.ts @@ -156,11 +156,23 @@ export function mergeHomepageMetrics( } async function readCachedMetrics(env: Env): Promise { - const cached = await env.HOMEPAGE_METRICS.get(CACHE_KEY, { + const cached = await readStoredMetrics(env); + return isHomepageMetrics(cached) ? cached : fallbackMetrics; +} + +async function readStoredMetrics(env: Env): Promise { + return env.HOMEPAGE_METRICS.get(CACHE_KEY, { type: "json", cacheTtl: 300, }); - return isHomepageMetrics(cached) ? cached : fallbackMetrics; +} + +export async function loadOrRefreshHomepageMetrics( + read: () => Promise, + refresh: () => Promise, +): Promise { + const cached = await read(); + return isHomepageMetrics(cached) ? cached : (await refresh()).metrics; } export async function refreshHomepageMetrics(env: Env): Promise { @@ -212,7 +224,11 @@ export default { } try { - return metricsResponse(await readCachedMetrics(env), request.method); + const metrics = await loadOrRefreshHomepageMetrics( + () => readStoredMetrics(env), + () => refreshHomepageMetrics(env), + ); + return metricsResponse(metrics, request.method); } catch (error) { console.error(JSON.stringify({ event: "homepage_metrics_read_failed",