feat: lru cache

This commit is contained in:
u80864958
2026-01-23 14:32:51 +01:00
parent df9e6e95cb
commit ab3c8982e2
4 changed files with 61 additions and 7 deletions
+45 -5
View File
@@ -1,5 +1,44 @@
const cache = new Map<string, { timestamp: number; data: any }>();
import { LRUCache } from "lru-cache";
// const cache = new Map<string, { timestamp: number; data: any }>();
const DEFAULT_TTL = 1000 * 60 * 15; // 15 minutes default TTL
const MAX_CACHE_SIZE = 1000 * 1000 * 750; // 750 MB
const cache = new LRUCache({
max: 100,
// Maximum allowed size (in bytes)
maxSize: MAX_CACHE_SIZE,
// IMPORTANT: We must teach the cache how to calculate the size of an item.
// We check for your specific image structure, generic buffers, or fallback to JSON length.
sizeCalculation: (value) => {
// 1. Check if it's our specific image object { buffer: ArrayBuffer, ... }
if (value && typeof value === "object") {
const obj = value as Record<string, any>;
if (obj.buffer instanceof ArrayBuffer) {
return obj.buffer.byteLength;
}
}
// 2. Handle raw ArrayBuffers or Node.js Buffers
if (value instanceof ArrayBuffer) {
return value.byteLength;
}
if (Buffer.isBuffer(value)) {
return value.length;
}
// 3. Fallback for strings or JSON metadata
try {
const str = JSON.stringify(value);
return str ? str.length : 1;
} catch {
return 1;
}
},
ttl: DEFAULT_TTL,
});
/**
* Caches the result of a promise-returning function.
@@ -12,16 +51,17 @@ export async function withCache<T>(
fetcher: () => Promise<T>,
ttl = DEFAULT_TTL,
): Promise<T> {
const now = Date.now();
const entry = cache.get(key);
if (entry && now - entry.timestamp < ttl) {
return entry.data as T;
if (entry) {
return entry as T;
}
try {
const data = await fetcher();
cache.set(key, { timestamp: now, data });
if (data) {
cache.set(key, data, { ttl: ttl });
}
return data;
} catch (error) {
console.error(`Error fetching data for key ${key}:`, error);
+6 -2
View File
@@ -6,6 +6,9 @@ import mime from "mime-types";
import { env } from "$env/dynamic/private";
import { withCache } from "$lib/server/cache";
const DEFAULT_TTL = 1000 * 60 * 15; // 15min
const EXTENDED_TTL = 1000 * 60 * 60 * 24 * 7; // 1 week
// Define what we are storing in the cache
type CachedAsset = {
buffer: ArrayBuffer;
@@ -24,6 +27,8 @@ export const GET: RequestHandler = async ({ params, url }) => {
// 1. Create a unique cache key for this specific asset variation
const cacheKey = `asset:${path}?w=${width ?? ""}&h=${height ?? ""}`;
const ttl = path.toString().includes("cover") ? DEFAULT_TTL : EXTENDED_TTL;
try {
// 2. Use withCache to wrap the fetching logic
const cachedData = await withCache<CachedAsset>(
@@ -113,8 +118,7 @@ export const GET: RequestHandler = async ({ params, url }) => {
throw lastError || new Error("Asset not found");
// --- END OF FETCH LOGIC ---
},
// Optional: Set a specific TTL for images (e.g., 1 hour instead of default 15m)
1000 * 60 * 60,
ttl,
);
// 3. Construct a fresh Response using the cached data