fix(rates): reduce cache TTL, expose fallback header and show stale data warning

This commit is contained in:
2026-03-09 18:17:01 +01:00
parent 1f86a5ead4
commit 10493826bf
4 changed files with 31 additions and 6 deletions

View File

@@ -5,7 +5,7 @@ import {
RATES_CACHE_CONTROL_VALUE,
} from "@/lib/server/rates-cache";
export const revalidate = 300;
export const revalidate = 60;
export async function GET() {
try {
@@ -19,13 +19,18 @@ export async function GET() {
});
} catch (error) {
const cachedRates = getLastCachedRates();
const fallbackErrorMessage =
error instanceof Error
? error.message.replace(/[\r\n]+/g, " ")
: "Upstream provider error";
if (cachedRates) {
return NextResponse.json(cachedRates, {
status: 200,
headers: {
"Cache-Control": RATES_CACHE_CONTROL_VALUE,
"X-Cache-Fallback": "stale-on-error"
"X-Cache-Fallback": "stale-on-error",
"X-Cache-Fallback-Error": fallbackErrorMessage
}
});
}

View File

@@ -243,6 +243,24 @@ export function ConverterCard({
}
};
const displayUpdatedAt = useMemo(() => {
if (!data) {
return null;
}
const timestamps = [new Date(data.updatedAt).getTime()];
if (marketData?.updatedAt) {
timestamps.push(new Date(marketData.updatedAt).getTime());
}
const latest = Math.max(
...timestamps.filter((timestamp) => Number.isFinite(timestamp)),
);
return Number.isFinite(latest) ? new Date(latest).toISOString() : data.updatedAt;
}, [data, marketData?.updatedAt]);
if (isLoading && !data) {
return <ConverterSkeleton />;
}
@@ -497,7 +515,7 @@ export function ConverterCard({
<div>
<p className="text-xs uppercase tracking-[0.12em]">Last updated</p>
<p className="mt-1 text-sm text-foreground">
{formatTimestamp(data.updatedAt)}
{formatTimestamp(displayUpdatedAt ?? data.updatedAt)}
</p>
</div>
</div>

View File

@@ -28,9 +28,11 @@ export function useMarketRates() {
const payload = await response.json();
const parsed = parseRatesResponse(payload);
const isFallback = response.headers.get("X-Cache-Fallback") === "stale-on-error";
const fallbackError = response.headers.get("X-Cache-Fallback-Error");
setData(parsed);
setError(null);
setError(isFallback ? (fallbackError ?? "Upstream provider error") : null);
} catch (err) {
const message = err instanceof Error ? err.message : "Unknown error";
setError(message);

View File

@@ -1,9 +1,9 @@
import { fetchUnifiedRates } from "@/lib/api/normalize";
import type { RatesResponse } from "@/lib/rates";
export const RATES_CACHE_TTL_MS = 300_000;
export const RATES_CACHE_TTL_MS = 60_000;
export const RATES_CACHE_CONTROL_VALUE =
"s-maxage=300, stale-while-revalidate=1800";
"s-maxage=60, stale-while-revalidate=600";
let cachedRates: RatesResponse | null = null;
let cacheTimestamp = 0;