Workaround for broken cache expiry handling (#47)

Deno's cache API does not respect expiry dates.
This meant that feeds were cached much longer than intended.

This PR implments a manual cache handling instead of relying on
Deno's implementation. Relevant issue: https://github.com/denoland/deno/issues/25795
This commit is contained in:
Olav Sundfør 2025-10-01 09:51:29 +02:00 committed by GitHub
parent a17798fe64
commit b2c8662861
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 22 additions and 2 deletions

View File

@ -27,6 +27,7 @@ export function responseXML(body: string, status: Status) {
export const withExpiry = <T>(response: Response, ttlInSeconds: number) => {
const clonedResponse = response.clone();
clonedResponse.headers.set("Cache-Control", `max-age=${ttlInSeconds}`);
clonedResponse.headers.set("Expires", new Date(Date.now() + ttlInSeconds * 1000).toUTCString());
return clonedResponse;
};

View File

@ -5,6 +5,23 @@ import { responseJSON, responseXML, withExpiry } from "../../../lib/utils.ts";
const FEED_CACHE_KEY = "feeds-cache";
// Deno's cache API does not respect these headers.
// Until it does, manual expiry handling is needed.
// When it's working, this function would not be needed.
// See: https://github.com/denoland/deno/issues/25795
function isExpired(headers: Headers) {
const expiresHeader = headers.get("Expires");
if (!expiresHeader) {
console.log("No Expires header found, treating as expired");
return true;
}
const now = new Date();
const expiryDate = new Date(expiresHeader);
const expired = now > expiryDate;
return expired;
}
export const handler = async (
request: Request,
ctx: FreshContext,
@ -13,11 +30,13 @@ export const handler = async (
const cache = await caches.open(FEED_CACHE_KEY);
const cachedResponse = await cache.match(request);
if (cachedResponse) {
if (cachedResponse && !isExpired(cachedResponse.headers)) {
console.log(`Cache hit for feed ${seriesId}`);
return cachedResponse;
}
console.log(`Fetching feed for ${seriesId}`);
const series = await caching.getSeries({ id: seriesId });
if (!series) {
return responseJSON({ message: "Series not found" }, STATUS_CODE.NotFound);
@ -28,7 +47,7 @@ export const handler = async (
const response = withExpiry(
responseXML(feed, STATUS_CODE.OK),
// 2 hours
60 * 60 * 2,
2 * 60 * 60,
);
await cache.put(request, response.clone());